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.
|
// 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.
|
// 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
|
namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -14,12 +16,20 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
||||||
/// <param name="actionContext">The <see cref="ActionContext"/> for validation.</param>
|
/// <param name="actionContext">The <see cref="ActionContext"/> for validation.</param>
|
||||||
/// <param name="metadata">The <see cref="ModelMetadata"/> 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="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(
|
public ClientModelValidationContext(
|
||||||
ActionContext actionContext,
|
ActionContext actionContext,
|
||||||
ModelMetadata metadata,
|
ModelMetadata metadata,
|
||||||
IModelMetadataProvider metadataProvider)
|
IModelMetadataProvider metadataProvider,
|
||||||
|
IDictionary<string, string> attributes)
|
||||||
: base(actionContext, metadata, metadataProvider)
|
: 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.
|
// 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.
|
// 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
|
namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
public interface IClientModelValidator
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
|
|
@ -13,26 +12,24 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
{
|
{
|
||||||
public class CompareAttributeAdapter : AttributeAdapterBase<CompareAttribute>
|
public class CompareAttributeAdapter : AttributeAdapterBase<CompareAttribute>
|
||||||
{
|
{
|
||||||
|
private readonly string _otherProperty;
|
||||||
|
|
||||||
public CompareAttributeAdapter(CompareAttribute attribute, IStringLocalizer stringLocalizer)
|
public CompareAttributeAdapter(CompareAttribute attribute, IStringLocalizer stringLocalizer)
|
||||||
: base(new CompareAttributeWrapper(attribute), stringLocalizer)
|
: base(new CompareAttributeWrapper(attribute), stringLocalizer)
|
||||||
{
|
{
|
||||||
if (attribute == null)
|
_otherProperty = "*." + attribute.OtherProperty;
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(attribute));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
public override void AddValidation(ClientModelValidationContext context)
|
||||||
ClientModelValidationContext context)
|
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
var errorMessage = GetErrorMessage(context);
|
MergeAttribute(context.Attributes, "data-val", "true");
|
||||||
var clientRule = new ModelClientValidationEqualToRule(errorMessage, "*." + Attribute.OtherProperty);
|
MergeAttribute(context.Attributes, "data-val-equalto", GetErrorMessage(context));
|
||||||
return new[] { clientRule };
|
MergeAttribute(context.Attributes, "data-val-equalto-other", _otherProperty);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
@ -26,18 +25,17 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
RuleName = ruleName;
|
RuleName = ruleName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string RuleName { get; private set; }
|
public string RuleName { get; }
|
||||||
|
|
||||||
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
public override void AddValidation(ClientModelValidationContext context)
|
||||||
ClientModelValidationContext context)
|
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
var errorMessage = GetErrorMessage(context);
|
MergeAttribute(context.Attributes, "data-val", "true");
|
||||||
return new[] { new ModelClientValidationRule(RuleName, errorMessage) };
|
MergeAttribute(context.Attributes, RuleName, GetErrorMessage(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Globalization;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
|
|
@ -11,21 +11,24 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
{
|
{
|
||||||
public class MaxLengthAttributeAdapter : AttributeAdapterBase<MaxLengthAttribute>
|
public class MaxLengthAttributeAdapter : AttributeAdapterBase<MaxLengthAttribute>
|
||||||
{
|
{
|
||||||
|
private readonly string _max;
|
||||||
|
|
||||||
public MaxLengthAttributeAdapter(MaxLengthAttribute attribute, IStringLocalizer stringLocalizer)
|
public MaxLengthAttributeAdapter(MaxLengthAttribute attribute, IStringLocalizer stringLocalizer)
|
||||||
: base(attribute, stringLocalizer)
|
: base(attribute, stringLocalizer)
|
||||||
{
|
{
|
||||||
|
_max = Attribute.Length.ToString(CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
public override void AddValidation(ClientModelValidationContext context)
|
||||||
ClientModelValidationContext context)
|
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
var message = GetErrorMessage(context);
|
MergeAttribute(context.Attributes, "data-val", "true");
|
||||||
return new[] { new ModelClientValidationMaxLengthRule(message, Attribute.Length) };
|
MergeAttribute(context.Attributes, "data-val-maxlength", GetErrorMessage(context));
|
||||||
|
MergeAttribute(context.Attributes, "data-val-maxlength-max", _max);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Globalization;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
|
|
@ -11,21 +11,24 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
{
|
{
|
||||||
public class MinLengthAttributeAdapter : AttributeAdapterBase<MinLengthAttribute>
|
public class MinLengthAttributeAdapter : AttributeAdapterBase<MinLengthAttribute>
|
||||||
{
|
{
|
||||||
|
private readonly string _min;
|
||||||
|
|
||||||
public MinLengthAttributeAdapter(MinLengthAttribute attribute, IStringLocalizer stringLocalizer)
|
public MinLengthAttributeAdapter(MinLengthAttribute attribute, IStringLocalizer stringLocalizer)
|
||||||
: base(attribute, stringLocalizer)
|
: base(attribute, stringLocalizer)
|
||||||
{
|
{
|
||||||
|
_min = Attribute.Length.ToString(CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
public override void AddValidation(ClientModelValidationContext context)
|
||||||
ClientModelValidationContext context)
|
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
var message = GetErrorMessage(context);
|
MergeAttribute(context.Attributes, "data-val", "true");
|
||||||
return new[] { new ModelClientValidationMinLengthRule(message, Attribute.Length) };
|
MergeAttribute(context.Attributes, "data-val-minlength", GetErrorMessage(context));
|
||||||
|
MergeAttribute(context.Attributes, "data-val-minlength-min", _min);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <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
|
public class NumericClientModelValidator : IClientModelValidator
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ClientModelValidationContext context)
|
public void AddValidation(ClientModelValidationContext context)
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(context));
|
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)
|
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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Globalization;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
|
|
@ -11,26 +11,34 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
{
|
{
|
||||||
public class RangeAttributeAdapter : AttributeAdapterBase<RangeAttribute>
|
public class RangeAttributeAdapter : AttributeAdapterBase<RangeAttribute>
|
||||||
{
|
{
|
||||||
|
private readonly string _max;
|
||||||
|
private readonly string _min;
|
||||||
|
|
||||||
public RangeAttributeAdapter(RangeAttribute attribute, IStringLocalizer stringLocalizer)
|
public RangeAttributeAdapter(RangeAttribute attribute, IStringLocalizer stringLocalizer)
|
||||||
: base(attribute, 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(
|
public override void AddValidation(ClientModelValidationContext context)
|
||||||
ClientModelValidationContext context)
|
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Only calling this so Minimum and Maximum convert. Caused by a bug in CoreFx.
|
MergeAttribute(context.Attributes, "data-val", "true");
|
||||||
Attribute.IsValid(null);
|
MergeAttribute(context.Attributes, "data-val-range", GetErrorMessage(context));
|
||||||
|
MergeAttribute(context.Attributes, "data-val-range-max", _max);
|
||||||
var errorMessage = GetErrorMessage(context);
|
MergeAttribute(context.Attributes, "data-val-range-min", _min);
|
||||||
|
|
||||||
|
|
||||||
return new[] { new ModelClientValidationRangeRule(errorMessage, Attribute.Minimum, Attribute.Maximum) };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
@ -16,16 +15,16 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
public override void AddValidation(ClientModelValidationContext context)
|
||||||
ClientModelValidationContext context)
|
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
var errorMessage = GetErrorMessage(context);
|
MergeAttribute(context.Attributes, "data-val", "true");
|
||||||
return new[] { new ModelClientValidationRegexRule(errorMessage, Attribute.Pattern) };
|
MergeAttribute(context.Attributes, "data-val-regex", GetErrorMessage(context));
|
||||||
|
MergeAttribute(context.Attributes, "data-val-regex-pattern", Attribute.Pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
@ -16,16 +15,15 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
public override void AddValidation(ClientModelValidationContext context)
|
||||||
ClientModelValidationContext context)
|
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
var errorMessage = GetErrorMessage(context);
|
MergeAttribute(context.Attributes, "data-val", "true");
|
||||||
return new[] { new ModelClientValidationRequiredRule(errorMessage) };
|
MergeAttribute(context.Attributes, "data-val-required", GetErrorMessage(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Globalization;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
||||||
|
|
@ -11,24 +11,36 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
{
|
{
|
||||||
public class StringLengthAttributeAdapter : AttributeAdapterBase<StringLengthAttribute>
|
public class StringLengthAttributeAdapter : AttributeAdapterBase<StringLengthAttribute>
|
||||||
{
|
{
|
||||||
|
private readonly string _max;
|
||||||
|
private readonly string _min;
|
||||||
|
|
||||||
public StringLengthAttributeAdapter(StringLengthAttribute attribute, IStringLocalizer stringLocalizer)
|
public StringLengthAttributeAdapter(StringLengthAttribute attribute, IStringLocalizer stringLocalizer)
|
||||||
: base(attribute, stringLocalizer)
|
: base(attribute, stringLocalizer)
|
||||||
{
|
{
|
||||||
|
_max = Attribute.MaximumLength.ToString(CultureInfo.InvariantCulture);
|
||||||
|
_min = Attribute.MinimumLength.ToString(CultureInfo.InvariantCulture);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
/// <inheritdoc />
|
||||||
ClientModelValidationContext context)
|
public override void AddValidation(ClientModelValidationContext context)
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
var errorMessage = GetErrorMessage(context);
|
MergeAttribute(context.Attributes, "data-val", "true");
|
||||||
var rule = new ModelClientValidationStringLengthRule(errorMessage,
|
MergeAttribute(context.Attributes, "data-val-length", GetErrorMessage(context));
|
||||||
Attribute.MinimumLength,
|
|
||||||
Attribute.MaximumLength);
|
if (Attribute.MaximumLength != int.MaxValue)
|
||||||
return new[] { rule };
|
{
|
||||||
|
MergeAttribute(context.Attributes, "data-val-length-max", _max);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Attribute.MinimumLength != 0)
|
||||||
|
{
|
||||||
|
MergeAttribute(context.Attributes, "data-val-length-min", _min);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
|
||||||
|
|
@ -32,14 +32,30 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the <typeparamref name="TAttribute"/> instance.
|
/// Gets the <typeparamref name="TAttribute"/> instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TAttribute Attribute
|
public TAttribute Attribute { get; }
|
||||||
{
|
|
||||||
get;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public abstract IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
public abstract void AddValidation(ClientModelValidationContext context);
|
||||||
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>
|
/// <summary>
|
||||||
/// Gets the error message formatted using the <see cref="Attribute"/>.
|
/// 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))
|
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))
|
else if (type == typeof(StringLengthAttribute))
|
||||||
{
|
{
|
||||||
|
|
@ -63,15 +63,15 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
}
|
}
|
||||||
else if (type == typeof(EmailAddressAttribute))
|
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))
|
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))
|
else if (type == typeof(UrlAttribute))
|
||||||
{
|
{
|
||||||
adapter = new DataTypeAttributeAdapter((DataTypeAttribute)attribute, "url", stringLocalizer);
|
adapter = new DataTypeAttributeAdapter((DataTypeAttribute)attribute, "data-val-url", stringLocalizer);
|
||||||
}
|
}
|
||||||
else
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
public virtual void AddValidation(ClientModelValidationContext context)
|
||||||
/// <exception cref="InvalidOperationException">
|
|
||||||
/// Thrown if unable to generate a target URL for a validation request.
|
|
||||||
/// </exception>
|
|
||||||
public virtual IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
|
||||||
ClientModelValidationContext context)
|
|
||||||
{
|
{
|
||||||
if (context == null)
|
if (context == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(context));
|
throw new ArgumentNullException(nameof(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
var metadata = context.ModelMetadata;
|
MergeAttribute(context.Attributes, "data-val", "true");
|
||||||
var rule = new ModelClientValidationRemoteRule(
|
|
||||||
FormatErrorMessage(metadata.GetDisplayName()),
|
|
||||||
GetUrl(context),
|
|
||||||
HttpMethod,
|
|
||||||
FormatAdditionalFieldsForClientValidation(metadata.PropertyName));
|
|
||||||
|
|
||||||
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)
|
private static IEnumerable<string> SplitAndTrimPropertyNames(string original)
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ using System.Text.Encodings.Web;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Html;
|
using Microsoft.AspNetCore.Html;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
|
||||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.Rendering
|
namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
|
|
@ -375,20 +374,6 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
|
||||||
/// <returns>A <see cref="string"/> containing the element Id.</returns>
|
/// <returns>A <see cref="string"/> containing the element Id.</returns>
|
||||||
string GenerateIdFromName(string fullName);
|
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>
|
/// <summary>
|
||||||
/// Returns a select list for the given <typeparamref name="TEnum"/>.
|
/// Returns a select list for the given <typeparamref name="TEnum"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -569,7 +569,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tagBuilder.MergeAttributes(GetValidationAttributes(viewContext, modelExplorer, expression));
|
AddValidationAttributes(viewContext, tagBuilder, modelExplorer, expression);
|
||||||
|
|
||||||
return tagBuilder;
|
return tagBuilder;
|
||||||
}
|
}
|
||||||
|
|
@ -640,7 +640,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
}
|
}
|
||||||
|
|
||||||
tagBuilder.MergeAttribute("name", fullName, true);
|
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 there are any errors for a named field, we add this CSS attribute.
|
||||||
if (entry != null && entry.Errors.Count > 0)
|
if (entry != null && entry.Errors.Count > 0)
|
||||||
|
|
@ -865,31 +865,6 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
return tagBuilder;
|
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 />
|
/// <inheritdoc />
|
||||||
public virtual ICollection<string> GetCurrentValues(
|
public virtual ICollection<string> GetCurrentValues(
|
||||||
ViewContext viewContext,
|
ViewContext viewContext,
|
||||||
|
|
@ -1251,7 +1226,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
|
tagBuilder.AddCssClass(HtmlHelper.ValidationInputCssClassName);
|
||||||
}
|
}
|
||||||
|
|
||||||
tagBuilder.MergeAttributes(GetValidationAttributes(viewContext, modelExplorer, expression));
|
AddValidationAttributes(viewContext, tagBuilder, modelExplorer, expression);
|
||||||
|
|
||||||
return tagBuilder;
|
return tagBuilder;
|
||||||
}
|
}
|
||||||
|
|
@ -1275,30 +1250,58 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
return tagBuilder;
|
return tagBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only render attributes if client-side validation is enabled, and then only if we've
|
/// <summary>
|
||||||
// never rendered validation for a field with this name in this form.
|
/// Adds validation attributes to the <paramref name="tagBuilder" /> if client validation
|
||||||
protected virtual IDictionary<string, object> GetValidationAttributes(
|
/// 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,
|
ViewContext viewContext,
|
||||||
|
TagBuilder tagBuilder,
|
||||||
ModelExplorer modelExplorer,
|
ModelExplorer modelExplorer,
|
||||||
string expression)
|
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;
|
var formContext = viewContext.ClientValidationEnabled ? viewContext.FormContext : null;
|
||||||
if (formContext == null)
|
if (formContext == null)
|
||||||
{
|
{
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fullName = GetFullHtmlFieldName(viewContext, expression);
|
var fullName = GetFullHtmlFieldName(viewContext, expression);
|
||||||
if (formContext.RenderedField(fullName))
|
if (formContext.RenderedField(fullName))
|
||||||
{
|
{
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
formContext.RenderedField(fullName, true);
|
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)
|
private static Enum ConvertEnumFromInteger(object value, Type targetType)
|
||||||
|
|
|
||||||
|
|
@ -1212,14 +1212,6 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
return resolvedValue;
|
return resolvedValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
|
||||||
ModelExplorer modelExplorer,
|
|
||||||
string expression)
|
|
||||||
{
|
|
||||||
return _htmlGenerator.GetClientValidationRules(ViewContext, modelExplorer, expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a select list for the given <paramref name="metadata"/>.
|
/// Returns a select list for the given <paramref name="metadata"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -365,15 +365,6 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||||
string headerTag,
|
string headerTag,
|
||||||
object htmlAttributes);
|
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>
|
/// <summary>
|
||||||
/// Gets the collection of current values for the given <paramref name="expression"/>.
|
/// Gets the collection of current values for the given <paramref name="expression"/>.
|
||||||
/// </summary>
|
/// </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
|
private class TestClientModelValidationAttribute : Attribute, IClientModelValidator
|
||||||
{
|
{
|
||||||
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ClientModelValidationContext context)
|
public void AddValidation(ClientModelValidationContext context)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
@ -87,7 +87,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Metadata
|
||||||
|
|
||||||
private class TestValidationAttribute : Attribute, IModelValidator, IClientModelValidator
|
private class TestValidationAttribute : Attribute, IModelValidator, IClientModelValidator
|
||||||
{
|
{
|
||||||
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ClientModelValidationContext context)
|
public void AddValidation(ClientModelValidationContext context)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
using Microsoft.AspNetCore.Testing;
|
using Microsoft.AspNetCore.Testing;
|
||||||
using Microsoft.AspNetCore.Testing.xunit;
|
using Microsoft.AspNetCore.Testing.xunit;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
|
|
@ -25,19 +26,30 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
var attribute = new CompareAttribute("OtherProperty");
|
var attribute = new CompareAttribute("OtherProperty");
|
||||||
var adapter = new CompareAttributeAdapter(attribute, stringLocalizer: null);
|
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 actionContext = new ActionContext();
|
||||||
var context = new ClientModelValidationContext(actionContext, metadata, metadataProvider);
|
var context = new ClientModelValidationContext(
|
||||||
|
actionContext,
|
||||||
|
metadata,
|
||||||
|
metadataProvider,
|
||||||
|
new AttributeDictionary());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
adapter.AddValidation(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var rule = Assert.Single(rules);
|
Assert.Collection(
|
||||||
// Mono issue - https://github.com/aspnet/External/issues/19
|
context.Attributes,
|
||||||
Assert.Equal(
|
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||||
PlatformNormalizer.NormalizeContent(
|
kvp => { Assert.Equal("data-val-equalto", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||||
"'MyPropertyDisplayName' and 'OtherPropertyDisplayName' do not match."),
|
kvp =>
|
||||||
rule.ErrorMessage);
|
{
|
||||||
|
Assert.Equal("data-val-equalto-other", kvp.Key);
|
||||||
|
Assert.Equal(kvp.Value, "*.OtherProperty");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -62,15 +74,25 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
var adapter = new CompareAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
var adapter = new CompareAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
||||||
|
|
||||||
var actionContext = new ActionContext();
|
var actionContext = new ActionContext();
|
||||||
var context = new ClientModelValidationContext(actionContext, metadata, metadataProvider);
|
var context = new ClientModelValidationContext(
|
||||||
|
actionContext,
|
||||||
|
metadata,
|
||||||
|
metadataProvider,
|
||||||
|
new AttributeDictionary());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
adapter.AddValidation(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var rule = Assert.Single(rules);
|
Assert.Collection(
|
||||||
// Mono issue - https://github.com/aspnet/External/issues/19
|
context.Attributes,
|
||||||
Assert.Equal(expectedMessage, rule.ErrorMessage);
|
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]
|
[Fact]
|
||||||
|
|
@ -84,18 +106,29 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
var attribute = new CompareAttribute("OtherProperty");
|
var attribute = new CompareAttribute("OtherProperty");
|
||||||
var adapter = new CompareAttributeAdapter(attribute, stringLocalizer: null);
|
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 actionContext = new ActionContext();
|
||||||
var context = new ClientModelValidationContext(actionContext, metadata, metadataProvider);
|
var context = new ClientModelValidationContext(
|
||||||
|
actionContext,
|
||||||
|
metadata,
|
||||||
|
metadataProvider,
|
||||||
|
new AttributeDictionary());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
adapter.AddValidation(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var rule = Assert.Single(rules);
|
Assert.Collection(
|
||||||
// Mono issue - https://github.com/aspnet/External/issues/19
|
context.Attributes,
|
||||||
Assert.Equal(
|
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||||
PlatformNormalizer.NormalizeContent("'MyProperty' and 'OtherProperty' do not match."),
|
kvp => { Assert.Equal("data-val-equalto", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||||
rule.ErrorMessage);
|
kvp =>
|
||||||
|
{
|
||||||
|
Assert.Equal("data-val-equalto-other", kvp.Key);
|
||||||
|
Assert.Equal(kvp.Value, "*.OtherProperty");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -111,15 +144,28 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
};
|
};
|
||||||
var adapter = new CompareAttributeAdapter(attribute, stringLocalizer: null);
|
var adapter = new CompareAttributeAdapter(attribute, stringLocalizer: null);
|
||||||
|
|
||||||
|
var expectedMessage = "Hello 'MyProperty', goodbye 'OtherProperty'.";
|
||||||
|
|
||||||
var actionContext = new ActionContext();
|
var actionContext = new ActionContext();
|
||||||
var context = new ClientModelValidationContext(actionContext, metadata, metadataProvider);
|
var context = new ClientModelValidationContext(
|
||||||
|
actionContext,
|
||||||
|
metadata,
|
||||||
|
metadataProvider,
|
||||||
|
new AttributeDictionary());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
adapter.AddValidation(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var rule = Assert.Single(rules);
|
Assert.Collection(
|
||||||
Assert.Equal("Hello 'MyProperty', goodbye 'OtherProperty'.", rule.ErrorMessage);
|
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]
|
[ConditionalFact]
|
||||||
|
|
@ -138,15 +184,61 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
};
|
};
|
||||||
var adapter = new CompareAttributeAdapter(attribute, stringLocalizer: null);
|
var adapter = new CompareAttributeAdapter(attribute, stringLocalizer: null);
|
||||||
|
|
||||||
|
var expectedMessage = "Comparing MyProperty to OtherProperty.";
|
||||||
|
|
||||||
var actionContext = new ActionContext();
|
var actionContext = new ActionContext();
|
||||||
var context = new ClientModelValidationContext(actionContext, metadata, metadataProvider);
|
var context = new ClientModelValidationContext(
|
||||||
|
actionContext,
|
||||||
|
metadata,
|
||||||
|
metadataProvider,
|
||||||
|
new AttributeDictionary());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
adapter.AddValidation(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var rule = Assert.Single(rules);
|
Assert.Collection(
|
||||||
Assert.Equal("Comparing MyProperty to OtherProperty.", rule.ErrorMessage);
|
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
|
private class PropertyDisplayNameModel
|
||||||
|
|
|
||||||
|
|
@ -672,7 +672,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
|
|
||||||
private class TestValidationAttribute : ValidationAttribute, IClientModelValidator
|
private class TestValidationAttribute : ValidationAttribute, IClientModelValidator
|
||||||
{
|
{
|
||||||
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ClientModelValidationContext context)
|
public void AddValidation(ClientModelValidationContext context)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -245,9 +245,8 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
|
|
||||||
private class CustomValidationAttribute : Attribute, IClientModelValidator
|
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 System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
using Microsoft.AspNetCore.Testing;
|
using Microsoft.AspNetCore.Testing;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
|
@ -15,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void ClientRulesWithMaxLengthAttribute_Localize()
|
public void MaxLengthAttribute_AddValidation_Localize()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
|
|
@ -34,22 +35,22 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
var adapter = new MaxLengthAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
var adapter = new MaxLengthAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
||||||
|
|
||||||
var actionContext = new ActionContext();
|
var actionContext = new ActionContext();
|
||||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
adapter.AddValidation(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var rule = Assert.Single(rules);
|
Assert.Collection(
|
||||||
Assert.Equal("maxlength", rule.ValidationType);
|
context.Attributes,
|
||||||
Assert.Equal(1, rule.ValidationParameters.Count);
|
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||||
Assert.Equal(10, rule.ValidationParameters["max"]);
|
kvp => { Assert.Equal("data-val-maxlength", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||||
Assert.Equal(attribute.FormatErrorMessage("Length"), rule.ErrorMessage);
|
kvp => { Assert.Equal("data-val-maxlength-max", kvp.Key); Assert.Equal("10", kvp.Value); });
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void ClientRulesWithMaxLengthAttribute()
|
public void MaxLengthAttribute_AddValidation()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
|
|
@ -58,23 +59,25 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
var attribute = new MaxLengthAttribute(10);
|
var attribute = new MaxLengthAttribute(10);
|
||||||
var adapter = new MaxLengthAttributeAdapter(attribute, stringLocalizer: null);
|
var adapter = new MaxLengthAttributeAdapter(attribute, stringLocalizer: null);
|
||||||
|
|
||||||
|
var expectedMessage = attribute.FormatErrorMessage("Length");
|
||||||
|
|
||||||
var actionContext = new ActionContext();
|
var actionContext = new ActionContext();
|
||||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
adapter.AddValidation(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var rule = Assert.Single(rules);
|
Assert.Collection(
|
||||||
Assert.Equal("maxlength", rule.ValidationType);
|
context.Attributes,
|
||||||
Assert.Equal(1, rule.ValidationParameters.Count);
|
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||||
Assert.Equal(10, rule.ValidationParameters["max"]);
|
kvp => { Assert.Equal("data-val-maxlength", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||||
Assert.Equal(attribute.FormatErrorMessage("Length"), rule.ErrorMessage);
|
kvp => { Assert.Equal("data-val-maxlength-max", kvp.Key); Assert.Equal("10", kvp.Value); });
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void ClientRulesWithMaxLengthAttributeAndCustomMessage()
|
public void MaxLengthAttribute_AddValidation_CustomMessage()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var propertyName = "Length";
|
var propertyName = "Length";
|
||||||
|
|
@ -82,26 +85,28 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var metadata = provider.GetMetadataForProperty(typeof(string), propertyName);
|
var metadata = provider.GetMetadataForProperty(typeof(string), propertyName);
|
||||||
|
|
||||||
|
var expectedMessage = "Length must be at most 5";
|
||||||
|
|
||||||
var attribute = new MaxLengthAttribute(5) { ErrorMessage = message };
|
var attribute = new MaxLengthAttribute(5) { ErrorMessage = message };
|
||||||
var adapter = new MaxLengthAttributeAdapter(attribute, stringLocalizer: null);
|
var adapter = new MaxLengthAttributeAdapter(attribute, stringLocalizer: null);
|
||||||
|
|
||||||
var actionContext = new ActionContext();
|
var actionContext = new ActionContext();
|
||||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
adapter.AddValidation(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var rule = Assert.Single(rules);
|
Assert.Collection(
|
||||||
Assert.Equal("maxlength", rule.ValidationType);
|
context.Attributes,
|
||||||
Assert.Equal(1, rule.ValidationParameters.Count);
|
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||||
Assert.Equal(5, rule.ValidationParameters["max"]);
|
kvp => { Assert.Equal("data-val-maxlength", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||||
Assert.Equal("Length must be at most 5", rule.ErrorMessage);
|
kvp => { Assert.Equal("data-val-maxlength-max", kvp.Key); Assert.Equal("5", kvp.Value); });
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void ClientRulesWithMaxLengthAttribute_StringLocalizer_ReturnsLocalizedErrorString()
|
public void MaxLengthAttribute_AddValidation_StringLocalizer_ReturnsLocalizedErrorString()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
|
|
@ -114,20 +119,53 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
var stringLocalizer = new Mock<IStringLocalizer>();
|
var stringLocalizer = new Mock<IStringLocalizer>();
|
||||||
stringLocalizer.Setup(s => s[errorKey, metadata.GetDisplayName(), attribute.Length]).Returns(localizedString);
|
stringLocalizer.Setup(s => s[errorKey, metadata.GetDisplayName(), attribute.Length]).Returns(localizedString);
|
||||||
|
|
||||||
|
var expectedMessage = "Longueur est invalide";
|
||||||
|
|
||||||
var adapter = new MaxLengthAttributeAdapter(attribute, stringLocalizer.Object);
|
var adapter = new MaxLengthAttributeAdapter(attribute, stringLocalizer.Object);
|
||||||
|
|
||||||
var actionContext = new ActionContext();
|
var actionContext = new ActionContext();
|
||||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
adapter.AddValidation(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var rule = Assert.Single(rules);
|
Assert.Collection(
|
||||||
Assert.Equal("maxlength", rule.ValidationType);
|
context.Attributes,
|
||||||
Assert.Equal(1, rule.ValidationParameters.Count);
|
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||||
Assert.Equal(10, rule.ValidationParameters["max"]);
|
kvp => { Assert.Equal("data-val-maxlength", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||||
Assert.Equal("Longueur est invalide", rule.ErrorMessage);
|
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 System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
using Microsoft.AspNetCore.Testing;
|
using Microsoft.AspNetCore.Testing;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
|
@ -15,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void ClientRulesWithMinLengthAttribute_Localize()
|
public void MinLengthAttribute_AddValidation_Localize()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
|
|
@ -34,22 +35,22 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
var adapter = new MinLengthAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
var adapter = new MinLengthAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
||||||
|
|
||||||
var actionContext = new ActionContext();
|
var actionContext = new ActionContext();
|
||||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
adapter.AddValidation(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var rule = Assert.Single(rules);
|
Assert.Collection(
|
||||||
Assert.Equal("minlength", rule.ValidationType);
|
context.Attributes,
|
||||||
Assert.Equal(1, rule.ValidationParameters.Count);
|
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||||
Assert.Equal(6, rule.ValidationParameters["min"]);
|
kvp => { Assert.Equal("data-val-minlength", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||||
Assert.Equal(attribute.FormatErrorMessage("Length"), rule.ErrorMessage);
|
kvp => { Assert.Equal("data-val-minlength-min", kvp.Key); Assert.Equal("6", kvp.Value); });
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void ClientRulesWithMinLengthAttribute()
|
public void MinLengthAttribute_AddValidation_Attribute()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
|
|
@ -58,45 +59,78 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
var attribute = new MinLengthAttribute(6);
|
var attribute = new MinLengthAttribute(6);
|
||||||
var adapter = new MinLengthAttributeAdapter(attribute, stringLocalizer: null);
|
var adapter = new MinLengthAttributeAdapter(attribute, stringLocalizer: null);
|
||||||
|
|
||||||
|
var expectedMessage = attribute.FormatErrorMessage("Length");
|
||||||
|
|
||||||
var actionContext = new ActionContext();
|
var actionContext = new ActionContext();
|
||||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
adapter.AddValidation(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var rule = Assert.Single(rules);
|
Assert.Collection(
|
||||||
Assert.Equal("minlength", rule.ValidationType);
|
context.Attributes,
|
||||||
Assert.Equal(1, rule.ValidationParameters.Count);
|
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||||
Assert.Equal(6, rule.ValidationParameters["min"]);
|
kvp => { Assert.Equal("data-val-minlength", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||||
Assert.Equal(attribute.FormatErrorMessage("Length"), rule.ErrorMessage);
|
kvp => { Assert.Equal("data-val-minlength-min", kvp.Key); Assert.Equal("6", kvp.Value); });
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void ClientRulesWithMinLengthAttributeAndCustomMessage()
|
public void MinLengthAttribute_AddValidation_AttributeAndCustomMessage()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var propertyName = "Length";
|
var propertyName = "Length";
|
||||||
var message = "Array must have at least {1} items.";
|
|
||||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var metadata = provider.GetMetadataForProperty(typeof(string), propertyName);
|
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 adapter = new MinLengthAttributeAdapter(attribute, stringLocalizer: null);
|
||||||
|
|
||||||
var actionContext = new ActionContext();
|
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
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
adapter.AddValidation(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var rule = Assert.Single(rules);
|
Assert.Collection(
|
||||||
Assert.Equal("minlength", rule.ValidationType);
|
context.Attributes,
|
||||||
Assert.Equal(1, rule.ValidationParameters.Count);
|
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("original", kvp.Value); },
|
||||||
Assert.Equal(2, rule.ValidationParameters["min"]);
|
kvp => { Assert.Equal("data-val-minlength", kvp.Key); Assert.Equal("original", kvp.Value); },
|
||||||
Assert.Equal("Array must have at least 2 items.", rule.ErrorMessage);
|
kvp => { Assert.Equal("data-val-minlength-min", kvp.Key); Assert.Equal("original", kvp.Value); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
using Microsoft.AspNetCore.Testing;
|
using Microsoft.AspNetCore.Testing;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -13,7 +14,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void ClientRulesWithCorrectValidationTypeAndErrorMessage()
|
public void AddValidation_CorrectValidationTypeAndErrorMessage()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
|
|
@ -22,17 +23,44 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
var adapter = new NumericClientModelValidator();
|
var adapter = new NumericClientModelValidator();
|
||||||
|
|
||||||
var actionContext = new ActionContext();
|
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.";
|
var expectedMessage = "The field DisplayId must be a number.";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
adapter.AddValidation(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var rule = Assert.Single(rules);
|
Assert.Collection(
|
||||||
Assert.Equal("number", rule.ValidationType);
|
context.Attributes,
|
||||||
Assert.Equal(expectedMessage, rule.ErrorMessage);
|
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
|
private class TypeWithNumericProperty
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNetCore.Mvc.DataAnnotations.Internal;
|
using Microsoft.AspNetCore.Mvc.DataAnnotations.Internal;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
using Microsoft.AspNetCore.Testing;
|
using Microsoft.AspNetCore.Testing;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
|
@ -14,7 +15,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void GetClientValidationRules_ReturnsValidationParameters_WithoutLocalization()
|
public void AddValidation_WithoutLocalization()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
|
|
@ -28,23 +29,23 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
||||||
var adapter = new RangeAttributeAdapter(attribute, stringLocalizer: null);
|
var adapter = new RangeAttributeAdapter(attribute, stringLocalizer: null);
|
||||||
|
|
||||||
var actionContext = new ActionContext();
|
var actionContext = new ActionContext();
|
||||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
adapter.AddValidation(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var rule = Assert.Single(rules);
|
Assert.Collection(
|
||||||
Assert.Equal("range", rule.ValidationType);
|
context.Attributes,
|
||||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||||
Assert.Equal(0m, rule.ValidationParameters["min"]);
|
kvp => { Assert.Equal("data-val-range", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||||
Assert.Equal(100m, rule.ValidationParameters["max"]);
|
kvp => { Assert.Equal("data-val-range-max", kvp.Key); Assert.Equal("100", kvp.Value); },
|
||||||
Assert.Equal(expectedMessage, rule.ErrorMessage);
|
kvp => { Assert.Equal("data-val-range-min", kvp.Key); Assert.Equal("0", kvp.Value); });
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void GetClientValidationRules_ReturnsValidationParameters()
|
public void AddValidation_WithLocalization()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
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 expectedMessage = "The field Length must be between 0 and 100.";
|
||||||
|
|
||||||
var stringLocalizer = new Mock<IStringLocalizer>();
|
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));
|
.Returns(new LocalizedString(attribute.ErrorMessage, expectedMessage));
|
||||||
|
|
||||||
var adapter = new RangeAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
var adapter = new RangeAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
||||||
|
|
||||||
var actionContext = new ActionContext();
|
var actionContext = new ActionContext();
|
||||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
adapter.AddValidation(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var rule = Assert.Single(rules);
|
Assert.Collection(
|
||||||
Assert.Equal("range", rule.ValidationType);
|
context.Attributes,
|
||||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||||
Assert.Equal(0m, rule.ValidationParameters["min"]);
|
kvp => { Assert.Equal("data-val-range", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||||
Assert.Equal(100m, rule.ValidationParameters["max"]);
|
kvp => { Assert.Equal("data-val-range-max", kvp.Key); Assert.Equal("100", kvp.Value); },
|
||||||
Assert.Equal(expectedMessage, rule.ErrorMessage);
|
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 System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
using Microsoft.AspNetCore.Testing;
|
using Microsoft.AspNetCore.Testing;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
|
@ -15,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void GetClientValidationRules_ReturnsValidationParameters_Localize()
|
public void AddValidation_AddsValidation_Localize()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
|
|
@ -35,24 +36,24 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
var adapter = new RequiredAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
var adapter = new RequiredAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
||||||
|
|
||||||
var actionContext = new ActionContext();
|
var actionContext = new ActionContext();
|
||||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
adapter.AddValidation(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var rule = Assert.Single(rules);
|
Assert.Collection(
|
||||||
Assert.Equal("required", rule.ValidationType);
|
context.Attributes,
|
||||||
Assert.Empty(rule.ValidationParameters);
|
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||||
Assert.Equal(expectedMessage, rule.ErrorMessage);
|
kvp => { Assert.Equal("data-val-required", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); });
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void GetClientValidationRules_ReturnsValidationParameters()
|
public void AddValidation_AddsValidation()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var expected = ValidationAttributeUtil.GetRequiredErrorMessage("Length");
|
var expectedMessage = ValidationAttributeUtil.GetRequiredErrorMessage("Length");
|
||||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var metadata = provider.GetMetadataForProperty(typeof(string), "Length");
|
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 adapter = new RequiredAttributeAdapter(attribute, stringLocalizer: null);
|
||||||
|
|
||||||
var actionContext = new ActionContext();
|
var actionContext = new ActionContext();
|
||||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
adapter.AddValidation(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var rule = Assert.Single(rules);
|
Assert.Collection(
|
||||||
Assert.Equal("required", rule.ValidationType);
|
context.Attributes,
|
||||||
Assert.Empty(rule.ValidationParameters);
|
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||||
Assert.Equal(expected, rule.ErrorMessage);
|
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 System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
using Microsoft.AspNetCore.Testing;
|
using Microsoft.AspNetCore.Testing;
|
||||||
using Microsoft.Extensions.Localization;
|
using Microsoft.Extensions.Localization;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
|
@ -15,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void GetClientValidationRules_WithMaxLength_ReturnsValidationParameters_Localize()
|
public void AddValidation_WithMaxLength_AddsAttributes_Localize()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
|
|
@ -35,22 +36,23 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
var adapter = new StringLengthAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
var adapter = new StringLengthAttributeAdapter(attribute, stringLocalizer: stringLocalizer.Object);
|
||||||
|
|
||||||
var actionContext = new ActionContext();
|
var actionContext = new ActionContext();
|
||||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
adapter.AddValidation(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var rule = Assert.Single(rules);
|
Assert.Collection(
|
||||||
Assert.Equal("length", rule.ValidationType);
|
context.Attributes,
|
||||||
Assert.Equal(1, rule.ValidationParameters.Count);
|
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||||
Assert.Equal(8, rule.ValidationParameters["max"]);
|
kvp => { Assert.Equal("data-val-length", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||||
Assert.Equal(expectedMessage, rule.ErrorMessage);
|
kvp => { Assert.Equal("data-val-length-max", kvp.Key); Assert.Equal("8", kvp.Value); });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void GetClientValidationRules_WithMaxLength_ReturnsValidationParameters()
|
public void AddValidation_WithMaxLength_AddsAttributes()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
|
|
@ -59,23 +61,25 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
var attribute = new StringLengthAttribute(8);
|
var attribute = new StringLengthAttribute(8);
|
||||||
var adapter = new StringLengthAttributeAdapter(attribute, stringLocalizer: null);
|
var adapter = new StringLengthAttributeAdapter(attribute, stringLocalizer: null);
|
||||||
|
|
||||||
|
var expectedMessage = attribute.FormatErrorMessage("Length");
|
||||||
|
|
||||||
var actionContext = new ActionContext();
|
var actionContext = new ActionContext();
|
||||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
adapter.AddValidation(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var rule = Assert.Single(rules);
|
Assert.Collection(
|
||||||
Assert.Equal("length", rule.ValidationType);
|
context.Attributes,
|
||||||
Assert.Equal(1, rule.ValidationParameters.Count);
|
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||||
Assert.Equal(8, rule.ValidationParameters["max"]);
|
kvp => { Assert.Equal("data-val-length", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||||
Assert.Equal(attribute.FormatErrorMessage("Length"), rule.ErrorMessage);
|
kvp => { Assert.Equal("data-val-length-max", kvp.Key); Assert.Equal("8", kvp.Value); });
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void GetClientValidationRules_WithMinAndMaxLength_ReturnsValidationParameters()
|
public void AddValidation_WithMinAndMaxLength_AddsAttributes()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
var provider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
|
|
@ -84,19 +88,80 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
var attribute = new StringLengthAttribute(10) { MinimumLength = 3 };
|
var attribute = new StringLengthAttribute(10) { MinimumLength = 3 };
|
||||||
var adapter = new StringLengthAttributeAdapter(attribute, stringLocalizer: null);
|
var adapter = new StringLengthAttributeAdapter(attribute, stringLocalizer: null);
|
||||||
|
|
||||||
|
var expectedMessage = attribute.FormatErrorMessage("Length");
|
||||||
|
|
||||||
var actionContext = new ActionContext();
|
var actionContext = new ActionContext();
|
||||||
var context = new ClientModelValidationContext(actionContext, metadata, provider);
|
var context = new ClientModelValidationContext(actionContext, metadata, provider, new AttributeDictionary());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
adapter.AddValidation(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var rule = Assert.Single(rules);
|
Assert.Collection(
|
||||||
Assert.Equal("length", rule.ValidationType);
|
context.Attributes,
|
||||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||||
Assert.Equal(3, rule.ValidationParameters["min"]);
|
kvp => { Assert.Equal("data-val-length", kvp.Key); Assert.Equal(expectedMessage, kvp.Value); },
|
||||||
Assert.Equal(10, rule.ValidationParameters["max"]);
|
kvp => { Assert.Equal("data-val-length-max", kvp.Key); Assert.Equal("10", kvp.Value); },
|
||||||
Assert.Equal(attribute.FormatErrorMessage("Length"), rule.ErrorMessage);
|
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)
|
: base(attribute, stringLocalizer)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
public override IEnumerable<ModelClientValidationRule> GetClientValidationRules(ClientModelValidationContext context)
|
public override void AddValidation(ClientModelValidationContext context)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,10 +64,10 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return new TheoryData<ValidationAttribute, string> {
|
return new TheoryData<ValidationAttribute, string> {
|
||||||
{ new UrlAttribute(), "url" },
|
{ new UrlAttribute(), "data-val-url" },
|
||||||
{ new CreditCardAttribute(), "creditcard" },
|
{ new CreditCardAttribute(), "data-val-creditcard" },
|
||||||
{ new EmailAddressAttribute(), "email" },
|
{ new EmailAddressAttribute(), "data-val-email" },
|
||||||
{ new PhoneAttribute(), "phone" }
|
{ new PhoneAttribute(), "data-val-phone" }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -95,12 +95,13 @@ namespace Microsoft.AspNetCore.Mvc.TagHelpers
|
||||||
return tagBuilder;
|
return tagBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IDictionary<string, object> GetValidationAttributes(
|
protected override void AddValidationAttributes(
|
||||||
ViewContext viewContext,
|
ViewContext viewContext,
|
||||||
|
TagBuilder tagBuilder,
|
||||||
ModelExplorer modelExplorer,
|
ModelExplorer modelExplorer,
|
||||||
string name)
|
string expression)
|
||||||
{
|
{
|
||||||
return ValidationAttributes;
|
tagBuilder.MergeAttributes(ValidationAttributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IOptions<MvcViewOptions> GetOptions()
|
private static IOptions<MvcViewOptions> GetOptions()
|
||||||
|
|
|
||||||
|
|
@ -1085,13 +1085,6 @@ Environment.NewLine;
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
|
||||||
ModelExplorer modelExplorer,
|
|
||||||
string name)
|
|
||||||
{
|
|
||||||
return Enumerable.Empty<ModelClientValidationRule>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<SelectListItem> GetEnumSelectList<TEnum>() where TEnum : struct
|
public IEnumerable<SelectListItem> GetEnumSelectList<TEnum>() where TEnum : struct
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.AspNetCore.Mvc.Routing;
|
using Microsoft.AspNetCore.Mvc.Routing;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
@ -184,19 +185,19 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetClientValidationRules_WithBadRouteName_Throws()
|
public void AddValidation_WithBadRouteName_Throws()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var attribute = new RemoteAttribute("nonexistentRoute");
|
var attribute = new RemoteAttribute("nonexistentRoute");
|
||||||
var context = GetValidationContextWithArea(currentArea: null);
|
var context = GetValidationContextWithArea(currentArea: null);
|
||||||
|
|
||||||
// Act & Assert
|
// 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);
|
Assert.Equal("No URL for remote validation could be found.", exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetClientValidationRules_WithRoute_CallsUrlHelperWithExpectedValues()
|
public void AddValidation_WithRoute_CallsUrlHelperWithExpectedValues()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var routeName = "RouteName";
|
var routeName = "RouteName";
|
||||||
|
|
@ -205,21 +206,23 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
var urlHelper = new MockUrlHelper(url, routeName);
|
var urlHelper = new MockUrlHelper(url, routeName);
|
||||||
var context = GetValidationContext(urlHelper);
|
var context = GetValidationContext(urlHelper);
|
||||||
|
|
||||||
// Act & Assert
|
// Act
|
||||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
attribute.AddValidation(context);
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
|
||||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
|
||||||
|
|
||||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
// Assert
|
||||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
Assert.Collection(
|
||||||
Assert.Equal(url, rule.ValidationParameters["url"]);
|
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);
|
var routeDictionary = Assert.IsType<RouteValueDictionary>(urlHelper.RouteValues);
|
||||||
Assert.Empty(routeDictionary);
|
Assert.Empty(routeDictionary);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetClientValidationRules_WithActionController_CallsUrlHelperWithExpectedValues()
|
public void AddValidation_WithActionController_CallsUrlHelperWithExpectedValues()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var attribute = new RemoteAttribute("Action", "Controller");
|
var attribute = new RemoteAttribute("Action", "Controller");
|
||||||
|
|
@ -227,14 +230,16 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
var urlHelper = new MockUrlHelper(url, routeName: null);
|
var urlHelper = new MockUrlHelper(url, routeName: null);
|
||||||
var context = GetValidationContext(urlHelper);
|
var context = GetValidationContext(urlHelper);
|
||||||
|
|
||||||
// Act & Assert
|
// Act
|
||||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
attribute.AddValidation(context);
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
|
||||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
|
||||||
|
|
||||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
// Assert
|
||||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
Assert.Collection(
|
||||||
Assert.Equal(url, rule.ValidationParameters["url"]);
|
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);
|
var routeDictionary = Assert.IsType<RouteValueDictionary>(urlHelper.RouteValues);
|
||||||
Assert.Equal(2, routeDictionary.Count);
|
Assert.Equal(2, routeDictionary.Count);
|
||||||
|
|
@ -243,7 +248,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetClientValidationRules_WithActionController_PropertiesSet_CallsUrlHelperWithExpectedValues()
|
public void AddValidation_WithActionController_PropertiesSet_CallsUrlHelperWithExpectedValues()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var attribute = new RemoteAttribute("Action", "Controller")
|
var attribute = new RemoteAttribute("Action", "Controller")
|
||||||
|
|
@ -255,15 +260,21 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
var urlHelper = new MockUrlHelper(url, routeName: null);
|
var urlHelper = new MockUrlHelper(url, routeName: null);
|
||||||
var context = GetValidationContext(urlHelper);
|
var context = GetValidationContext(urlHelper);
|
||||||
|
|
||||||
// Act & Assert
|
// Act
|
||||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
attribute.AddValidation(context);
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
|
||||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
|
||||||
|
|
||||||
Assert.Equal(3, rule.ValidationParameters.Count);
|
// Assert
|
||||||
Assert.Equal("*.Length,*.Password,*.ConfirmPassword", rule.ValidationParameters["additionalfields"]);
|
Assert.Collection(
|
||||||
Assert.Equal("POST", rule.ValidationParameters["type"]);
|
context.Attributes,
|
||||||
Assert.Equal(url, rule.ValidationParameters["url"]);
|
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);
|
var routeDictionary = Assert.IsType<RouteValueDictionary>(urlHelper.RouteValues);
|
||||||
Assert.Equal(2, routeDictionary.Count);
|
Assert.Equal(2, routeDictionary.Count);
|
||||||
|
|
@ -272,7 +283,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetClientValidationRules_WithActionControllerArea_CallsUrlHelperWithExpectedValues()
|
public void AddValidation_WithActionControllerArea_CallsUrlHelperWithExpectedValues()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var attribute = new RemoteAttribute("Action", "Controller", "Test")
|
var attribute = new RemoteAttribute("Action", "Controller", "Test")
|
||||||
|
|
@ -283,15 +294,21 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
var urlHelper = new MockUrlHelper(url, routeName: null);
|
var urlHelper = new MockUrlHelper(url, routeName: null);
|
||||||
var context = GetValidationContext(urlHelper);
|
var context = GetValidationContext(urlHelper);
|
||||||
|
|
||||||
// Act & Assert
|
// Act
|
||||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
attribute.AddValidation(context);
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
|
||||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
|
||||||
|
|
||||||
Assert.Equal(3, rule.ValidationParameters.Count);
|
// Assert
|
||||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
Assert.Collection(
|
||||||
Assert.Equal("POST", rule.ValidationParameters["type"]);
|
context.Attributes,
|
||||||
Assert.Equal(url, rule.ValidationParameters["url"]);
|
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);
|
var routeDictionary = Assert.IsType<RouteValueDictionary>(urlHelper.RouteValues);
|
||||||
Assert.Equal(3, routeDictionary.Count);
|
Assert.Equal(3, routeDictionary.Count);
|
||||||
|
|
@ -302,138 +319,203 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
|
|
||||||
// Root area is current in this case.
|
// Root area is current in this case.
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetClientValidationRules_WithActionController_FindsControllerInCurrentArea()
|
public void AddValidation_WithActionController_FindsControllerInCurrentArea()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var attribute = new RemoteAttribute("Action", "Controller");
|
var attribute = new RemoteAttribute("Action", "Controller");
|
||||||
var context = GetValidationContextWithArea(currentArea: null);
|
var context = GetValidationContextWithArea(currentArea: null);
|
||||||
|
|
||||||
// Act & Assert
|
// Act
|
||||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
attribute.AddValidation(context);
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
|
||||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
|
||||||
|
|
||||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
// Assert
|
||||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
Assert.Collection(
|
||||||
Assert.Equal("/UrlEncode[[Controller]]/UrlEncode[[Action]]", rule.ValidationParameters["url"]);
|
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.
|
// Test area is current in this case.
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetClientValidationRules_WithActionControllerInArea_FindsControllerInCurrentArea()
|
public void AddValidation_WithActionControllerInArea_FindsControllerInCurrentArea()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var attribute = new RemoteAttribute("Action", "Controller");
|
var attribute = new RemoteAttribute("Action", "Controller");
|
||||||
var context = GetValidationContextWithArea(currentArea: "Test");
|
var context = GetValidationContextWithArea(currentArea: "Test");
|
||||||
|
|
||||||
// Act & Assert
|
// Act
|
||||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
attribute.AddValidation(context);
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
|
||||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
|
||||||
|
|
||||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
// Assert
|
||||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
Assert.Collection(
|
||||||
Assert.Equal(
|
context.Attributes,
|
||||||
"/UrlEncode[[Test]]/UrlEncode[[Controller]]/UrlEncode[[Action]]",
|
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||||
rule.ValidationParameters["url"]);
|
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.
|
// Explicit reference to the (current) root area.
|
||||||
[Theory]
|
[Theory]
|
||||||
[MemberData(nameof(NullOrEmptyNames))]
|
[MemberData(nameof(NullOrEmptyNames))]
|
||||||
public void GetClientValidationRules_WithActionControllerArea_FindsControllerInRootArea(string areaName)
|
public void AddValidation_WithActionControllerArea_FindsControllerInRootArea(string areaName)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var attribute = new RemoteAttribute("Action", "Controller", areaName);
|
var attribute = new RemoteAttribute("Action", "Controller", areaName);
|
||||||
var context = GetValidationContextWithArea(currentArea: null);
|
var context = GetValidationContextWithArea(currentArea: null);
|
||||||
|
|
||||||
// Act & Assert
|
// Act
|
||||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
attribute.AddValidation(context);
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
|
||||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
|
||||||
|
|
||||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
// Assert
|
||||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
Assert.Collection(
|
||||||
Assert.Equal("/UrlEncode[[Controller]]/UrlEncode[[Action]]", rule.ValidationParameters["url"]);
|
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.
|
// Test area is current in this case.
|
||||||
[Theory]
|
[Theory]
|
||||||
[MemberData(nameof(NullOrEmptyNames))]
|
[MemberData(nameof(NullOrEmptyNames))]
|
||||||
public void GetClientValidationRules_WithActionControllerAreaInArea_FindsControllerInRootArea(string areaName)
|
public void AddValidation_WithActionControllerAreaInArea_FindsControllerInRootArea(string areaName)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var attribute = new RemoteAttribute("Action", "Controller", areaName);
|
var attribute = new RemoteAttribute("Action", "Controller", areaName);
|
||||||
var context = GetValidationContextWithArea(currentArea: "Test");
|
var context = GetValidationContextWithArea(currentArea: "Test");
|
||||||
|
|
||||||
// Act & Assert
|
// Act
|
||||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
attribute.AddValidation(context);
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
|
||||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
|
||||||
|
|
||||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
// Assert
|
||||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
Assert.Collection(
|
||||||
Assert.Equal("/UrlEncode[[Controller]]/UrlEncode[[Action]]", rule.ValidationParameters["url"]);
|
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.
|
// Root area is current in this case.
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetClientValidationRules_WithActionControllerArea_FindsControllerInNamedArea()
|
public void AddValidation_WithActionControllerArea_FindsControllerInNamedArea()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var attribute = new RemoteAttribute("Action", "Controller", "Test");
|
var attribute = new RemoteAttribute("Action", "Controller", "Test");
|
||||||
var context = GetValidationContextWithArea(currentArea: null);
|
var context = GetValidationContextWithArea(currentArea: null);
|
||||||
|
|
||||||
// Act & Assert
|
// Act
|
||||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
attribute.AddValidation(context);
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
|
||||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
|
||||||
|
|
||||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
// Assert
|
||||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
Assert.Collection(
|
||||||
Assert.Equal(
|
context.Attributes,
|
||||||
"/UrlEncode[[Test]]/UrlEncode[[Controller]]/UrlEncode[[Action]]",
|
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||||
rule.ValidationParameters["url"]);
|
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.
|
// Explicit reference to the current (Test) area.
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetClientValidationRules_WithActionControllerAreaInArea_FindsControllerInNamedArea()
|
public void AddValidation_WithActionControllerAreaInArea_FindsControllerInNamedArea()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var attribute = new RemoteAttribute("Action", "Controller", "Test");
|
var attribute = new RemoteAttribute("Action", "Controller", "Test");
|
||||||
var context = GetValidationContextWithArea(currentArea: "Test");
|
var context = GetValidationContextWithArea(currentArea: "Test");
|
||||||
|
|
||||||
// Act & Assert
|
// Act
|
||||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
attribute.AddValidation(context);
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
|
||||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
|
||||||
|
|
||||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
// Assert
|
||||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
Assert.Collection(
|
||||||
Assert.Equal(
|
context.Attributes,
|
||||||
"/UrlEncode[[Test]]/UrlEncode[[Controller]]/UrlEncode[[Action]]",
|
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||||
rule.ValidationParameters["url"]);
|
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.
|
// Test area is current in this case.
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetClientValidationRules_WithActionControllerAreaInArea_FindsControllerInDifferentArea()
|
public void AddValidation_WithActionControllerAreaInArea_FindsControllerInDifferentArea()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var attribute = new RemoteAttribute("Action", "Controller", "AnotherArea");
|
var attribute = new RemoteAttribute("Action", "Controller", "AnotherArea");
|
||||||
var context = GetValidationContextWithArea(currentArea: "Test");
|
var context = GetValidationContextWithArea(currentArea: "Test");
|
||||||
|
|
||||||
// Act & Assert
|
// Act
|
||||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
attribute.AddValidation(context);
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
|
||||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
|
||||||
|
|
||||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
// Assert
|
||||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
Assert.Collection(
|
||||||
Assert.Equal(
|
context.Attributes,
|
||||||
"/UrlEncode[[AnotherArea]]/UrlEncode[[Controller]]/UrlEncode[[Action]]",
|
kvp => { Assert.Equal("data-val", kvp.Key); Assert.Equal("true", kvp.Value); },
|
||||||
rule.ValidationParameters["url"]);
|
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)
|
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)
|
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)
|
private static IRouter GetRouteCollectionWithArea(IServiceProvider serviceProvider)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue