WebFX 98 - Adding GetValidationAttributes support

This commit is contained in:
sornaks 2014-04-21 17:48:27 -07:00
parent bbafa0a29a
commit 3b7983181d
6 changed files with 217 additions and 7 deletions

View File

@ -197,6 +197,7 @@
<Compile Include="Rendering\SelectList.cs" />
<Compile Include="Rendering\SelectListGroup.cs" />
<Compile Include="Rendering\SelectListItem.cs" />
<Compile Include="Rendering\UnobtrusiveValidationAttributesGenerator.cs" />
<Compile Include="Rendering\ViewEngineResult.cs" />
<Compile Include="RouteConstraintAttribute.cs" />
<Compile Include="RouteDataActionConstraint.cs" />

View File

@ -939,6 +939,86 @@ namespace Microsoft.AspNet.Mvc.Core
return string.Format(CultureInfo.CurrentCulture, GetString("FilterFactoryAttribute_TypeMustImplementIFilter"), p0, p1);
}
/// <summary>
/// Validation parameter names in unobtrusive client validation rules cannot be empty. Client rule type: {0}
/// </summary>
internal static string UnobtrusiveJavascript_ValidationParameterCannotBeEmpty
{
get { return GetString("UnobtrusiveJavascript_ValidationParameterCannotBeEmpty"); }
}
/// <summary>
/// Validation parameter names in unobtrusive client validation rules cannot be empty. Client rule type: {0}
/// </summary>
internal static string FormatUnobtrusiveJavascript_ValidationParameterCannotBeEmpty(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("UnobtrusiveJavascript_ValidationParameterCannotBeEmpty"), p0);
}
/// <summary>
/// Validation parameter names in unobtrusive client validation rules must start with a lowercase letter and consist of only lowercase letters or digits. Validation parameter name: {0}, client rule type: {1}
/// </summary>
internal static string UnobtrusiveJavascript_ValidationParameterMustBeLegal
{
get { return GetString("UnobtrusiveJavascript_ValidationParameterMustBeLegal"); }
}
/// <summary>
/// Validation parameter names in unobtrusive client validation rules must start with a lowercase letter and consist of only lowercase letters or digits. Validation parameter name: {0}, client rule type: {1}
/// </summary>
internal static string FormatUnobtrusiveJavascript_ValidationParameterMustBeLegal(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("UnobtrusiveJavascript_ValidationParameterMustBeLegal"), p0, p1);
}
/// <summary>
/// Validation type names in unobtrusive client validation rules cannot be empty. Client rule type: {0}
/// </summary>
internal static string UnobtrusiveJavascript_ValidationTypeCannotBeEmpty
{
get { return GetString("UnobtrusiveJavascript_ValidationTypeCannotBeEmpty"); }
}
/// <summary>
/// Validation type names in unobtrusive client validation rules cannot be empty. Client rule type: {0}
/// </summary>
internal static string FormatUnobtrusiveJavascript_ValidationTypeCannotBeEmpty(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("UnobtrusiveJavascript_ValidationTypeCannotBeEmpty"), p0);
}
/// <summary>
/// Validation type names in unobtrusive client validation rules must consist of only lowercase letters. Invalid name: "{0}", client rule type: {1}
/// </summary>
internal static string UnobtrusiveJavascript_ValidationTypeMustBeLegal
{
get { return GetString("UnobtrusiveJavascript_ValidationTypeMustBeLegal"); }
}
/// <summary>
/// Validation type names in unobtrusive client validation rules must consist of only lowercase letters. Invalid name: "{0}", client rule type: {1}
/// </summary>
internal static string FormatUnobtrusiveJavascript_ValidationTypeMustBeLegal(object p0, object p1)
{
return string.Format(CultureInfo.CurrentCulture, GetString("UnobtrusiveJavascript_ValidationTypeMustBeLegal"), p0, p1);
}
/// <summary>
/// Validation type names in unobtrusive client validation rules must be unique. The following validation type was seen more than once: {0}
/// </summary>
internal static string UnobtrusiveJavascript_ValidationTypeMustBeUnique
{
get { return GetString("UnobtrusiveJavascript_ValidationTypeMustBeUnique"); }
}
/// <summary>
/// Validation type names in unobtrusive client validation rules must be unique. The following validation type was seen more than once: {0}
/// </summary>
internal static string FormatUnobtrusiveJavascript_ValidationTypeMustBeUnique(object p0)
{
return string.Format(CultureInfo.CurrentCulture, GetString("UnobtrusiveJavascript_ValidationTypeMustBeUnique"), p0);
}
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);

