Undesign client validation
This change removes a layer of abstraction. These validators now just do what they do now without creating a bunch of intermediate objects. ModelClientValidationRule has been removed, and client validations manipulate the attributes collection of a tag directly.
This commit is contained in:
parent
8c4bcf14c7
commit
1a70f12bf8
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -14,12 +16,20 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
|||
/// <param name="actionContext">The <see cref="ActionContext"/> for validation.</param>
|
||||
/// <param name="metadata">The <see cref="ModelMetadata"/> for validation.</param>
|
||||
/// <param name="metadataProvider">The <see cref="IModelMetadataProvider"/> to be used in validation.</param>
|
||||
/// <param name="attributes">The attributes dictionary for the HTML tag being rendered.</param>
|
||||
public ClientModelValidationContext(
|
||||
ActionContext actionContext,
|
||||
ModelMetadata metadata,
|
||||
IModelMetadataProvider metadataProvider)
|
||||
IModelMetadataProvider metadataProvider,
|
||||
IDictionary<string, string> attributes)
|
||||
: base(actionContext, metadata, metadataProvider)
|
||||
{
|
||||
Attributes = attributes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attributes dictionary for the HTML tag being rendered.
|
||||
/// </summary>
|
||||
public IDictionary<string, string> Attributes { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public interface IClientModelValidator
|
||||
{
|
||||
IEnumerable<ModelClientValidationRule> GetClientValidationRules(ClientModelValidationContext context);
|
||||
void AddValidation(ClientModelValidationContext context);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,55 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
||||
{
|
||||
public class ModelClientValidationRule
|
||||
{
|
||||
private readonly Dictionary<string, object> _validationParameters =
|
||||
new Dictionary<string, object>(StringComparer.Ordinal);
|
||||
|
||||
public ModelClientValidationRule(string errorMessage)
|
||||
: this(validationType: string.Empty, errorMessage: errorMessage)
|
||||
{
|
||||
if (errorMessage == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(errorMessage));
|
||||
}
|
||||
}
|
||||
|
||||
public ModelClientValidationRule(
|
||||
string validationType,
|
||||
string errorMessage)
|
||||
{
|
||||
if (validationType == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(validationType));
|
||||
}
|
||||
|
||||
if (errorMessage == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(errorMessage));
|
||||
}
|
||||
|
||||
ValidationType = validationType;
|
||||
ErrorMessage = errorMessage;
|
||||
}
|
||||
|
||||
public string ErrorMessage { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Identifier of the <see cref="ModelClientValidationRule"/>. If client-side validation is enabled, default
|
||||
/// validation attribute generator uses this <see cref="string"/> as part of the generated "data-val"
|
||||
/// attribute name. Must be unique in the set of enabled validation rules.
|
||||
/// </summary>
|
||||
public string ValidationType { get; private set; }
|
||||
|
||||
public IDictionary<string, object> ValidationParameters
|
||||
{
|
||||
get { return _validationParameters; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
|
@ -13,26 +12,24 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
{
|
||||
public class CompareAttributeAdapter : AttributeAdapterBase<CompareAttribute>
|
||||
{
|
||||
private readonly string _otherProperty;
|
||||
|
||||
public CompareAttributeAdapter(CompareAttribute attribute, IStringLocalizer stringLocalizer)
|
||||
: base(new CompareAttributeWrapper(attribute), stringLocalizer)
|
||||
{
|
||||
if (attribute == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(attribute));
|
||||
}
|
||||
_otherProperty = "*." + attribute.OtherProperty;
|
||||
}
|
||||
|
||||
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||
ClientModelValidationContext context)
|
||||
public override void AddValidation(ClientModelValidationContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var errorMessage = GetErrorMessage(context);
|
||||
var clientRule = new ModelClientValidationEqualToRule(errorMessage, "*." + Attribute.OtherProperty);
|
||||
return new[] { clientRule };
|
||||
MergeAttribute(context.Attributes, "data-val", "true");
|
||||
MergeAttribute(context.Attributes, "data-val-equalto", GetErrorMessage(context));
|
||||
MergeAttribute(context.Attributes, "data-val-equalto-other", _otherProperty);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
|
@ -26,18 +25,17 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
RuleName = ruleName;
|
||||
}
|
||||
|
||||
public string RuleName { get; private set; }
|
||||
public string RuleName { get; }
|
||||
|
||||
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||
ClientModelValidationContext context)
|
||||
public override void AddValidation(ClientModelValidationContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var errorMessage = GetErrorMessage(context);
|
||||
return new[] { new ModelClientValidationRule(RuleName, errorMessage) };
|
||||
MergeAttribute(context.Attributes, "data-val", "true");
|
||||
MergeAttribute(context.Attributes, RuleName, GetErrorMessage(context));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
|
|
@ -11,21 +11,24 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
{
|
||||
public class MaxLengthAttributeAdapter : AttributeAdapterBase<MaxLengthAttribute>
|
||||
{
|
||||
private readonly string _max;
|
||||
|
||||
public MaxLengthAttributeAdapter(MaxLengthAttribute attribute, IStringLocalizer stringLocalizer)
|
||||
: base(attribute, stringLocalizer)
|
||||
{
|
||||
_max = Attribute.Length.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||
ClientModelValidationContext context)
|
||||
public override void AddValidation(ClientModelValidationContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var message = GetErrorMessage(context);
|
||||
return new[] { new ModelClientValidationMaxLengthRule(message, Attribute.Length) };
|
||||
MergeAttribute(context.Attributes, "data-val", "true");
|
||||
MergeAttribute(context.Attributes, "data-val-maxlength", GetErrorMessage(context));
|
||||
MergeAttribute(context.Attributes, "data-val-maxlength-max", _max);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
|
|
@ -11,21 +11,24 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
{
|
||||
public class MinLengthAttributeAdapter : AttributeAdapterBase<MinLengthAttribute>
|
||||
{
|
||||
private readonly string _min;
|
||||
|
||||
public MinLengthAttributeAdapter(MinLengthAttribute attribute, IStringLocalizer stringLocalizer)
|
||||
: base(attribute, stringLocalizer)
|
||||
{
|
||||
_min = Attribute.Length.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||
ClientModelValidationContext context)
|
||||
public override void AddValidation(ClientModelValidationContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var message = GetErrorMessage(context);
|
||||
return new[] { new ModelClientValidationMinLengthRule(message, Attribute.Length) };
|
||||
MergeAttribute(context.Attributes, "data-val", "true");
|
||||
MergeAttribute(context.Attributes, "data-val-minlength", GetErrorMessage(context));
|
||||
MergeAttribute(context.Attributes, "data-val-minlength-min", _min);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents client side validation rule that determines if two values are equal.
|
||||
/// </summary>
|
||||
public class ModelClientValidationEqualToRule : ModelClientValidationRule
|
||||
{
|
||||
private const string EqualToValidationType = "equalto";
|
||||
private const string EqualToValidationParameter = "other";
|
||||
|
||||
public ModelClientValidationEqualToRule(
|
||||
string errorMessage,
|
||||
object other)
|
||||
: base(EqualToValidationType, errorMessage)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
|
||||
ValidationParameters[EqualToValidationParameter] = other;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||
{
|
||||
public class ModelClientValidationMaxLengthRule : ModelClientValidationRule
|
||||
{
|
||||
private const string MaxLengthValidationType = "maxlength";
|
||||
private const string MaxLengthValidationParameter = "max";
|
||||
|
||||
public ModelClientValidationMaxLengthRule(string errorMessage, int maximumLength)
|
||||
: base(MaxLengthValidationType, errorMessage)
|
||||
{
|
||||
ValidationParameters[MaxLengthValidationParameter] = maximumLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||
{
|
||||
public class ModelClientValidationMinLengthRule : ModelClientValidationRule
|
||||
{
|
||||
private const string MinLengthValidationType = "minlength";
|
||||
private const string MinLengthValidationParameter = "min";
|
||||
|
||||
public ModelClientValidationMinLengthRule(string errorMessage, int minimumLength)
|
||||
: base(MinLengthValidationType, errorMessage)
|
||||
{
|
||||
ValidationParameters[MinLengthValidationParameter] = minimumLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// This is a <see cref="ModelClientValidationRule"/> for numeric values.
|
||||
/// </summary>
|
||||
public class ModelClientValidationNumericRule : ModelClientValidationRule
|
||||
{
|
||||
private const string NumericValidationType = "number";
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of <see cref="ModelClientValidationNumericRule"/>
|
||||
/// with the given <paramref name="errorMessage"/>.
|
||||
/// </summary>
|
||||
/// <param name="errorMessage">The error message to be displayed.</param>
|
||||
public ModelClientValidationNumericRule(string errorMessage)
|
||||
: base(NumericValidationType, errorMessage)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||
{
|
||||
public class ModelClientValidationRangeRule : ModelClientValidationRule
|
||||
{
|
||||
private const string RangeValidationType = "range";
|
||||
private const string MinValidationParameter = "min";
|
||||
private const string MaxValidationParameter = "max";
|
||||
|
||||
public ModelClientValidationRangeRule(
|
||||
string errorMessage,
|
||||
object minValue,
|
||||
object maxValue)
|
||||
: base(RangeValidationType, errorMessage)
|
||||
{
|
||||
if (minValue == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(minValue));
|
||||
}
|
||||
|
||||
if (maxValue == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(maxValue));
|
||||
}
|
||||
|
||||
ValidationParameters[MinValidationParameter] = minValue;
|
||||
ValidationParameters[MaxValidationParameter] = maxValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||
{
|
||||
public class ModelClientValidationRegexRule : ModelClientValidationRule
|
||||
{
|
||||
private const string RegexValidationType = "regex";
|
||||
private const string RegexValidationRuleName = "pattern";
|
||||
|
||||
public ModelClientValidationRegexRule(string errorMessage, string pattern)
|
||||
: base(RegexValidationType, errorMessage)
|
||||
{
|
||||
ValidationParameters.Add(RegexValidationRuleName, pattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||
{
|
||||
public class ModelClientValidationRequiredRule : ModelClientValidationRule
|
||||
{
|
||||
private const string RequiredValidationType = "required";
|
||||
|
||||
public ModelClientValidationRequiredRule(string errorMessage) :
|
||||
base(RequiredValidationType, errorMessage)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||
{
|
||||
public class ModelClientValidationStringLengthRule : ModelClientValidationRule
|
||||
{
|
||||
private const string LengthValidationType = "length";
|
||||
private const string MinValidationParameter = "min";
|
||||
private const string MaxValidationParameter = "max";
|
||||
|
||||
public ModelClientValidationStringLengthRule(string errorMessage, int minimumLength, int maximumLength)
|
||||
: base(LengthValidationType, errorMessage)
|
||||
{
|
||||
if (minimumLength != 0)
|
||||
{
|
||||
ValidationParameters[MinValidationParameter] = minimumLength;
|
||||
}
|
||||
|
||||
if (maximumLength != int.MaxValue)
|
||||
{
|
||||
ValidationParameters[MaxValidationParameter] = maximumLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,14 +15,26 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
public class NumericClientModelValidator : IClientModelValidator
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ClientModelValidationContext context)
|
||||
public void AddValidation(ClientModelValidationContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
return new[] { new ModelClientValidationNumericRule(GetErrorMessage(context.ModelMetadata)) };
|
||||
MergeAttribute(context.Attributes, "data-val", "true");
|
||||
MergeAttribute(context.Attributes, "data-val-number", GetErrorMessage(context.ModelMetadata));
|
||||
}
|
||||
|
||||
private static bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
|
||||
{
|
||||
if (attributes.ContainsKey(key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
attributes.Add(key, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
private string GetErrorMessage(ModelMetadata modelMetadata)
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
|
|
@ -11,26 +11,34 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
{
|
||||
public class RangeAttributeAdapter : AttributeAdapterBase<RangeAttribute>
|
||||
{
|
||||
private readonly string _max;
|
||||
private readonly string _min;
|
||||
|
||||
public RangeAttributeAdapter(RangeAttribute attribute, IStringLocalizer stringLocalizer)
|
||||
: base(attribute, stringLocalizer)
|
||||
{
|
||||
// This will trigger the conversion of Attribute.Minimum and Attribute.Maximum.
|
||||
// This is needed, because the attribute is stateful and will convert from a string like
|
||||
// "100m" to the decimal value 100.
|
||||
//
|
||||
// Validate a randomly selected number.
|
||||
attribute.IsValid(3);
|
||||
|
||||
_max = Convert.ToString(Attribute.Maximum, CultureInfo.InvariantCulture);
|
||||
_min = Convert.ToString(Attribute.Minimum, CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||
ClientModelValidationContext context)
|
||||
public override void AddValidation(ClientModelValidationContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
// TODO: Only calling this so Minimum and Maximum convert. Caused by a bug in CoreFx.
|
||||
Attribute.IsValid(null);
|
||||
|
||||
var errorMessage = GetErrorMessage(context);
|
||||
|
||||
|
||||
return new[] { new ModelClientValidationRangeRule(errorMessage, Attribute.Minimum, Attribute.Maximum) };
|
||||
MergeAttribute(context.Attributes, "data-val", "true");
|
||||
MergeAttribute(context.Attributes, "data-val-range", GetErrorMessage(context));
|
||||
MergeAttribute(context.Attributes, "data-val-range-max", _max);
|
||||
MergeAttribute(context.Attributes, "data-val-range-min", _min);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
|
@ -16,16 +15,16 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
{
|
||||
}
|
||||
|
||||
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||
ClientModelValidationContext context)
|
||||
public override void AddValidation(ClientModelValidationContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var errorMessage = GetErrorMessage(context);
|
||||
return new[] { new ModelClientValidationRegexRule(errorMessage, Attribute.Pattern) };
|
||||
MergeAttribute(context.Attributes, "data-val", "true");
|
||||
MergeAttribute(context.Attributes, "data-val-regex", GetErrorMessage(context));
|
||||
MergeAttribute(context.Attributes, "data-val-regex-pattern", Attribute.Pattern);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
|
@ -16,16 +15,15 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
{
|
||||
}
|
||||
|
||||
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||
ClientModelValidationContext context)
|
||||
public override void AddValidation(ClientModelValidationContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var errorMessage = GetErrorMessage(context);
|
||||
return new[] { new ModelClientValidationRequiredRule(errorMessage) };
|
||||
MergeAttribute(context.Attributes, "data-val", "true");
|
||||
MergeAttribute(context.Attributes, "data-val-required", GetErrorMessage(context));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Globalization;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
|
|
@ -11,24 +11,36 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
{
|
||||
public class StringLengthAttributeAdapter : AttributeAdapterBase<StringLengthAttribute>
|
||||
{
|
||||
private readonly string _max;
|
||||
private readonly string _min;
|
||||
|
||||
public StringLengthAttributeAdapter(StringLengthAttribute attribute, IStringLocalizer stringLocalizer)
|
||||
: base(attribute, stringLocalizer)
|
||||
{
|
||||
_max = Attribute.MaximumLength.ToString(CultureInfo.InvariantCulture);
|
||||
_min = Attribute.MinimumLength.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||
ClientModelValidationContext context)
|
||||
/// <inheritdoc />
|
||||
public override void AddValidation(ClientModelValidationContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var errorMessage = GetErrorMessage(context);
|
||||
var rule = new ModelClientValidationStringLengthRule(errorMessage,
|
||||
Attribute.MinimumLength,
|
||||
Attribute.MaximumLength);
|
||||
return new[] { rule };
|
||||
MergeAttribute(context.Attributes, "data-val", "true");
|
||||
MergeAttribute(context.Attributes, "data-val-length", GetErrorMessage(context));
|
||||
|
||||
if (Attribute.MaximumLength != int.MaxValue)
|
||||
{
|
||||
MergeAttribute(context.Attributes, "data-val-length-max", _max);
|
||||
}
|
||||
|
||||
if (Attribute.MinimumLength != 0)
|
||||
{
|
||||
MergeAttribute(context.Attributes, "data-val-length-min", _min);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
|
|||
|
|
@ -32,14 +32,30 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
/// <summary>
|
||||
/// Gets the <typeparamref name="TAttribute"/> instance.
|
||||
/// </summary>
|
||||
public TAttribute Attribute
|
||||
{
|
||||
get;
|
||||
}
|
||||
public TAttribute Attribute { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||
ClientModelValidationContext context);
|
||||
public abstract void AddValidation(ClientModelValidationContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Adds the given <paramref name="key"/> and <paramref name="value"/> into
|
||||
/// <paramref name="attributes"/> if <paramref name="attributes"/> does not contain a value for
|
||||
/// <paramref name="key"/>.
|
||||
/// </summary>
|
||||
/// <param name="attributes">The HTML attributes dictionary.</param>
|
||||
/// <param name="key">The attribute key.</param>
|
||||
/// <param name="value">The attribute value.</param>
|
||||
/// <returns><c>true</c> if an attribute was added, otherwise <c>false</c>.</returns>
|
||||
protected static bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
|
||||
{
|
||||
if (attributes.ContainsKey(key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
attributes.Add(key, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the error message formatted using the <see cref="Attribute"/>.
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
}
|
||||
else if (type == typeof(CreditCardAttribute))
|
||||
{
|
||||
adapter = new DataTypeAttributeAdapter((DataTypeAttribute)attribute, "creditcard", stringLocalizer);
|
||||
adapter = new DataTypeAttributeAdapter((DataTypeAttribute)attribute, "data-val-creditcard", stringLocalizer);
|
||||
}
|
||||
else if (type == typeof(StringLengthAttribute))
|
||||
{
|
||||
|
|
@ -63,15 +63,15 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
}
|
||||
else if (type == typeof(EmailAddressAttribute))
|
||||
{
|
||||
adapter = new DataTypeAttributeAdapter((DataTypeAttribute)attribute, "email", stringLocalizer);
|
||||
adapter = new DataTypeAttributeAdapter((DataTypeAttribute)attribute, "data-val-email", stringLocalizer);
|
||||
}
|
||||
else if (type == typeof(PhoneAttribute))
|
||||
{
|
||||
adapter = new DataTypeAttributeAdapter((DataTypeAttribute)attribute, "phone", stringLocalizer);
|
||||
adapter = new DataTypeAttributeAdapter((DataTypeAttribute)attribute, "data-val-phone", stringLocalizer);
|
||||
}
|
||||
else if (type == typeof(UrlAttribute))
|
||||
{
|
||||
adapter = new DataTypeAttributeAdapter((DataTypeAttribute)attribute, "url", stringLocalizer);
|
||||
adapter = new DataTypeAttributeAdapter((DataTypeAttribute)attribute, "data-val-url", stringLocalizer);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,46 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="ModelClientValidationRule"/> containing information for HTML attribute generation in fields a
|
||||
/// <see cref="RemoteAttribute"/> targets.
|
||||
/// </summary>
|
||||
public class ModelClientValidationRemoteRule : ModelClientValidationRule
|
||||
{
|
||||
private const string RemoteValidationType = "remote";
|
||||
private const string AdditionalFieldsValidationParameter = "additionalfields";
|
||||
private const string TypeValidationParameter = "type";
|
||||
private const string UrlValidationParameter = "url";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ModelClientValidationRemoteRule"/> class.
|
||||
/// </summary>
|
||||
/// <param name="errorMessage">Error message client should display when validation fails.</param>
|
||||
/// <param name="url">URL where client should send a validation request.</param>
|
||||
/// <param name="httpMethod">
|
||||
/// HTTP method (<c>"GET"</c> or <c>"POST"</c>) client should use when sending a validation request.
|
||||
/// </param>
|
||||
/// <param name="additionalFields">
|
||||
/// Comma-separated names of fields the client should include in a validation request.
|
||||
/// </param>
|
||||
public ModelClientValidationRemoteRule(
|
||||
string errorMessage,
|
||||
string url,
|
||||
string httpMethod,
|
||||
string additionalFields)
|
||||
: base(validationType: RemoteValidationType, errorMessage: errorMessage)
|
||||
{
|
||||
ValidationParameters[UrlValidationParameter] = url;
|
||||
if (!string.IsNullOrEmpty(httpMethod))
|
||||
{
|
||||
ValidationParameters[TypeValidationParameter] = httpMethod;
|
||||
}
|
||||
|
||||
ValidationParameters[AdditionalFieldsValidationParameter] = additionalFields;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -232,26 +232,37 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown if unable to generate a target URL for a validation request.
|
||||
/// </exception>
|
||||
public virtual IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||
ClientModelValidationContext context)
|
||||
public virtual void AddValidation(ClientModelValidationContext context)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var metadata = context.ModelMetadata;
|
||||
var rule = new ModelClientValidationRemoteRule(
|
||||
FormatErrorMessage(metadata.GetDisplayName()),
|
||||
GetUrl(context),
|
||||
HttpMethod,
|
||||
FormatAdditionalFieldsForClientValidation(metadata.PropertyName));
|
||||
MergeAttribute(context.Attributes, "data-val", "true");
|
||||
|
||||
return new[] { rule };
|
||||
var errorMessage = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
|
||||
MergeAttribute(context.Attributes, "data-val-remote", errorMessage);
|
||||
MergeAttribute(context.Attributes, "data-val-remote-url", GetUrl(context));
|
||||
|
||||
if (!string.IsNullOrEmpty(HttpMethod))
|
||||
{
|
||||
MergeAttribute(context.Attributes, "data-val-remote-type", HttpMethod);
|
||||
}
|
||||
|
||||
var additionalFields = FormatAdditionalFieldsForClientValidation(context.ModelMetadata.PropertyName);
|
||||
MergeAttribute(context.Attributes, "data-val-remote-additionalfields", additionalFields);
|
||||
}
|
||||
|
||||
private static bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
|
||||
{
|
||||
if (attributes.ContainsKey(key))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
attributes.Add(key, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static IEnumerable<string> SplitAndTrimPropertyNames(string original)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ using System.Text.Encodings.Web;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Html;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||
|
|
@ -375,20 +374,6 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
|||
/// <returns>A <see cref="string"/> containing the element Id.</returns>
|
||||
string GenerateIdFromName(string fullName);
|
||||
|
||||
/// <summary>
|
||||
/// Returns information about about client validation rules for the specified <paramref name="metadata"/> or
|
||||
/// <paramref name="expression"/>. Intended for use in <see cref="IHtmlHelper"/> extension methods.
|
||||
/// </summary>
|
||||
/// <param name="metadata">Metadata about the <see cref="object"/> of interest.</param>
|
||||
/// <param name="expression">
|
||||
/// Expression name, relative to the current model. Used to determine <see cref="ModelMetadata"/> when
|
||||
/// <paramref name="metadata"/> is <c>null</c>; ignored otherwise.
|
||||
/// </param>
|
||||
/// <returns>An <see cref="IEnumerable{ModelClientValidationRule}"/> containing the relevant rules.</returns>
|
||||
IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||
ModelExplorer modelExplorer,
|
||||
string expression);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a select list for the given <typeparamref name="TEnum"/>.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -569,7 +569,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
}
|
||||
}
|
||||
|
||||
tagBuilder.MergeAttributes(GetValidationAttributes(viewContext, modelExplorer, expression));
|
||||
AddValidationAttributes(viewContext, tagBuilder, modelExplorer, expression);
|
||||
|
||||
return tagBuilder;
|
||||
}
|
||||
|
|
@ -640,7 +640,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
}
|
||||
|
||||
tagBuilder.MergeAttribute("name", fullName, true);
|
||||
tagBuilder.MergeAttributes(GetValidationAttributes(viewContext, modelExplorer, expression));
|
||||
AddValidationAttributes(viewContext, tagBuilder, modelExplorer, expression);
|
||||
|
||||
// If there are any errors for a named field, we add this CSS attribute.
|
||||
if (entry != null && entry.Errors.Count > 0)
|
||||
|
|
@ -865,31 +865,6 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
return tagBuilder;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||
ViewContext viewContext,
|
||||
ModelExplorer modelExplorer,
|
||||
string expression)
|
||||
{
|
||||
if (viewContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(viewContext));
|
||||
}
|
||||
|
||||
modelExplorer = modelExplorer ??
|
||||
ExpressionMetadataProvider.FromStringExpression(expression, viewContext.ViewData, _metadataProvider);
|
||||
var validationContext = new ClientModelValidationContext(
|
||||
viewContext,
|
||||
modelExplorer.Metadata,
|
||||
_metadataProvider);
|
||||
|
||||
var validatorProviderContext = new ClientValidatorProviderContext(modelExplorer.Metadata);
|
||||
_clientModelValidatorProvider.GetValidators(validatorProviderContext);
|
||||
|
||||
var validators = validatorProviderContext.Validators;
|
||||
return validators.SelectMany(v => v.GetClientValidationRules(validationContext));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual ICollection<string> GetCurrentValues(
|
||||
ViewContext viewContext,
|
||||
|
|
@ -1251,7 +1226,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
|
||||
}
|
||||
|
||||
tagBuilder.MergeAttributes(GetValidationAttributes(viewContext, modelExplorer, expression));
|
||||
AddValidationAttributes(viewContext, tagBuilder, modelExplorer, expression);
|
||||
|
||||
return tagBuilder;
|
||||
}
|
||||
|
|
@ -1275,30 +1250,58 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
return tagBuilder;
|
||||
}
|
||||
|
||||
// Only render attributes if client-side validation is enabled, and then only if we've
|
||||
// never rendered validation for a field with this name in this form.
|
||||
protected virtual IDictionary<string, object> GetValidationAttributes(
|
||||
/// <summary>
|
||||
/// Adds validation attributes to the <paramref name="tagBuilder" /> if client validation
|
||||
/// is enabled.
|
||||
/// </summary>
|
||||
/// <param name="viewContext">A <see cref="ViewContext"/> instance for the current scope.</param>
|
||||
/// <param name="tagBuilder">A <see cref="TagBuilder"/> instance.</param>
|
||||
/// <param name="modelExplorer">The <see cref="ModelExplorer"/> for the <paramref name="expression"/>.</param>
|
||||
/// <param name="expression">Expression name, relative to the current model.</param>
|
||||
protected virtual void AddValidationAttributes(
|
||||
ViewContext viewContext,
|
||||
TagBuilder tagBuilder,
|
||||
ModelExplorer modelExplorer,
|
||||
string expression)
|
||||
{
|
||||
// Only render attributes if client-side validation is enabled, and then only if we've
|
||||
// never rendered validation for a field with this name in this form.
|
||||
var formContext = viewContext.ClientValidationEnabled ? viewContext.FormContext : null;
|
||||
if (formContext == null)
|
||||
{
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
var fullName = GetFullHtmlFieldName(viewContext, expression);
|
||||
if (formContext.RenderedField(fullName))
|
||||
{
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
formContext.RenderedField(fullName, true);
|
||||
|
||||
var clientRules = GetClientValidationRules(viewContext, modelExplorer, expression);
|
||||
modelExplorer = modelExplorer ??
|
||||
ExpressionMetadataProvider.FromStringExpression(expression, viewContext.ViewData, _metadataProvider);
|
||||
|
||||
return UnobtrusiveValidationAttributesGenerator.GetValidationAttributes(clientRules);
|
||||
|
||||
var validatorProviderContext = new ClientValidatorProviderContext(modelExplorer.Metadata);
|
||||
_clientModelValidatorProvider.GetValidators(validatorProviderContext);
|
||||
|
||||
var validators = validatorProviderContext.Validators;
|
||||
if (validators.Count > 0)
|
||||
{
|
||||
var validationContext = new ClientModelValidationContext(
|
||||
viewContext,
|
||||
modelExplorer.Metadata,
|
||||
_metadataProvider,
|
||||
tagBuilder.Attributes);
|
||||
|
||||
for (var i = 0; i < validators.Count; i++)
|
||||
{
|
||||
var validator = validators[i];
|
||||
validator.AddValidation(validationContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Enum ConvertEnumFromInteger(object value, Type targetType)
|
||||
|
|
|
|||
|
|
@ -1212,14 +1212,6 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
return resolvedValue;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||
ModelExplorer modelExplorer,
|
||||
string expression)
|
||||
{
|
||||
return _htmlGenerator.GetClientValidationRules(ViewContext, modelExplorer, expression);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a select list for the given <paramref name="metadata"/>.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -365,15 +365,6 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
|||
string headerTag,
|
||||
object htmlAttributes);
|
||||
|
||||
/// <remarks>
|
||||
/// Not used directly in <see cref="HtmlHelper"/>. Exposed publicly for use in other <see cref="IHtmlHelper"/>
|
||||
/// implementations.
|
||||
/// </remarks>
|
||||
IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||
ViewContext viewContext,
|
||||
ModelExplorer modelExplorer,
|
||||
string expression);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the collection of current values for the given <paramref name="expression"/>.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,96 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||
{
|
||||
public static class UnobtrusiveValidationAttributesGenerator
|
||||
{
|
||||
public static IDictionary<string, object> GetValidationAttributes(
|
||||
IEnumerable<ModelClientValidationRule> clientRules)
|
||||
{
|
||||
if (clientRules == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(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),
|
||||
nameof(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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -79,7 +79,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
|
|||
|
||||
private class TestClientModelValidationAttribute : Attribute, IClientModelValidator
|
||||
{
|
||||
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ClientModelValidationContext context)
|
||||
public void AddValidation(ClientModelValidationContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
@ -87,7 +87,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
|
|||
|
||||
private class TestValidationAttribute : Attribute, IModelValidator, IClientModelValidator
|
||||
{
|
||||
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ClientModelValidationContext context)
|
||||
public void AddValidation(ClientModelValidationContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
|
@ -25,19 +26,30 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var attribute = new CompareAttribute("OtherProperty");
|
||||
var adapter = new CompareAttributeAdapter(attribute, stringLocalizer: null);
|
||||
|
||||
// Mono issue - https://github.com/aspnet/External/issues/19
|
||||
var expectedMessage = PlatformNormalizer.NormalizeContent(
|
||||
"'MyPropertyDisplayName' and 'OtherPropertyDisplayName' do not match.");
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, metadataProvider);
|
||||
var context = new ClientModelValidationContext(
|
||||
actionContext,
|
||||
metadata,
|
||||
metadataProvider,
|
||||
new AttributeDictionary());
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
// Mono issue - https://github.com/aspnet/External/issues/19
|
||||
Assert.Equal(
|
||||
PlatformNormalizer.NormalizeContent(
|
||||
"'MyPropertyDisplayName' and 'OtherPropertyDisplayName' do not match."),
|
||||
rule.ErrorMessage);
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-equalto", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal("data-val-equalto-other", kvp.Key);
|
||||
Assert.Equal(kvp.Value, "*.OtherProperty");
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -62,15 +74,25 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var adapter = new CompareAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, metadataProvider);
|
||||
var context = new ClientModelValidationContext(
|
||||
actionContext,
|
||||
metadata,
|
||||
metadataProvider,
|
||||
new AttributeDictionary());
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
// Mono issue - https://github.com/aspnet/External/issues/19
|
||||
Assert.Equal(expectedMessage, rule.ErrorMessage);
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-equalto", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal("data-val-equalto-other", kvp.Key);
|
||||
Assert.Equal(kvp.Value, "*.OtherProperty");
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -84,18 +106,29 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var attribute = new CompareAttribute("OtherProperty");
|
||||
var adapter = new CompareAttributeAdapter(attribute, stringLocalizer: null);
|
||||
|
||||
// Mono issue - https://github.com/aspnet/External/issues/19
|
||||
var expectedMessage = PlatformNormalizer.NormalizeContent("'MyProperty' and 'OtherProperty' do not match.");
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, metadataProvider);
|
||||
var context = new ClientModelValidationContext(
|
||||
actionContext,
|
||||
metadata,
|
||||
metadataProvider,
|
||||
new AttributeDictionary());
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
// Mono issue - https://github.com/aspnet/External/issues/19
|
||||
Assert.Equal(
|
||||
PlatformNormalizer.NormalizeContent("'MyProperty' and 'OtherProperty' do not match."),
|
||||
rule.ErrorMessage);
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-equalto", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal("data-val-equalto-other", kvp.Key);
|
||||
Assert.Equal(kvp.Value, "*.OtherProperty");
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -111,15 +144,28 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
};
|
||||
var adapter = new CompareAttributeAdapter(attribute, stringLocalizer: null);
|
||||
|
||||
var expectedMessage = "Hello 'MyProperty', goodbye 'OtherProperty'.";
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, metadataProvider);
|
||||
var context = new ClientModelValidationContext(
|
||||
actionContext,
|
||||
metadata,
|
||||
metadataProvider,
|
||||
new AttributeDictionary());
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
Assert.Equal("Hello 'MyProperty', goodbye 'OtherProperty'.", rule.ErrorMessage);
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-equalto", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal("data-val-equalto-other", kvp.Key);
|
||||
Assert.Equal(kvp.Value, "*.OtherProperty");
|
||||
});
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
|
|
@ -138,15 +184,61 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
};
|
||||
var adapter = new CompareAttributeAdapter(attribute, stringLocalizer: null);
|
||||
|
||||
var expectedMessage = "Comparing MyProperty to OtherProperty.";
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, metadataProvider);
|
||||
var context = new ClientModelValidationContext(
|
||||
actionContext,
|
||||
metadata,
|
||||
metadataProvider,
|
||||
new AttributeDictionary());
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
Assert.Equal("Comparing MyProperty to OtherProperty.", rule.ErrorMessage);
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-equalto", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal("data-val-equalto-other", kvp.Key);
|
||||
Assert.Equal(kvp.Value, "*.OtherProperty");
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void AddValidation_DoesNotTrounceExistingAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var metadata = metadataProvider.GetMetadataForProperty(typeof(PropertyNameModel), "MyProperty");
|
||||
|
||||
var attribute = new CompareAttribute("OtherProperty");
|
||||
var adapter = new CompareAttributeAdapter(attribute, stringLocalizer: null);
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(
|
||||
actionContext,
|
||||
metadata,
|
||||
metadataProvider,
|
||||
new AttributeDictionary());
|
||||
|
||||
context.Attributes.Add("data-val", "original");
|
||||
context.Attributes.Add("data-val-equalto", "original");
|
||||
context.Attributes.Add("data-val-equalto-other", "original");
|
||||
|
||||
// Act
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("original", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-equalto", kvp.Key); Assert.Equal("original", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-equalto-other", kvp.Key); Assert.Equal("original", kvp.Value); });
|
||||
}
|
||||
|
||||
private class PropertyDisplayNameModel
|
||||
|
|
|
|||
|
|
@ -672,7 +672,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
private class TestValidationAttribute : ValidationAttribute, IClientModelValidator
|
||||
{
|
||||
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ClientModelValidationContext context)
|
||||
public void AddValidation(ClientModelValidationContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -245,9 +245,8 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
|
||||
private class CustomValidationAttribute : Attribute, IClientModelValidator
|
||||
{
|
||||
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ClientModelValidationContext context)
|
||||
public void AddValidation(ClientModelValidationContext context)
|
||||
{
|
||||
return Enumerable.Empty<ModelClientValidationRule>();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Moq;
|
||||
|
|
@ -15,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
{
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void ClientRulesWithMaxLengthAttribute_Localize()
|
||||
public void MaxLengthAttribute_AddValidation_Localize()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
|
|
@ -34,22 +35,22 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var adapter = new MaxLengthAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
Assert.Equal("maxlength", rule.ValidationType);
|
||||
Assert.Equal(1, rule.ValidationParameters.Count);
|
||||
Assert.Equal(10, rule.ValidationParameters["max"]);
|
||||
Assert.Equal(attribute.FormatErrorMessage("Length"), rule.ErrorMessage);
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-maxlength", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-maxlength-max", kvp.Key); Assert.Equal("10", kvp.Value); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void ClientRulesWithMaxLengthAttribute()
|
||||
public void MaxLengthAttribute_AddValidation()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
|
|
@ -58,23 +59,25 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var attribute = new MaxLengthAttribute(10);
|
||||
var adapter = new MaxLengthAttributeAdapter(attribute, stringLocalizer: null);
|
||||
|
||||
var expectedMessage = attribute.FormatErrorMessage("Length");
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
Assert.Equal("maxlength", rule.ValidationType);
|
||||
Assert.Equal(1, rule.ValidationParameters.Count);
|
||||
Assert.Equal(10, rule.ValidationParameters["max"]);
|
||||
Assert.Equal(attribute.FormatErrorMessage("Length"), rule.ErrorMessage);
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-maxlength", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-maxlength-max", kvp.Key); Assert.Equal("10", kvp.Value); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void ClientRulesWithMaxLengthAttributeAndCustomMessage()
|
||||
public void MaxLengthAttribute_AddValidation_CustomMessage()
|
||||
{
|
||||
// Arrange
|
||||
var propertyName = "Length";
|
||||
|
|
@ -82,26 +85,28 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var metadata = provider.GetMetadataForProperty(typeof(string), propertyName);
|
||||
|
||||
var expectedMessage = "Length must be at most 5";
|
||||
|
||||
var attribute = new MaxLengthAttribute(5) { ErrorMessage = message };
|
||||
var adapter = new MaxLengthAttributeAdapter(attribute, stringLocalizer: null);
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
Assert.Equal("maxlength", rule.ValidationType);
|
||||
Assert.Equal(1, rule.ValidationParameters.Count);
|
||||
Assert.Equal(5, rule.ValidationParameters["max"]);
|
||||
Assert.Equal("Length must be at most 5", rule.ErrorMessage);
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-maxlength", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-maxlength-max", kvp.Key); Assert.Equal("5", kvp.Value); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void ClientRulesWithMaxLengthAttribute_StringLocalizer_ReturnsLocalizedErrorString()
|
||||
public void MaxLengthAttribute_AddValidation_StringLocalizer_ReturnsLocalizedErrorString()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
|
|
@ -114,20 +119,53 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var stringLocalizer = new Mock<IStringLocalizer>();
|
||||
stringLocalizer.Setup(s => s[errorKey, metadata.GetDisplayName(), attribute.Length]).Returns(localizedString);
|
||||
|
||||
var expectedMessage = "Longueur est invalide";
|
||||
|
||||
var adapter = new MaxLengthAttributeAdapter(attribute, stringLocalizer.Object);
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
Assert.Equal("maxlength", rule.ValidationType);
|
||||
Assert.Equal(1, rule.ValidationParameters.Count);
|
||||
Assert.Equal(10, rule.ValidationParameters["max"]);
|
||||
Assert.Equal("Longueur est invalide", rule.ErrorMessage);
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-maxlength", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-maxlength-max", kvp.Key); Assert.Equal("10", kvp.Value); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void AddValidation_DoesNotTrounceExistingAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
|
||||
|
||||
var attribute = new MaxLengthAttribute(10);
|
||||
var adapter = new MaxLengthAttributeAdapter(attribute, stringLocalizer: null);
|
||||
|
||||
var expectedMessage = attribute.FormatErrorMessage("Length");
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||
|
||||
context.Attributes.Add("data-val", "original");
|
||||
context.Attributes.Add("data-val-maxlength", "original");
|
||||
context.Attributes.Add("data-val-maxlength-max", "original");
|
||||
|
||||
// Act
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("original", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-maxlength", kvp.Key); Assert.Equal("original", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-maxlength-max", kvp.Key); Assert.Equal("original", kvp.Value); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Moq;
|
||||
|
|
@ -15,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
{
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void ClientRulesWithMinLengthAttribute_Localize()
|
||||
public void MinLengthAttribute_AddValidation_Localize()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
|
|
@ -34,22 +35,22 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var adapter = new MinLengthAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
Assert.Equal("minlength", rule.ValidationType);
|
||||
Assert.Equal(1, rule.ValidationParameters.Count);
|
||||
Assert.Equal(6, rule.ValidationParameters["min"]);
|
||||
Assert.Equal(attribute.FormatErrorMessage("Length"), rule.ErrorMessage);
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-minlength", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-minlength-min", kvp.Key); Assert.Equal("6", kvp.Value); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void ClientRulesWithMinLengthAttribute()
|
||||
public void MinLengthAttribute_AddValidation_Attribute()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
|
|
@ -58,45 +59,78 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var attribute = new MinLengthAttribute(6);
|
||||
var adapter = new MinLengthAttributeAdapter(attribute, stringLocalizer: null);
|
||||
|
||||
var expectedMessage = attribute.FormatErrorMessage("Length");
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
Assert.Equal("minlength", rule.ValidationType);
|
||||
Assert.Equal(1, rule.ValidationParameters.Count);
|
||||
Assert.Equal(6, rule.ValidationParameters["min"]);
|
||||
Assert.Equal(attribute.FormatErrorMessage("Length"), rule.ErrorMessage);
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-minlength", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-minlength-min", kvp.Key); Assert.Equal("6", kvp.Value); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void ClientRulesWithMinLengthAttributeAndCustomMessage()
|
||||
public void MinLengthAttribute_AddValidation_AttributeAndCustomMessage()
|
||||
{
|
||||
// Arrange
|
||||
var propertyName = "Length";
|
||||
var message = "Array must have at least {1} items.";
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var metadata = provider.GetMetadataForProperty(typeof(string), propertyName);
|
||||
|
||||
var attribute = new MinLengthAttribute(2) { ErrorMessage = message };
|
||||
var attribute = new MinLengthAttribute(2) { ErrorMessage = "Array must have at least {1} items." };
|
||||
var adapter = new MinLengthAttributeAdapter(attribute, stringLocalizer: null);
|
||||
|
||||
var expectedMessage = "Array must have at least 2 items.";
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||
|
||||
// Act
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-minlength", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-minlength-min", kvp.Key); Assert.Equal("2", kvp.Value); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void AddValidation_DoesNotTrounceExistingAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var propertyName = "Length";
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var metadata = provider.GetMetadataForProperty(typeof(string), propertyName);
|
||||
|
||||
var attribute = new MinLengthAttribute(2) { ErrorMessage = "Array must have at least {1} items." };
|
||||
var adapter = new MinLengthAttributeAdapter(attribute, stringLocalizer: null);
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||
|
||||
context.Attributes.Add("data-val", "original");
|
||||
context.Attributes.Add("data-val-minlength", "original");
|
||||
context.Attributes.Add("data-val-minlength-min", "original");
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
Assert.Equal("minlength", rule.ValidationType);
|
||||
Assert.Equal(1, rule.ValidationParameters.Count);
|
||||
Assert.Equal(2, rule.ValidationParameters["min"]);
|
||||
Assert.Equal("Array must have at least 2 items.", rule.ErrorMessage);
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("original", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-minlength", kvp.Key); Assert.Equal("original", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-minlength-min", kvp.Key); Assert.Equal("original", kvp.Value); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -13,7 +14,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
{
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void ClientRulesWithCorrectValidationTypeAndErrorMessage()
|
||||
public void AddValidation_CorrectValidationTypeAndErrorMessage()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
|
|
@ -22,17 +23,44 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var adapter = new NumericClientModelValidator();
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||
|
||||
var expectedMessage = "The field DisplayId must be a number.";
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
Assert.Equal("number", rule.ValidationType);
|
||||
Assert.Equal(expectedMessage, rule.ErrorMessage);
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-number", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void AddValidation_DoesNotTrounceExistingAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var metadata = provider.GetMetadataForProperty(typeof(TypeWithNumericProperty), "Id");
|
||||
|
||||
var adapter = new NumericClientModelValidator();
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||
|
||||
context.Attributes.Add("data-val", "original");
|
||||
context.Attributes.Add("data-val-number", "original");
|
||||
|
||||
// Act
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("original", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-number", kvp.Key); Assert.Equal("original", kvp.Value); });
|
||||
}
|
||||
|
||||
private class TypeWithNumericProperty
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Mvc.DataAnnotations.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Moq;
|
||||
|
|
@ -14,7 +15,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
|||
{
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void GetClientValidationRules_ReturnsValidationParameters_WithoutLocalization()
|
||||
public void AddValidation_WithoutLocalization()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
|
|
@ -28,23 +29,23 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
|||
var adapter = new RangeAttributeAdapter(attribute, stringLocalizer: null);
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
Assert.Equal("range", rule.ValidationType);
|
||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||
Assert.Equal(0m, rule.ValidationParameters["min"]);
|
||||
Assert.Equal(100m, rule.ValidationParameters["max"]);
|
||||
Assert.Equal(expectedMessage, rule.ErrorMessage);
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-range", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-range-max", kvp.Key); Assert.Equal("100", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-range-min", kvp.Key); Assert.Equal("0", kvp.Value); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void GetClientValidationRules_ReturnsValidationParameters()
|
||||
public void AddValidation_WithLocalization()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
|
|
@ -57,24 +58,58 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
|||
var expectedMessage = "The field Length must be between 0 and 100.";
|
||||
|
||||
var stringLocalizer = new Mock<IStringLocalizer>();
|
||||
stringLocalizer.Setup(s => s[attribute.ErrorMessage, expectedProperties])
|
||||
stringLocalizer
|
||||
.Setup(s => s[attribute.ErrorMessage, expectedProperties])
|
||||
.Returns(new LocalizedString(attribute.ErrorMessage, expectedMessage));
|
||||
|
||||
var adapter = new RangeAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
Assert.Equal("range", rule.ValidationType);
|
||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||
Assert.Equal(0m, rule.ValidationParameters["min"]);
|
||||
Assert.Equal(100m, rule.ValidationParameters["max"]);
|
||||
Assert.Equal(expectedMessage, rule.ErrorMessage);
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-range", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-range-max", kvp.Key); Assert.Equal("100", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-range-min", kvp.Key); Assert.Equal("0", kvp.Value); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void AddValidation_DoesNotTrounceExistingAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
|
||||
|
||||
var attribute = new RangeAttribute(typeof(decimal), "0", "100");
|
||||
attribute.ErrorMessage = "The field Length must be between {1} and {2}.";
|
||||
|
||||
var adapter = new RangeAttributeAdapter(attribute, stringLocalizer: null);
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||
|
||||
context.Attributes.Add("data-val", "original");
|
||||
context.Attributes.Add("data-val-range", "original");
|
||||
context.Attributes.Add("data-val-range-max", "original");
|
||||
context.Attributes.Add("data-val-range-min", "original");
|
||||
|
||||
// Act
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("original", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-range", kvp.Key); Assert.Equal("original", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-range-max", kvp.Key); Assert.Equal("original", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-range-min", kvp.Key); Assert.Equal("original", kvp.Value); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Moq;
|
||||
|
|
@ -15,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
{
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void GetClientValidationRules_ReturnsValidationParameters_Localize()
|
||||
public void AddValidation_AddsValidation_Localize()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
|
|
@ -35,24 +36,24 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var adapter = new RequiredAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
Assert.Equal("required", rule.ValidationType);
|
||||
Assert.Empty(rule.ValidationParameters);
|
||||
Assert.Equal(expectedMessage, rule.ErrorMessage);
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-required", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void GetClientValidationRules_ReturnsValidationParameters()
|
||||
public void AddValidation_AddsValidation()
|
||||
{
|
||||
// Arrange
|
||||
var expected = ValidationAttributeUtil.GetRequiredErrorMessage("Length");
|
||||
var expectedMessage = ValidationAttributeUtil.GetRequiredErrorMessage("Length");
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
|
||||
|
||||
|
|
@ -60,16 +61,44 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var adapter = new RequiredAttributeAdapter(attribute, stringLocalizer: null);
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
Assert.Equal("required", rule.ValidationType);
|
||||
Assert.Empty(rule.ValidationParameters);
|
||||
Assert.Equal(expected, rule.ErrorMessage);
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-required", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void AddValidation_DoesNotTrounceExistingAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var expectedMessage = ValidationAttributeUtil.GetRequiredErrorMessage("Length");
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
|
||||
|
||||
var attribute = new RequiredAttribute();
|
||||
var adapter = new RequiredAttributeAdapter(attribute, stringLocalizer: null);
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||
|
||||
context.Attributes.Add("data-val", "original");
|
||||
context.Attributes.Add("data-val-required", "original");
|
||||
|
||||
// Act
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("original", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-required", kvp.Key); Assert.Equal("original", kvp.Value); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Moq;
|
||||
|
|
@ -15,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
{
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void GetClientValidationRules_WithMaxLength_ReturnsValidationParameters_Localize()
|
||||
public void AddValidation_WithMaxLength_AddsAttributes_Localize()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
|
|
@ -35,22 +36,23 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var adapter = new StringLengthAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
Assert.Equal("length", rule.ValidationType);
|
||||
Assert.Equal(1, rule.ValidationParameters.Count);
|
||||
Assert.Equal(8, rule.ValidationParameters["max"]);
|
||||
Assert.Equal(expectedMessage, rule.ErrorMessage);
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-length", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-length-max", kvp.Key); Assert.Equal("8", kvp.Value); });
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void GetClientValidationRules_WithMaxLength_ReturnsValidationParameters()
|
||||
public void AddValidation_WithMaxLength_AddsAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
|
|
@ -59,23 +61,25 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var attribute = new StringLengthAttribute(8);
|
||||
var adapter = new StringLengthAttributeAdapter(attribute, stringLocalizer: null);
|
||||
|
||||
var expectedMessage = attribute.FormatErrorMessage("Length");
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
Assert.Equal("length", rule.ValidationType);
|
||||
Assert.Equal(1, rule.ValidationParameters.Count);
|
||||
Assert.Equal(8, rule.ValidationParameters["max"]);
|
||||
Assert.Equal(attribute.FormatErrorMessage("Length"), rule.ErrorMessage);
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-length", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-length-max", kvp.Key); Assert.Equal("8", kvp.Value); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void GetClientValidationRules_WithMinAndMaxLength_ReturnsValidationParameters()
|
||||
public void AddValidation_WithMinAndMaxLength_AddsAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
|
|
@ -84,19 +88,80 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
var attribute = new StringLengthAttribute(10) { MinimumLength = 3 };
|
||||
var adapter = new StringLengthAttributeAdapter(attribute, stringLocalizer: null);
|
||||
|
||||
var expectedMessage = attribute.FormatErrorMessage("Length");
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||
|
||||
// Act
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
var rule = Assert.Single(rules);
|
||||
Assert.Equal("length", rule.ValidationType);
|
||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||
Assert.Equal(3, rule.ValidationParameters["min"]);
|
||||
Assert.Equal(10, rule.ValidationParameters["max"]);
|
||||
Assert.Equal(attribute.FormatErrorMessage("Length"), rule.ErrorMessage);
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-length", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-length-max", kvp.Key); Assert.Equal("10", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-length-min", kvp.Key); Assert.Equal("3", kvp.Value); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void AddValidation_WithMaxLength_AtIntMaxValue_AddsAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
|
||||
|
||||
var attribute = new StringLengthAttribute(int.MaxValue);
|
||||
var adapter = new StringLengthAttributeAdapter(attribute, stringLocalizer: null);
|
||||
|
||||
var expectedMessage = attribute.FormatErrorMessage("Length");
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||
|
||||
// Act
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-length", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[ReplaceCulture]
|
||||
public void AddValidation_DoesNotTrounceExistingAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
|
||||
|
||||
var attribute = new StringLengthAttribute(10) { MinimumLength = 3 };
|
||||
var adapter = new StringLengthAttributeAdapter(attribute, stringLocalizer: null);
|
||||
|
||||
var expectedMessage = attribute.FormatErrorMessage("Length");
|
||||
|
||||
var actionContext = new ActionContext();
|
||||
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||
|
||||
context.Attributes.Add("data-val", "original");
|
||||
context.Attributes.Add("data-val-length", "original");
|
||||
context.Attributes.Add("data-val-length-max", "original");
|
||||
context.Attributes.Add("data-val-length-min", "original");
|
||||
|
||||
// Act
|
||||
adapter.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("original", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-length", kvp.Key); Assert.Equal("original", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-length-max", kvp.Key); Assert.Equal("original", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-length-min", kvp.Key); Assert.Equal("original", kvp.Value); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
: base(attribute, stringLocalizer)
|
||||
{ }
|
||||
|
||||
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules(ClientModelValidationContext context)
|
||||
public override void AddValidation(ClientModelValidationContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,10 +64,10 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
|||
get
|
||||
{
|
||||
return new TheoryData<ValidationAttribute, string> {
|
||||
{ new UrlAttribute(), "url" },
|
||||
{ new CreditCardAttribute(), "creditcard" },
|
||||
{ new EmailAddressAttribute(), "email" },
|
||||
{ new PhoneAttribute(), "phone" }
|
||||
{ new UrlAttribute(), "data-val-url" },
|
||||
{ new CreditCardAttribute(), "data-val-creditcard" },
|
||||
{ new EmailAddressAttribute(), "data-val-email" },
|
||||
{ new PhoneAttribute(), "data-val-phone" }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -95,12 +95,13 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
|||
return tagBuilder;
|
||||
}
|
||||
|
||||
protected override IDictionary<string, object> GetValidationAttributes(
|
||||
protected override void AddValidationAttributes(
|
||||
ViewContext viewContext,
|
||||
TagBuilder tagBuilder,
|
||||
ModelExplorer modelExplorer,
|
||||
string name)
|
||||
string expression)
|
||||
{
|
||||
return ValidationAttributes;
|
||||
tagBuilder.MergeAttributes(ValidationAttributes);
|
||||
}
|
||||
|
||||
private static IOptions<MvcViewOptions> GetOptions()
|
||||
|
|
|
|||
|
|
@ -1085,13 +1085,6 @@ Environment.NewLine;
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||
ModelExplorer modelExplorer,
|
||||
string name)
|
||||
{
|
||||
return Enumerable.Empty<ModelClientValidationRule>();
|
||||
}
|
||||
|
||||
public IEnumerable<SelectListItem> GetEnumSelectList<TEnum>() where TEnum : struct
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Mvc.Abstractions;
|
|||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -184,19 +185,19 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void GetClientValidationRules_WithBadRouteName_Throws()
|
||||
public void AddValidation_WithBadRouteName_Throws()
|
||||
{
|
||||
// Arrange
|
||||
var attribute = new RemoteAttribute("nonexistentRoute");
|
||||
var context = GetValidationContextWithArea(currentArea: null);
|
||||
|
||||
// Act & Assert
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => attribute.GetClientValidationRules(context));
|
||||
var exception = Assert.Throws<InvalidOperationException>(() => attribute.AddValidation(context));
|
||||
Assert.Equal("No URL for remote validation could be found.", exception.Message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetClientValidationRules_WithRoute_CallsUrlHelperWithExpectedValues()
|
||||
public void AddValidation_WithRoute_CallsUrlHelperWithExpectedValues()
|
||||
{
|
||||
// Arrange
|
||||
var routeName = "RouteName";
|
||||
|
|
@ -205,21 +206,23 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
var urlHelper = new MockUrlHelper(url, routeName);
|
||||
var context = GetValidationContext(urlHelper);
|
||||
|
||||
// Act & Assert
|
||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||
Assert.Equal("remote", rule.ValidationType);
|
||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||
// Act
|
||||
attribute.AddValidation(context);
|
||||
|
||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
||||
Assert.Equal(url, rule.ValidationParameters["url"]);
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote", kvp.Key); Assert.Equal("'Length' is invalid.", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote-additionalfields", kvp.Key); Assert.Equal("*.Length", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote-url", kvp.Key); Assert.Equal(url, kvp.Value); });
|
||||
|
||||
var routeDictionary = Assert.IsType<RouteValueDictionary>(urlHelper.RouteValues);
|
||||
Assert.Empty(routeDictionary);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetClientValidationRules_WithActionController_CallsUrlHelperWithExpectedValues()
|
||||
public void AddValidation_WithActionController_CallsUrlHelperWithExpectedValues()
|
||||
{
|
||||
// Arrange
|
||||
var attribute = new RemoteAttribute("Action", "Controller");
|
||||
|
|
@ -227,14 +230,16 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
var urlHelper = new MockUrlHelper(url, routeName: null);
|
||||
var context = GetValidationContext(urlHelper);
|
||||
|
||||
// Act & Assert
|
||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||
Assert.Equal("remote", rule.ValidationType);
|
||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||
// Act
|
||||
attribute.AddValidation(context);
|
||||
|
||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
||||
Assert.Equal(url, rule.ValidationParameters["url"]);
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote", kvp.Key); Assert.Equal("'Length' is invalid.", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote-additionalfields", kvp.Key); Assert.Equal("*.Length", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote-url", kvp.Key); Assert.Equal(url, kvp.Value); });
|
||||
|
||||
var routeDictionary = Assert.IsType<RouteValueDictionary>(urlHelper.RouteValues);
|
||||
Assert.Equal(2, routeDictionary.Count);
|
||||
|
|
@ -243,7 +248,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void GetClientValidationRules_WithActionController_PropertiesSet_CallsUrlHelperWithExpectedValues()
|
||||
public void AddValidation_WithActionController_PropertiesSet_CallsUrlHelperWithExpectedValues()
|
||||
{
|
||||
// Arrange
|
||||
var attribute = new RemoteAttribute("Action", "Controller")
|
||||
|
|
@ -255,15 +260,21 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
var urlHelper = new MockUrlHelper(url, routeName: null);
|
||||
var context = GetValidationContext(urlHelper);
|
||||
|
||||
// Act & Assert
|
||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||
Assert.Equal("remote", rule.ValidationType);
|
||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||
// Act
|
||||
attribute.AddValidation(context);
|
||||
|
||||
Assert.Equal(3, rule.ValidationParameters.Count);
|
||||
Assert.Equal("*.Length,*.Password,*.ConfirmPassword", rule.ValidationParameters["additionalfields"]);
|
||||
Assert.Equal("POST", rule.ValidationParameters["type"]);
|
||||
Assert.Equal(url, rule.ValidationParameters["url"]);
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote", kvp.Key); Assert.Equal("'Length' is invalid.", kvp.Value); },
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal("data-val-remote-additionalfields", kvp.Key);
|
||||
Assert.Equal("*.Length,*.Password,*.ConfirmPassword", kvp.Value);
|
||||
},
|
||||
kvp => { Assert.Equal("data-val-remote-type", kvp.Key); Assert.Equal("POST", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote-url", kvp.Key); Assert.Equal(url, kvp.Value); });
|
||||
|
||||
var routeDictionary = Assert.IsType<RouteValueDictionary>(urlHelper.RouteValues);
|
||||
Assert.Equal(2, routeDictionary.Count);
|
||||
|
|
@ -272,7 +283,7 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void GetClientValidationRules_WithActionControllerArea_CallsUrlHelperWithExpectedValues()
|
||||
public void AddValidation_WithActionControllerArea_CallsUrlHelperWithExpectedValues()
|
||||
{
|
||||
// Arrange
|
||||
var attribute = new RemoteAttribute("Action", "Controller", "Test")
|
||||
|
|
@ -283,15 +294,21 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
var urlHelper = new MockUrlHelper(url, routeName: null);
|
||||
var context = GetValidationContext(urlHelper);
|
||||
|
||||
// Act & Assert
|
||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||
Assert.Equal("remote", rule.ValidationType);
|
||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||
// Act
|
||||
attribute.AddValidation(context);
|
||||
|
||||
Assert.Equal(3, rule.ValidationParameters.Count);
|
||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
||||
Assert.Equal("POST", rule.ValidationParameters["type"]);
|
||||
Assert.Equal(url, rule.ValidationParameters["url"]);
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote", kvp.Key); Assert.Equal("'Length' is invalid.", kvp.Value); },
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal("data-val-remote-additionalfields", kvp.Key);
|
||||
Assert.Equal("*.Length", kvp.Value);
|
||||
},
|
||||
kvp => { Assert.Equal("data-val-remote-type", kvp.Key); Assert.Equal("POST", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote-url", kvp.Key); Assert.Equal(url, kvp.Value); });
|
||||
|
||||
var routeDictionary = Assert.IsType<RouteValueDictionary>(urlHelper.RouteValues);
|
||||
Assert.Equal(3, routeDictionary.Count);
|
||||
|
|
@ -302,138 +319,203 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
|
||||
// Root area is current in this case.
|
||||
[Fact]
|
||||
public void GetClientValidationRules_WithActionController_FindsControllerInCurrentArea()
|
||||
public void AddValidation_WithActionController_FindsControllerInCurrentArea()
|
||||
{
|
||||
// Arrange
|
||||
var attribute = new RemoteAttribute("Action", "Controller");
|
||||
var context = GetValidationContextWithArea(currentArea: null);
|
||||
|
||||
// Act & Assert
|
||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||
Assert.Equal("remote", rule.ValidationType);
|
||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||
// Act
|
||||
attribute.AddValidation(context);
|
||||
|
||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
||||
Assert.Equal("/UrlEncode[[Controller]]/UrlEncode[[Action]]", rule.ValidationParameters["url"]);
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote", kvp.Key); Assert.Equal("'Length' is invalid.", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote-additionalfields", kvp.Key); Assert.Equal("*.Length", kvp.Value); },
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal("data-val-remote-url", kvp.Key);
|
||||
Assert.Equal("/UrlEncode[[Controller]]/UrlEncode[[Action]]", kvp.Value);
|
||||
});
|
||||
}
|
||||
|
||||
// Test area is current in this case.
|
||||
[Fact]
|
||||
public void GetClientValidationRules_WithActionControllerInArea_FindsControllerInCurrentArea()
|
||||
public void AddValidation_WithActionControllerInArea_FindsControllerInCurrentArea()
|
||||
{
|
||||
// Arrange
|
||||
var attribute = new RemoteAttribute("Action", "Controller");
|
||||
var context = GetValidationContextWithArea(currentArea: "Test");
|
||||
|
||||
// Act & Assert
|
||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||
Assert.Equal("remote", rule.ValidationType);
|
||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||
// Act
|
||||
attribute.AddValidation(context);
|
||||
|
||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
||||
Assert.Equal(
|
||||
"/UrlEncode[[Test]]/UrlEncode[[Controller]]/UrlEncode[[Action]]",
|
||||
rule.ValidationParameters["url"]);
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote", kvp.Key); Assert.Equal("'Length' is invalid.", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote-additionalfields", kvp.Key); Assert.Equal("*.Length", kvp.Value); },
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal("data-val-remote-url", kvp.Key);
|
||||
Assert.Equal("/UrlEncode[[Test]]/UrlEncode[[Controller]]/UrlEncode[[Action]]", kvp.Value);
|
||||
});
|
||||
}
|
||||
|
||||
// Explicit reference to the (current) root area.
|
||||
[Theory]
|
||||
[MemberData(nameof(NullOrEmptyNames))]
|
||||
public void GetClientValidationRules_WithActionControllerArea_FindsControllerInRootArea(string areaName)
|
||||
public void AddValidation_WithActionControllerArea_FindsControllerInRootArea(string areaName)
|
||||
{
|
||||
// Arrange
|
||||
var attribute = new RemoteAttribute("Action", "Controller", areaName);
|
||||
var context = GetValidationContextWithArea(currentArea: null);
|
||||
|
||||
// Act & Assert
|
||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||
Assert.Equal("remote", rule.ValidationType);
|
||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||
// Act
|
||||
attribute.AddValidation(context);
|
||||
|
||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
||||
Assert.Equal("/UrlEncode[[Controller]]/UrlEncode[[Action]]", rule.ValidationParameters["url"]);
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote", kvp.Key); Assert.Equal("'Length' is invalid.", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote-additionalfields", kvp.Key); Assert.Equal("*.Length", kvp.Value); },
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal("data-val-remote-url", kvp.Key);
|
||||
Assert.Equal("/UrlEncode[[Controller]]/UrlEncode[[Action]]", kvp.Value);
|
||||
});
|
||||
}
|
||||
|
||||
// Test area is current in this case.
|
||||
[Theory]
|
||||
[MemberData(nameof(NullOrEmptyNames))]
|
||||
public void GetClientValidationRules_WithActionControllerAreaInArea_FindsControllerInRootArea(string areaName)
|
||||
public void AddValidation_WithActionControllerAreaInArea_FindsControllerInRootArea(string areaName)
|
||||
{
|
||||
// Arrange
|
||||
var attribute = new RemoteAttribute("Action", "Controller", areaName);
|
||||
var context = GetValidationContextWithArea(currentArea: "Test");
|
||||
|
||||
// Act & Assert
|
||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||
Assert.Equal("remote", rule.ValidationType);
|
||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||
// Act
|
||||
attribute.AddValidation(context);
|
||||
|
||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
||||
Assert.Equal("/UrlEncode[[Controller]]/UrlEncode[[Action]]", rule.ValidationParameters["url"]);
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote", kvp.Key); Assert.Equal("'Length' is invalid.", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote-additionalfields", kvp.Key); Assert.Equal("*.Length", kvp.Value); },
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal("data-val-remote-url", kvp.Key);
|
||||
Assert.Equal("/UrlEncode[[Controller]]/UrlEncode[[Action]]", kvp.Value);
|
||||
});
|
||||
}
|
||||
|
||||
// Root area is current in this case.
|
||||
[Fact]
|
||||
public void GetClientValidationRules_WithActionControllerArea_FindsControllerInNamedArea()
|
||||
public void AddValidation_WithActionControllerArea_FindsControllerInNamedArea()
|
||||
{
|
||||
// Arrange
|
||||
var attribute = new RemoteAttribute("Action", "Controller", "Test");
|
||||
var context = GetValidationContextWithArea(currentArea: null);
|
||||
|
||||
// Act & Assert
|
||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||
Assert.Equal("remote", rule.ValidationType);
|
||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||
// Act
|
||||
attribute.AddValidation(context);
|
||||
|
||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
||||
Assert.Equal(
|
||||
"/UrlEncode[[Test]]/UrlEncode[[Controller]]/UrlEncode[[Action]]",
|
||||
rule.ValidationParameters["url"]);
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote", kvp.Key); Assert.Equal("'Length' is invalid.", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote-additionalfields", kvp.Key); Assert.Equal("*.Length", kvp.Value); },
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal("data-val-remote-url", kvp.Key);
|
||||
Assert.Equal("/UrlEncode[[Test]]/UrlEncode[[Controller]]/UrlEncode[[Action]]", kvp.Value);
|
||||
});
|
||||
}
|
||||
|
||||
// Explicit reference to the current (Test) area.
|
||||
[Fact]
|
||||
public void GetClientValidationRules_WithActionControllerAreaInArea_FindsControllerInNamedArea()
|
||||
public void AddValidation_WithActionControllerAreaInArea_FindsControllerInNamedArea()
|
||||
{
|
||||
// Arrange
|
||||
var attribute = new RemoteAttribute("Action", "Controller", "Test");
|
||||
var context = GetValidationContextWithArea(currentArea: "Test");
|
||||
|
||||
// Act & Assert
|
||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||
Assert.Equal("remote", rule.ValidationType);
|
||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||
// Act
|
||||
attribute.AddValidation(context);
|
||||
|
||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
||||
Assert.Equal(
|
||||
"/UrlEncode[[Test]]/UrlEncode[[Controller]]/UrlEncode[[Action]]",
|
||||
rule.ValidationParameters["url"]);
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote", kvp.Key); Assert.Equal("'Length' is invalid.", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote-additionalfields", kvp.Key); Assert.Equal("*.Length", kvp.Value); },
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal("data-val-remote-url", kvp.Key);
|
||||
Assert.Equal("/UrlEncode[[Test]]/UrlEncode[[Controller]]/UrlEncode[[Action]]", kvp.Value);
|
||||
});
|
||||
}
|
||||
|
||||
// Test area is current in this case.
|
||||
[Fact]
|
||||
public void GetClientValidationRules_WithActionControllerAreaInArea_FindsControllerInDifferentArea()
|
||||
public void AddValidation_WithActionControllerAreaInArea_FindsControllerInDifferentArea()
|
||||
{
|
||||
// Arrange
|
||||
var attribute = new RemoteAttribute("Action", "Controller", "AnotherArea");
|
||||
var context = GetValidationContextWithArea(currentArea: "Test");
|
||||
|
||||
// Act & Assert
|
||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||
Assert.Equal("remote", rule.ValidationType);
|
||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||
// Act
|
||||
attribute.AddValidation(context);
|
||||
|
||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
||||
Assert.Equal(
|
||||
"/UrlEncode[[AnotherArea]]/UrlEncode[[Controller]]/UrlEncode[[Action]]",
|
||||
rule.ValidationParameters["url"]);
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote", kvp.Key); Assert.Equal("'Length' is invalid.", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote-additionalfields", kvp.Key); Assert.Equal("*.Length", kvp.Value); },
|
||||
kvp =>
|
||||
{
|
||||
Assert.Equal("data-val-remote-url", kvp.Key);
|
||||
Assert.Equal("/UrlEncode[[AnotherArea]]/UrlEncode[[Controller]]/UrlEncode[[Action]]", kvp.Value);
|
||||
});
|
||||
}
|
||||
|
||||
// Test area is current in this case.
|
||||
[Fact]
|
||||
public void AddValidation_DoesNotTrounceExistingAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var attribute = new RemoteAttribute("Action", "Controller", "AnotherArea")
|
||||
{
|
||||
HttpMethod = "PUT",
|
||||
};
|
||||
|
||||
var context = GetValidationContextWithArea(currentArea: "Test");
|
||||
|
||||
context.Attributes.Add("data-val", "original");
|
||||
context.Attributes.Add("data-val-remote", "original");
|
||||
context.Attributes.Add("data-val-remote-additionalfields", "original");
|
||||
context.Attributes.Add("data-val-remote-type", "original");
|
||||
context.Attributes.Add("data-val-remote-url", "original");
|
||||
|
||||
// Act
|
||||
attribute.AddValidation(context);
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
context.Attributes,
|
||||
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("original", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote", kvp.Key); Assert.Equal("original", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote-additionalfields", kvp.Key); Assert.Equal("original", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote-type", kvp.Key); Assert.Equal("original", kvp.Value); },
|
||||
kvp => { Assert.Equal("data-val-remote-url", kvp.Key); Assert.Equal("original", kvp.Value); });
|
||||
}
|
||||
|
||||
private static ClientModelValidationContext GetValidationContext(IUrlHelper urlHelper)
|
||||
|
|
@ -455,7 +537,11 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
},
|
||||
};
|
||||
|
||||
return new ClientModelValidationContext(actionContext, _metadata, _metadataProvider);
|
||||
return new ClientModelValidationContext(
|
||||
actionContext,
|
||||
_metadata,
|
||||
_metadataProvider,
|
||||
new AttributeDictionary());
|
||||
}
|
||||
|
||||
private static ClientModelValidationContext GetValidationContextWithArea(string currentArea)
|
||||
|
|
@ -499,7 +585,11 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
},
|
||||
};
|
||||
|
||||
return new ClientModelValidationContext(actionContext, _metadata, _metadataProvider);
|
||||
return new ClientModelValidationContext(
|
||||
actionContext,
|
||||
_metadata,
|
||||
_metadataProvider,
|
||||
new AttributeDictionary());
|
||||
}
|
||||
|
||||
private static IRouter GetRouteCollectionWithArea(IServiceProvider serviceProvider)
|
||||
|
|
|
|||
Loading…
Reference in New Issue