View File

@ -48,6 +48,7 @@ namespace Microsoft.AspNet.Mvc.Rendering
private readonly IUrlHelper _urlHelper;
private readonly IViewEngine _viewEngine;
private readonly AntiForgery _antiForgeryInstance;
private readonly IActionBindingContextProvider _actionBindingContextProvider;
private ViewContext _viewContext;
@ -57,13 +58,15 @@ namespace Microsoft.AspNet.Mvc.Rendering
public HtmlHelper(
[NotNull] IViewEngine viewEngine,
[NotNull] IModelMetadataProvider metadataProvider,
[NotNull] IUrlHelper urlHelper,
[NotNull] AntiForgery antiForgeryInstance)
[NotNull] IUrlHelper urlHelper,
[NotNull] AntiForgery antiForgeryInstance,
[NotNull] IActionBindingContextProvider actionBindingContextProvider)
{
_viewEngine = viewEngine;
MetadataProvider = metadataProvider;
_urlHelper = urlHelper;
_antiForgeryInstance = antiForgeryInstance;
_actionBindingContextProvider = actionBindingContextProvider;
// Underscores are fine characters in id's.
IdAttributeDotReplacement = "_";
@ -640,8 +643,21 @@ namespace Microsoft.AspNet.Mvc.Rendering
// then we can't render the attributes (we'd have no <form> to attach them to).
protected IDictionary<string, object> GetValidationAttributes(string name, ModelMetadata metadata)
{
// TODO: Add validation attributes to input helpers.
return new Dictionary<string, object>();
var formContext = ViewContext.GetFormContextForClientValidation();
if (!ViewContext.UnobtrusiveJavaScriptEnabled || formContext == null)
{
return null;
}
var fullName = ViewData.TemplateInfo.GetFullHtmlFieldName(name);
if (formContext.RenderedField(fullName))
{
return null;
}
formContext.RenderedField(fullName, true);
var clientRules = GetClientValidationRules(name, metadata);
return UnobtrusiveValidationAttributesGenerator.GetValidationAttributes(clientRules);
}
protected virtual HtmlString GenerateCheckBox(ModelMetadata metadata, string name, bool? isChecked,
@ -1350,6 +1366,18 @@ namespace Microsoft.AspNet.Mvc.Rendering
return new HtmlString(Encode(resolvedValue));
}
protected virtual IEnumerable<ModelClientValidationRule> GetClientValidationRules(string name, ModelMetadata metadata)
{
var actionBindingContext = _actionBindingContextProvider.GetActionBindingContextAsync(ViewContext).Result;
metadata = metadata ??
ExpressionMetadataProvider.FromStringExpression(name, ViewData, MetadataProvider);
return actionBindingContext.ValidatorProviders
.SelectMany(vp => vp.GetValidators(metadata))
.OfType<IClientModelValidator>()
.SelectMany(v => v.GetClientValidationRules(
new ClientModelValidationContext(metadata, MetadataProvider)));
}
private static string GetInputTypeString(InputType inputType)
{
switch (inputType)

View File

@ -33,8 +33,9 @@ namespace Microsoft.AspNet.Mvc.Rendering
[NotNull] IViewEngine viewEngine,
[NotNull] IModelMetadataProvider metadataProvider,
[NotNull] IUrlHelper urlHelper,
[NotNull] AntiForgery antiForgeryInstance)
: base(viewEngine, metadataProvider, urlHelper, antiForgeryInstance)
[NotNull] AntiForgery antiForgeryInstance,
[NotNull] IActionBindingContextProvider actionBindingContextProvider)
: base(viewEngine, metadataProvider, urlHelper, antiForgeryInstance, actionBindingContextProvider)
{
}

View File

@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNet.Mvc.ModelBinding;
using Microsoft.AspNet.Mvc.Core;
namespace Microsoft.AspNet.Mvc.Rendering
{
public static class UnobtrusiveValidationAttributesGenerator
{
public static IDictionary<string, object> GetValidationAttributes(
[NotNull] IEnumerable<ModelClientValidationRule> clientRules)
{
IDictionary<string, object> results = null;
foreach (var rule in clientRules)
{
if (results == null)
{
results = new Dictionary<string, object>(StringComparer.Ordinal);
}
var ruleName = "data-val-" + rule.ValidationType;
ValidateUnobtrusiveValidationRule(rule, results, ruleName);
results.Add(ruleName, rule.ErrorMessage ?? string.Empty);
ruleName += "-";
foreach (var kvp in rule.ValidationParameters)
{
results.Add(ruleName + kvp.Key, kvp.Value ?? string.Empty);
}
}
if (results != null)
{
results.Add("data-val", "true");
}
return results;
}
private static void ValidateUnobtrusiveValidationRule(ModelClientValidationRule rule,
IDictionary<string, object> resultsDictionary, string dictionaryKey)
{
if (string.IsNullOrEmpty(rule.ValidationType))
{
throw new ArgumentException(
Resources.FormatUnobtrusiveJavascript_ValidationTypeCannotBeEmpty(rule.GetType().FullName), "rule");
}
if (resultsDictionary.ContainsKey(dictionaryKey))
{
throw new InvalidOperationException(
Resources.FormatUnobtrusiveJavascript_ValidationTypeMustBeUnique(rule.ValidationType));
}
if (!rule.ValidationType.All(char.IsLower))
{
throw new InvalidOperationException(
Resources.FormatUnobtrusiveJavascript_ValidationTypeMustBeLegal(
rule.ValidationType,
rule.GetType().FullName));
}
foreach (var key in rule.ValidationParameters.Keys)
{
if (string.IsNullOrEmpty(key))
{
throw new InvalidOperationException(
Resources.FormatUnobtrusiveJavascript_ValidationParameterCannotBeEmpty(rule.GetType().FullName));
}
if (!char.IsLower(key[0]) || key.Any(c => !char.IsLower(c) && !char.IsDigit(c)))
{
throw new InvalidOperationException(
Resources.FormatUnobtrusiveJavascript_ValidationParameterMustBeLegal(
key,
rule.GetType().FullName));
}
}
}
}
}

View File

@ -288,4 +288,19 @@
<data name="FilterFactoryAttribute_TypeMustImplementIFilter" xml:space="preserve">
<value>The type provided to '{0}' must implement '{1}'.</value>
</data>
</root>
<data name="UnobtrusiveJavascript_ValidationParameterCannotBeEmpty" xml:space="preserve">
<value>Validation parameter names in unobtrusive client validation rules cannot be empty. Client rule type: {0}</value>
</data>
<data name="UnobtrusiveJavascript_ValidationParameterMustBeLegal" xml:space="preserve">
<value>Validation parameter names in unobtrusive client validation rules must start with a lowercase letter and consist of only lowercase letters or digits. Validation parameter name: {0}, client rule type: {1}</value>
</data>
<data name="UnobtrusiveJavascript_ValidationTypeCannotBeEmpty" xml:space="preserve">
<value>Validation type names in unobtrusive client validation rules cannot be empty. Client rule type: {0}</value>
</data>
<data name="UnobtrusiveJavascript_ValidationTypeMustBeLegal" xml:space="preserve">
<value>Validation type names in unobtrusive client validation rules must consist of only lowercase letters. Invalid name: "{0}", client rule type: {1}</value>
</data>
<data name="UnobtrusiveJavascript_ValidationTypeMustBeUnique" xml:space="preserve">
<value>Validation type names in unobtrusive client validation rules must be unique. The following validation type was seen more than once: {0}</value>
</data>
</root>