Get `[Remote]` and supporting classes building
- #439 (2 of 3) - correct namespaces - correct `Resources` class and member names; add new resources - add `RequestServices` property to `ClientModelValidationContext` - adjust to modern `IUrlHelper` API - add `IClientModelValidator` support in `DataAnnotationsModelValidator` - move previously-unused `StringSplit()` to `RemoteAttribute` and rename - rewrite `RemoteAttributeTest` - improve test method names Reduce number of `[Remote]` constructor overloads - remove `AreaReference` enum and related `[Remote]` constructor overload - use `null` or empty `string` as explicit reference to the root area - generally reduce parameter validation; match `UrlHelper` Cleanup - correct Engineering Guidelines violations - especially: add doc comments - correct spelling error in `_additonalFieldsSplit` nits: - minimize `null` checks in `AdditionalFields` - make `GetClientValidationRules` `virtual`; some subclasses use `new` today - add tests of `DataAnnotationsModelValidator.GetClientValidationRules()` - remove `builder.ToString()` calls since it appears https://roslyn.codeplex.com/workitem/246 has been resolved or Moq has worked around that issue.
This commit is contained in:
parent
1af1583302
commit
80b004678d
|
|
@ -1,20 +0,0 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
namespace System.Web.Mvc
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Controls interpretation of a controller name when constructing a <see cref="RemoteAttribute"/>.
|
|
||||||
/// </summary>
|
|
||||||
public enum AreaReference
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Find the controller in the current area.
|
|
||||||
/// </summary>
|
|
||||||
UseCurrent = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Find the controller in the root area.
|
|
||||||
/// </summary>
|
|
||||||
UseRoot = 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,26 +1,47 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
|
||||||
namespace System.Web.Mvc
|
namespace Microsoft.AspNet.Mvc.Internal
|
||||||
{
|
{
|
||||||
[TypeForwardedFrom("System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")]
|
/// <summary>
|
||||||
|
/// <see cref="ModelClientValidationRule"/> containing information for HTML attribute generation in fields a
|
||||||
|
/// <see cref="RemoteAttribute"/> targets.
|
||||||
|
/// </summary>
|
||||||
public class ModelClientValidationRemoteRule : ModelClientValidationRule
|
public class ModelClientValidationRemoteRule : ModelClientValidationRule
|
||||||
{
|
{
|
||||||
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", Justification = "The value is a not a regular URL since it may contain ~/ ASP.NET-specific characters")]
|
private const string RemoteValidationType = "remote";
|
||||||
public ModelClientValidationRemoteRule(string errorMessage, string url, string httpMethod, string additionalFields)
|
private const string AdditionalFieldsValidationParameter = "additionalfields";
|
||||||
{
|
private const string TypeValidationParameter = "type";
|
||||||
ErrorMessage = errorMessage;
|
private const string UrlValidationParameter = "url";
|
||||||
ValidationType = "remote";
|
|
||||||
ValidationParameters["url"] = url;
|
|
||||||
|
|
||||||
if (!String.IsNullOrEmpty(httpMethod))
|
/// <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["type"] = httpMethod;
|
ValidationParameters[TypeValidationParameter] = httpMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
ValidationParameters["additionalfields"] = additionalFields;
|
ValidationParameters[AdditionalFieldsValidationParameter] = additionalFields;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1706,6 +1706,38 @@ namespace Microsoft.AspNet.Mvc.Core
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("ApiExplorer_UnsupportedAction"), p0);
|
return string.Format(CultureInfo.CurrentCulture, GetString("ApiExplorer_UnsupportedAction"), p0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// No URL for remote validation could be found.
|
||||||
|
/// </summary>
|
||||||
|
internal static string RemoteAttribute_NoUrlFound
|
||||||
|
{
|
||||||
|
get { return GetString("RemoteAttribute_NoUrlFound"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// No URL for remote validation could be found.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatRemoteAttribute_NoUrlFound()
|
||||||
|
{
|
||||||
|
return GetString("RemoteAttribute_NoUrlFound");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// '{0}' is invalid.
|
||||||
|
/// </summary>
|
||||||
|
internal static string RemoteAttribute_RemoteValidationFailed
|
||||||
|
{
|
||||||
|
get { return GetString("RemoteAttribute_RemoteValidationFailed"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// '{0}' is invalid.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatRemoteAttribute_RemoteValidationFailed(object p0)
|
||||||
|
{
|
||||||
|
return string.Format(CultureInfo.CurrentCulture, GetString("RemoteAttribute_RemoteValidationFailed"), p0);
|
||||||
|
}
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -1,158 +1,251 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
// Copyright (c) Microsoft Open Technologies, Inc. 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.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Web.Mvc.Properties;
|
using System.Linq;
|
||||||
using System.Web.Routing;
|
using Microsoft.AspNet.Mvc.Core;
|
||||||
|
using Microsoft.AspNet.Mvc.Internal;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNet.Routing;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
|
||||||
namespace System.Web.Mvc
|
namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
/// <summary>
|
||||||
[SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments", Justification = "The constructor parameters are used to feed RouteData, which is public.")]
|
/// A <see cref="ValidationAttribute"/> which configures Unobtrusive validation to send an Ajax request to the
|
||||||
[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "This attribute is designed to be a base class for other attributes.")]
|
/// web site. The invoked action should return JSON indicating whether the value is valid.
|
||||||
public class RemoteAttribute : ValidationAttribute, IClientValidatable
|
/// </summary>
|
||||||
|
/// <remarks>Does no server-side validation of the final form submission.</remarks>
|
||||||
|
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
|
||||||
|
public class RemoteAttribute : ValidationAttribute, IClientModelValidator
|
||||||
{
|
{
|
||||||
private string _additionalFields;
|
private string _additionalFields = string.Empty;
|
||||||
private string[] _additonalFieldsSplit = new string[0];
|
private string[] _additionalFieldsSplit = new string[0];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="RemoteAttribute"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Intended for subclasses that support URL generation with no route, action, or controller names.
|
||||||
|
/// </remarks>
|
||||||
protected RemoteAttribute()
|
protected RemoteAttribute()
|
||||||
: base(MvcResources.RemoteAttribute_RemoteValidationFailed)
|
: base(Resources.RemoteAttribute_RemoteValidationFailed)
|
||||||
{
|
{
|
||||||
RouteData = new RouteValueDictionary();
|
RouteData = new RouteValueDictionary();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="RemoteAttribute"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="routeName">
|
||||||
|
/// The route name used when generating the URL where client should send a validation request.
|
||||||
|
/// </param>
|
||||||
|
/// <remarks>
|
||||||
|
/// Finds the <paramref name="routeName"/> in any area of the application.
|
||||||
|
/// </remarks>
|
||||||
public RemoteAttribute(string routeName)
|
public RemoteAttribute(string routeName)
|
||||||
: this()
|
: this()
|
||||||
{
|
{
|
||||||
if (String.IsNullOrWhiteSpace(routeName))
|
|
||||||
{
|
|
||||||
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "routeName");
|
|
||||||
}
|
|
||||||
|
|
||||||
RouteName = routeName;
|
RouteName = routeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="RemoteAttribute"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="action">
|
||||||
|
/// The action name used when generating the URL where client should send a validation request.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="controller">
|
||||||
|
/// The controller name used when generating the URL where client should send a validation request.
|
||||||
|
/// </param>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// If either <paramref name="action"/> or <paramref name="controller"/> is <c>null</c>, uses the corresponding
|
||||||
|
/// ambient value.
|
||||||
|
/// </para>
|
||||||
|
/// <para>Finds the <paramref name="controller"/> in the current area.</para>
|
||||||
|
/// </remarks>
|
||||||
public RemoteAttribute(string action, string controller)
|
public RemoteAttribute(string action, string controller)
|
||||||
:
|
|
||||||
this(action, controller, null /* areaName */)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public RemoteAttribute(string action, string controller, string areaName)
|
|
||||||
: this()
|
: this()
|
||||||
{
|
{
|
||||||
if (String.IsNullOrWhiteSpace(action))
|
if (action != null)
|
||||||
{
|
{
|
||||||
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "action");
|
RouteData["action"] = action;
|
||||||
}
|
|
||||||
if (String.IsNullOrWhiteSpace(controller))
|
|
||||||
{
|
|
||||||
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controller");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RouteData["controller"] = controller;
|
if (controller != null)
|
||||||
RouteData["action"] = action;
|
|
||||||
|
|
||||||
if (!String.IsNullOrWhiteSpace(areaName))
|
|
||||||
{
|
{
|
||||||
RouteData["area"] = areaName;
|
RouteData["controller"] = controller;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="RemoteAttribute"/> class.
|
/// Initializes a new instance of the <see cref="RemoteAttribute"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="action">The route name.</param>
|
/// <param name="action">
|
||||||
/// <param name="controller">The name of the controller.</param>
|
/// The action name used when generating the URL where client should send a validation request.
|
||||||
/// <param name="areaReference">
|
|
||||||
/// Find the controller in the root if <see cref="AreaReference.UseRoot"/>. Otherwise look in the current area.
|
|
||||||
/// </param>
|
/// </param>
|
||||||
public RemoteAttribute(string action, string controller, AreaReference areaReference)
|
/// <param name="controller">
|
||||||
|
/// The controller name used when generating the URL where client should send a validation request.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="areaName">The name of the area containing the <paramref name="controller"/>.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// If either <paramref name="action"/> or <paramref name="controller"/> is <c>null</c>, uses the corresponding
|
||||||
|
/// ambient value.
|
||||||
|
/// </para>
|
||||||
|
/// If <paramref name="areaName"/> is <c>null</c>, finds the <paramref name="controller"/> in the root area.
|
||||||
|
/// Use the <see cref="RemoteAttribute(string, string)"/> overload find the <paramref name="controller"/> in
|
||||||
|
/// the current area. Or explicitly pass the current area's name as the <paramref name="areaName"/> argument to
|
||||||
|
/// this overload.
|
||||||
|
/// </remarks>
|
||||||
|
public RemoteAttribute(string action, string controller, string areaName)
|
||||||
: this(action, controller)
|
: this(action, controller)
|
||||||
{
|
{
|
||||||
if (areaReference == AreaReference.UseRoot)
|
RouteData["area"] = areaName;
|
||||||
{
|
|
||||||
RouteData["area"] = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the HTTP method (<c>"Get"</c> or <c>"Post"</c>) client should use when sending a validation
|
||||||
|
/// request.
|
||||||
|
/// </summary>
|
||||||
public string HttpMethod { get; set; }
|
public string HttpMethod { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the comma-separated names of fields the client should include in a validation request.
|
||||||
|
/// </summary>
|
||||||
public string AdditionalFields
|
public string AdditionalFields
|
||||||
{
|
{
|
||||||
get { return _additionalFields ?? String.Empty; }
|
get { return _additionalFields; }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_additionalFields = value;
|
_additionalFields = value ?? string.Empty;
|
||||||
_additonalFieldsSplit = AuthorizeAttribute.SplitString(value);
|
_additionalFieldsSplit = SplitAndTrimPropertyNames(value)
|
||||||
|
.Select(field => FormatPropertyForClientValidation(field))
|
||||||
|
.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected RouteValueDictionary RouteData { get; private set; }
|
/// <summary>
|
||||||
|
/// Gets the <see cref="RouteValueDictionary"/> used when generating the URL where client should send a
|
||||||
|
/// validation request.
|
||||||
|
/// </summary>
|
||||||
|
protected RouteValueDictionary RouteData { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the route name used when generating the URL where client should send a validation request.
|
||||||
|
/// </summary>
|
||||||
protected string RouteName { get; set; }
|
protected string RouteName { get; set; }
|
||||||
|
|
||||||
protected virtual RouteCollection Routes
|
/// <summary>
|
||||||
{
|
/// Formats <paramref name="property"/> and <see cref="AdditionalFields"/> for use in generated HTML.
|
||||||
get { return RouteTable.Routes; }
|
/// </summary>
|
||||||
}
|
/// <param name="property">
|
||||||
|
/// Name of the property associated with this <see cref="RemoteAttribute"/> instance.
|
||||||
|
/// </param>
|
||||||
|
/// <returns>Comma-separated names of fields the client should include in a validation request.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// Excludes any whitespace from <see cref="AdditionalFields"/> in the return value.
|
||||||
|
/// Prefixes each field name in the return value with <c>"*."</c>.
|
||||||
|
/// </remarks>
|
||||||
public string FormatAdditionalFieldsForClientValidation(string property)
|
public string FormatAdditionalFieldsForClientValidation(string property)
|
||||||
{
|
{
|
||||||
if (String.IsNullOrEmpty(property))
|
if (string.IsNullOrEmpty(property))
|
||||||
{
|
{
|
||||||
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "property");
|
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, "property");
|
||||||
}
|
}
|
||||||
|
|
||||||
string delimitedAdditionalFields = FormatPropertyForClientValidation(property);
|
var delimitedAdditionalFields = string.Join(",", _additionalFieldsSplit);
|
||||||
|
if (!string.IsNullOrEmpty(delimitedAdditionalFields))
|
||||||
foreach (string field in _additonalFieldsSplit)
|
|
||||||
{
|
{
|
||||||
delimitedAdditionalFields += "," + FormatPropertyForClientValidation(field);
|
delimitedAdditionalFields = "," + delimitedAdditionalFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
return delimitedAdditionalFields;
|
var formattedString = FormatPropertyForClientValidation(property) + delimitedAdditionalFields;
|
||||||
|
|
||||||
|
return formattedString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Formats <paramref name="property"/> for use in generated HTML.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="property">One field name the client should include in a validation request.</param>
|
||||||
|
/// <returns>Name of a field the client should include in a validation request.</returns>
|
||||||
|
/// <remarks>Returns <paramref name="property"/> with a <c>"*."</c> prefix.</remarks>
|
||||||
public static string FormatPropertyForClientValidation(string property)
|
public static string FormatPropertyForClientValidation(string property)
|
||||||
{
|
{
|
||||||
if (String.IsNullOrEmpty(property))
|
if (string.IsNullOrEmpty(property))
|
||||||
{
|
{
|
||||||
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "property");
|
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, "property");
|
||||||
}
|
}
|
||||||
|
|
||||||
return "*." + property;
|
return "*." + property;
|
||||||
}
|
}
|
||||||
|
|
||||||
[SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "The value is a not a regular URL since it may contain ~/ ASP.NET-specific characters")]
|
/// <summary>
|
||||||
protected virtual string GetUrl(ControllerContext controllerContext)
|
/// Returns the URL where the client should send a validation request.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context">The <see cref="ClientModelValidationContext"/> used to generate the URL.</param>
|
||||||
|
/// <returns>The URL where the client should send a validation request.</returns>
|
||||||
|
protected virtual string GetUrl([NotNull] ClientModelValidationContext context)
|
||||||
{
|
{
|
||||||
var pathData = Routes.GetVirtualPathForArea(controllerContext.RequestContext,
|
var urlHelper = context.RequestServices.GetRequiredService<IUrlHelper>();
|
||||||
RouteName,
|
var url = urlHelper.RouteUrl(RouteName, values: RouteData, protocol: null, host: null, fragment: null);
|
||||||
RouteData);
|
if (url == null)
|
||||||
|
|
||||||
if (pathData == null)
|
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException(MvcResources.RemoteAttribute_NoUrlFound);
|
throw new InvalidOperationException(Resources.RemoteAttribute_NoUrlFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
return pathData.VirtualPath;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override string FormatErrorMessage(string name)
|
public override string FormatErrorMessage(string name)
|
||||||
{
|
{
|
||||||
return String.Format(CultureInfo.CurrentCulture, ErrorMessageString, name);
|
return string.Format(CultureInfo.CurrentCulture, ErrorMessageString, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
/// <remarks>
|
||||||
|
/// Always returns <c>true</c> since this <see cref="ValidationAttribute"/> does no validation itself.
|
||||||
|
/// Related validations occur only when the client sends a validation request.
|
||||||
|
/// </remarks>
|
||||||
public override bool IsValid(object value)
|
public override bool IsValid(object value)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
|
/// <inheritdoc />
|
||||||
|
/// <exception cref="InvalidOperationException">
|
||||||
|
/// Thrown if unable to generate a target URL for a validation request.
|
||||||
|
/// </exception>
|
||||||
|
public virtual IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||||
|
[NotNull] ClientModelValidationContext context)
|
||||||
{
|
{
|
||||||
yield return new ModelClientValidationRemoteRule(FormatErrorMessage(metadata.GetDisplayName()), GetUrl(context), HttpMethod, FormatAdditionalFieldsForClientValidation(metadata.PropertyName));
|
var metadata = context.ModelMetadata;
|
||||||
|
var rule = new ModelClientValidationRemoteRule(
|
||||||
|
FormatErrorMessage(metadata.GetDisplayName()),
|
||||||
|
GetUrl(context),
|
||||||
|
HttpMethod,
|
||||||
|
FormatAdditionalFieldsForClientValidation(metadata.PropertyName));
|
||||||
|
|
||||||
|
return new[] { rule };
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<string> SplitAndTrimPropertyNames(string original)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(original))
|
||||||
|
{
|
||||||
|
return new string[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
var split = original.Split(',')
|
||||||
|
.Select(piece => piece.Trim())
|
||||||
|
.Where(trimmed => !string.IsNullOrEmpty(trimmed));
|
||||||
|
return split;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -724,16 +724,15 @@ namespace Microsoft.AspNet.Mvc.Rendering
|
||||||
string name)
|
string name)
|
||||||
{
|
{
|
||||||
var validatorProvider = _bindingContextAccessor.Value.ValidatorProvider;
|
var validatorProvider = _bindingContextAccessor.Value.ValidatorProvider;
|
||||||
|
|
||||||
metadata = metadata ??
|
metadata = metadata ??
|
||||||
ExpressionMetadataProvider.FromStringExpression(name, viewContext.ViewData, _metadataProvider);
|
ExpressionMetadataProvider.FromStringExpression(name, viewContext.ViewData, _metadataProvider);
|
||||||
|
var validationContext =
|
||||||
|
new ClientModelValidationContext(metadata, _metadataProvider, viewContext.HttpContext.RequestServices);
|
||||||
|
|
||||||
return
|
return validatorProvider
|
||||||
validatorProvider
|
|
||||||
.GetValidators(metadata)
|
.GetValidators(metadata)
|
||||||
.OfType<IClientModelValidator>()
|
.OfType<IClientModelValidator>()
|
||||||
.SelectMany(v => v.GetClientValidationRules(
|
.SelectMany(v => v.GetClientValidationRules(validationContext));
|
||||||
new ClientModelValidationContext(metadata, _metadataProvider)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static string EvalString(ViewContext viewContext, string key, string format)
|
internal static string EvalString(ViewContext viewContext, string key, string format)
|
||||||
|
|
|
||||||
|
|
@ -445,4 +445,10 @@
|
||||||
<data name="ApiExplorer_UnsupportedAction" xml:space="preserve">
|
<data name="ApiExplorer_UnsupportedAction" xml:space="preserve">
|
||||||
<value>The action '{0}' has ApiExplorer enabled, but is using conventional routing. Only actions which use attribute routing support ApiExplorer.</value>
|
<value>The action '{0}' has ApiExplorer enabled, but is using conventional routing. Only actions which use attribute routing support ApiExplorer.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="RemoteAttribute_NoUrlFound" xml:space="preserve">
|
||||||
|
<value>No URL for remote validation could be found.</value>
|
||||||
|
</data>
|
||||||
|
<data name="RemoteAttribute_RemoteValidationFailed" xml:space="preserve">
|
||||||
|
<value>'{0}' is invalid.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -351,19 +351,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<string> SplitString(string original)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(original))
|
|
||||||
{
|
|
||||||
return new string[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
var split = original.Split(',')
|
|
||||||
.Select(piece => piece.Trim())
|
|
||||||
.Where(trimmed => !string.IsNullOrEmpty(trimmed));
|
|
||||||
return split;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CompositePredicateProvider : IPropertyBindingPredicateProvider
|
private class CompositePredicateProvider : IPropertyBindingPredicateProvider
|
||||||
{
|
{
|
||||||
private readonly IPropertyBindingPredicateProvider[] _providers;
|
private readonly IPropertyBindingPredicateProvider[] _providers;
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,25 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
// Copyright (c) Microsoft Open Technologies, Inc. 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;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
public class ClientModelValidationContext
|
public class ClientModelValidationContext
|
||||||
{
|
{
|
||||||
public ClientModelValidationContext([NotNull] ModelMetadata metadata,
|
public ClientModelValidationContext([NotNull] ModelMetadata metadata,
|
||||||
[NotNull] IModelMetadataProvider metadataProvider)
|
[NotNull] IModelMetadataProvider metadataProvider,
|
||||||
|
[NotNull] IServiceProvider requestServices)
|
||||||
{
|
{
|
||||||
ModelMetadata = metadata;
|
ModelMetadata = metadata;
|
||||||
MetadataProvider = metadataProvider;
|
MetadataProvider = metadataProvider;
|
||||||
|
RequestServices = requestServices;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ModelMetadata ModelMetadata { get; private set; }
|
public ModelMetadata ModelMetadata { get; }
|
||||||
|
|
||||||
public IModelMetadataProvider MetadataProvider { get; private set; }
|
public IModelMetadataProvider MetadataProvider { get; }
|
||||||
|
|
||||||
|
public IServiceProvider RequestServices { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
public virtual IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
public virtual IEnumerable<ModelClientValidationRule> GetClientValidationRules(
|
||||||
[NotNull] ClientModelValidationContext context)
|
[NotNull] ClientModelValidationContext context)
|
||||||
{
|
{
|
||||||
|
var customValidator = Attribute as IClientModelValidator;
|
||||||
|
if (customValidator != null)
|
||||||
|
{
|
||||||
|
return customValidator.GetClientValidationRules(context);
|
||||||
|
}
|
||||||
|
|
||||||
return Enumerable.Empty<ModelClientValidationRule>();
|
return Enumerable.Empty<ModelClientValidationRule>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,54 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
// Copyright (c) Microsoft Open Technologies, Inc. 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.Collections.Generic;
|
||||||
using System.Linq;
|
using Microsoft.AspNet.Builder;
|
||||||
using System.Web.Routing;
|
using Microsoft.AspNet.Http.Core;
|
||||||
using Microsoft.TestCommon;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNet.Routing;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||||
|
using Microsoft.Framework.Logging;
|
||||||
|
using Microsoft.Framework.OptionsModel;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
namespace System.Web.Mvc.Test
|
namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
public class RemoteAttributeTest
|
public class RemoteAttributeTest
|
||||||
{
|
{
|
||||||
// Good route name, bad route name
|
private static readonly IModelMetadataProvider _metadataProvider = new EmptyModelMetadataProvider();
|
||||||
// Controller + Action
|
private static readonly ModelMetadata _metadata = _metadataProvider.GetMetadataForProperty(
|
||||||
|
modelAccessor: null,
|
||||||
|
containerType: typeof(string),
|
||||||
|
propertyName: "Length");
|
||||||
|
|
||||||
[Fact]
|
public static TheoryData<string> SomeNames
|
||||||
public void GuardClauses()
|
|
||||||
{
|
{
|
||||||
// Act & Assert
|
get
|
||||||
Assert.ThrowsArgumentNullOrEmpty(
|
{
|
||||||
() => new RemoteAttribute(null, "controller"),
|
return new TheoryData<string>
|
||||||
"action");
|
{
|
||||||
Assert.ThrowsArgumentNullOrEmpty(
|
string.Empty,
|
||||||
() => new RemoteAttribute("action", null),
|
"Action",
|
||||||
"controller");
|
"In a controller",
|
||||||
Assert.ThrowsArgumentNullOrEmpty(
|
" slightly\t odd\t whitespace\t\r\n",
|
||||||
() => new RemoteAttribute(null),
|
};
|
||||||
"routeName");
|
}
|
||||||
Assert.ThrowsArgumentNullOrEmpty(
|
}
|
||||||
() => RemoteAttribute.FormatPropertyForClientValidation(String.Empty),
|
|
||||||
"property");
|
// Null or empty property names are invalid. (Those containing just whitespace are legal.)
|
||||||
Assert.ThrowsArgumentNullOrEmpty(
|
public static TheoryData<string> NullOrEmptyNames
|
||||||
() => new RemoteAttribute("foo").FormatAdditionalFieldsForClientValidation(String.Empty),
|
{
|
||||||
"property");
|
get
|
||||||
|
{
|
||||||
|
return new TheoryData<string>
|
||||||
|
{
|
||||||
|
null,
|
||||||
|
string.Empty,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -43,408 +60,583 @@ namespace System.Web.Mvc.Test
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void BadRouteNameThrows()
|
public void Constructor_WithNullAction_IgnoresArgument()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange & Act
|
||||||
ControllerContext context = new ControllerContext();
|
var attribute = new TestableRemoteAttribute(action: null, controller: "AController");
|
||||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(object));
|
|
||||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("RouteName");
|
|
||||||
|
|
||||||
// Act & Assert
|
|
||||||
Assert.Throws<ArgumentException>(
|
|
||||||
() => new List<ModelClientValidationRule>(attribute.GetClientValidationRules(metadata, context)),
|
|
||||||
"A route named 'RouteName' could not be found in the route collection.\r\nParameter name: name");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void NoRouteWithActionControllerThrows()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
ControllerContext context = new ControllerContext();
|
|
||||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(null, typeof(string), "Length");
|
|
||||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller");
|
|
||||||
|
|
||||||
// Act & Assert
|
|
||||||
Assert.Throws<InvalidOperationException>(
|
|
||||||
() => new List<ModelClientValidationRule>(attribute.GetClientValidationRules(metadata, context)),
|
|
||||||
"No url for remote validation could be found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GoodRouteNameReturnsCorrectClientData()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
string url = null;
|
|
||||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(null, typeof(string), "Length");
|
|
||||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("RouteName");
|
|
||||||
attribute.RouteTable.Add("RouteName", new Route("my/url", new MvcRouteHandler()));
|
|
||||||
|
|
||||||
// Act
|
|
||||||
ModelClientValidationRule rule = attribute.GetClientValidationRules(metadata, GetMockControllerContext(url)).Single();
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
var keyValuePair = Assert.Single(attribute.RouteData);
|
||||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
Assert.Equal(keyValuePair.Key, "controller");
|
||||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
|
||||||
Assert.Equal("/my/url", rule.ValidationParameters["url"]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ActionControllerReturnsCorrectClientDataWithoutNamedParameters()
|
public void Constructor_WithNullController_IgnoresArgument()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange & Act
|
||||||
string url = null;
|
var attribute = new TestableRemoteAttribute("AnAction", controller: null);
|
||||||
|
|
||||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(null, typeof(string), "Length");
|
|
||||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller");
|
|
||||||
attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
|
|
||||||
|
|
||||||
// Act
|
|
||||||
ModelClientValidationRule rule = attribute.GetClientValidationRules(metadata, GetMockControllerContext(url)).Single();
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
var keyValuePair = Assert.Single(attribute.RouteData);
|
||||||
|
Assert.Equal(keyValuePair.Key, "action");
|
||||||
|
Assert.Null(attribute.RouteName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(null)]
|
||||||
|
[MemberData(nameof(SomeNames))]
|
||||||
|
public void Constructor_WithRouteName_UpdatesProperty(string routeName)
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var attribute = new TestableRemoteAttribute(routeName);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Empty(attribute.RouteData);
|
||||||
|
Assert.Equal(routeName, attribute.RouteName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(SomeNames))]
|
||||||
|
public void Constructor_WithActionController_UpdatesActionRouteData(string action)
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var attribute = new TestableRemoteAttribute(action, "AController");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(2, attribute.RouteData.Count);
|
||||||
|
Assert.Contains("controller", attribute.RouteData.Keys);
|
||||||
|
var resultName = Assert.Single(
|
||||||
|
attribute.RouteData,
|
||||||
|
keyValuePair => string.Equals(keyValuePair.Key, "action", StringComparison.Ordinal))
|
||||||
|
.Value;
|
||||||
|
Assert.Equal(action, resultName);
|
||||||
|
Assert.Null(attribute.RouteName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(SomeNames))]
|
||||||
|
public void Constructor_WithActionController_UpdatesControllerRouteData(string controller)
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var attribute = new TestableRemoteAttribute("AnAction", controller);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(2, attribute.RouteData.Count);
|
||||||
|
Assert.Contains("action", attribute.RouteData.Keys);
|
||||||
|
var resultName = Assert.Single(
|
||||||
|
attribute.RouteData,
|
||||||
|
keyValuePair => string.Equals(keyValuePair.Key, "controller", StringComparison.Ordinal))
|
||||||
|
.Value;
|
||||||
|
Assert.Equal(controller, resultName);
|
||||||
|
Assert.Null(attribute.RouteName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(null)]
|
||||||
|
[MemberData(nameof(SomeNames))]
|
||||||
|
public void Constructor_WithActionControllerAreaName_UpdatesAreaRouteData(string areaName)
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var attribute = new TestableRemoteAttribute("AnAction", "AController", areaName: areaName);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(3, attribute.RouteData.Count);
|
||||||
|
Assert.Contains("action", attribute.RouteData.Keys);
|
||||||
|
Assert.Contains("controller", attribute.RouteData.Keys);
|
||||||
|
var resultName = Assert.Single(
|
||||||
|
attribute.RouteData,
|
||||||
|
keyValuePair => string.Equals(keyValuePair.Key, "area", StringComparison.Ordinal))
|
||||||
|
.Value;
|
||||||
|
Assert.Equal(areaName, resultName);
|
||||||
|
Assert.Null(attribute.RouteName);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(NullOrEmptyNames))]
|
||||||
|
public void FormatAdditionalFieldsForClientValidation_WithInvalidPropertyName_Throws(string property)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var attribute = new RemoteAttribute(routeName: "default");
|
||||||
|
var expected = "Value cannot be null or empty." + Environment.NewLine + "Parameter name: property";
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var exception = Assert.Throws<ArgumentException>(
|
||||||
|
"property",
|
||||||
|
() => attribute.FormatAdditionalFieldsForClientValidation(property));
|
||||||
|
Assert.Equal(expected, exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(NullOrEmptyNames))]
|
||||||
|
public void FormatPropertyForClientValidation_WithInvalidPropertyName_Throws(string property)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = "Value cannot be null or empty." + Environment.NewLine + "Parameter name: property";
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var exception = Assert.Throws<ArgumentException>(
|
||||||
|
"property",
|
||||||
|
() => RemoteAttribute.FormatPropertyForClientValidation(property));
|
||||||
|
Assert.Equal(expected, exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetClientValidationRules_WithBadRouteName_Throws()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var attribute = new RemoteAttribute("nonexistentRoute");
|
||||||
|
var context = GetValidationContextWithArea(currentArea: null);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var exception = Assert.Throws<InvalidOperationException>(() => attribute.GetClientValidationRules(context));
|
||||||
|
Assert.Equal("No URL for remote validation could be found.", exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetClientValidationRules_WithActionController_NoController_Throws()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var attribute = new RemoteAttribute("Action", "Controller");
|
||||||
|
var context = GetValidationContextWithNoController();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var exception = Assert.Throws<InvalidOperationException>(() => attribute.GetClientValidationRules(context));
|
||||||
|
Assert.Equal("No URL for remote validation could be found.", exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetClientValidationRules_WithRoute_CallsUrlHelperWithExpectedValues()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var routeName = "RouteName";
|
||||||
|
var attribute = new RemoteAttribute(routeName);
|
||||||
|
var url = "/my/URL";
|
||||||
|
var urlHelper = new MockUrlHelper(url, routeName);
|
||||||
|
var context = GetValidationContext(urlHelper);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
Assert.Equal("remote", rule.ValidationType);
|
||||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||||
|
|
||||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||||
Assert.Equal("/Controller/Action", rule.ValidationParameters["url"]);
|
|
||||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
||||||
Assert.Throws<KeyNotFoundException>(
|
Assert.Equal(url, rule.ValidationParameters["url"]);
|
||||||
() => rule.ValidationParameters["type"],
|
|
||||||
"The given key was not present in the dictionary.");
|
var routeDictionary = Assert.IsType<RouteValueDictionary>(urlHelper.RouteValues);
|
||||||
|
Assert.Empty(routeDictionary);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ActionControllerReturnsCorrectClientDataWithNamedParameters()
|
public void GetClientValidationRules_WithActionController_CallsUrlHelperWithExpectedValues()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
string url = null;
|
var attribute = new RemoteAttribute("Action", "Controller");
|
||||||
|
var url = "/Controller/Action";
|
||||||
|
var urlHelper = new MockUrlHelper(url, routeName: null);
|
||||||
|
var context = GetValidationContext(urlHelper);
|
||||||
|
|
||||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(null, typeof(string), "Length");
|
// Act & Assert
|
||||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller");
|
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||||
attribute.HttpMethod = "POST";
|
|
||||||
attribute.AdditionalFields = "Password,ConfirmPassword";
|
|
||||||
|
|
||||||
attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
|
|
||||||
|
|
||||||
// Act
|
|
||||||
ModelClientValidationRule rule = attribute.GetClientValidationRules(metadata, GetMockControllerContext(url)).Single();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
Assert.Equal("remote", rule.ValidationType);
|
||||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||||
|
|
||||||
|
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||||
|
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
||||||
|
Assert.Equal(url, rule.ValidationParameters["url"]);
|
||||||
|
|
||||||
|
var routeDictionary = Assert.IsType<RouteValueDictionary>(urlHelper.RouteValues);
|
||||||
|
Assert.Equal(2, routeDictionary.Count);
|
||||||
|
Assert.Equal("Action", routeDictionary["action"] as string);
|
||||||
|
Assert.Equal("Controller", routeDictionary["controller"] as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetClientValidationRules_WithActionController_PropertiesSet_CallsUrlHelperWithExpectedValues()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var attribute = new RemoteAttribute("Action", "Controller")
|
||||||
|
{
|
||||||
|
HttpMethod = "POST",
|
||||||
|
AdditionalFields = "Password,ConfirmPassword",
|
||||||
|
};
|
||||||
|
var url = "/Controller/Action";
|
||||||
|
var urlHelper = new MockUrlHelper(url, routeName: null);
|
||||||
|
var context = GetValidationContext(urlHelper);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||||
|
Assert.Equal("remote", rule.ValidationType);
|
||||||
|
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||||
|
|
||||||
Assert.Equal(3, rule.ValidationParameters.Count);
|
Assert.Equal(3, rule.ValidationParameters.Count);
|
||||||
Assert.Equal("/Controller/Action", rule.ValidationParameters["url"]);
|
|
||||||
Assert.Equal("*.Length,*.Password,*.ConfirmPassword", rule.ValidationParameters["additionalfields"]);
|
Assert.Equal("*.Length,*.Password,*.ConfirmPassword", rule.ValidationParameters["additionalfields"]);
|
||||||
Assert.Equal("POST", rule.ValidationParameters["type"]);
|
Assert.Equal("POST", rule.ValidationParameters["type"]);
|
||||||
|
Assert.Equal(url, rule.ValidationParameters["url"]);
|
||||||
|
|
||||||
|
var routeDictionary = Assert.IsType<RouteValueDictionary>(urlHelper.RouteValues);
|
||||||
|
Assert.Equal(2, routeDictionary.Count);
|
||||||
|
Assert.Equal("Action", routeDictionary["action"] as string);
|
||||||
|
Assert.Equal("Controller", routeDictionary["controller"] as string);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Current area is root in this case.
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ActionController_RemoteFindsControllerInCurrentArea()
|
public void GetClientValidationRules_WithActionControllerArea_CallsUrlHelperWithExpectedValues()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
|
var attribute = new RemoteAttribute("Action", "Controller", "Test")
|
||||||
containerType: typeof(string), propertyName: "Length");
|
{
|
||||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller");
|
HttpMethod = "POST",
|
||||||
attribute.HttpMethod = "POST";
|
};
|
||||||
|
var url = "/Test/Controller/Action";
|
||||||
|
var urlHelper = new MockUrlHelper(url, routeName: null);
|
||||||
|
var context = GetValidationContext(urlHelper);
|
||||||
|
|
||||||
var context = new AreaRegistrationContext("Test", attribute.RouteTable);
|
// Act & Assert
|
||||||
context.MapRoute(name: null, url: "Test/{controller}/{action}");
|
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||||
|
|
||||||
attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
|
|
||||||
|
|
||||||
// Act
|
|
||||||
ModelClientValidationRule rule =
|
|
||||||
attribute.GetClientValidationRules(metadata, GetMockControllerContext(url: null)).Single();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
Assert.Equal("remote", rule.ValidationType);
|
||||||
|
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||||
|
|
||||||
|
Assert.Equal(3, rule.ValidationParameters.Count);
|
||||||
|
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
||||||
|
Assert.Equal("POST", rule.ValidationParameters["type"]);
|
||||||
|
Assert.Equal(url, rule.ValidationParameters["url"]);
|
||||||
|
|
||||||
|
var routeDictionary = Assert.IsType<RouteValueDictionary>(urlHelper.RouteValues);
|
||||||
|
Assert.Equal(3, routeDictionary.Count);
|
||||||
|
Assert.Equal("Action", routeDictionary["action"] as string);
|
||||||
|
Assert.Equal("Controller", routeDictionary["controller"] as string);
|
||||||
|
Assert.Equal("Test", routeDictionary["area"] as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Root area is current in this case.
|
||||||
|
[Fact]
|
||||||
|
public void GetClientValidationRules_WithActionController_FindsControllerInCurrentArea()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var attribute = new RemoteAttribute("Action", "Controller");
|
||||||
|
var context = GetValidationContextWithArea(currentArea: null);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||||
|
Assert.Equal("remote", rule.ValidationType);
|
||||||
|
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||||
|
|
||||||
|
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||||
|
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
||||||
Assert.Equal("/Controller/Action", rule.ValidationParameters["url"]);
|
Assert.Equal("/Controller/Action", rule.ValidationParameters["url"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test area is current in this case.
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ActionControllerArea_RemoteFindsControllerInNamedArea()
|
public void GetClientValidationRules_WithActionControllerInArea_FindsControllerInCurrentArea()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
|
var attribute = new RemoteAttribute("Action", "Controller");
|
||||||
containerType: typeof(string), propertyName: "Length");
|
var context = GetValidationContextWithArea(currentArea: "Test");
|
||||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", "Test");
|
|
||||||
attribute.HttpMethod = "POST";
|
|
||||||
|
|
||||||
var context = new AreaRegistrationContext("Test", attribute.RouteTable);
|
// Act & Assert
|
||||||
context.MapRoute(name: null, url: "Test/{controller}/{action}");
|
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||||
|
|
||||||
attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
|
|
||||||
|
|
||||||
// Act
|
|
||||||
ModelClientValidationRule rule =
|
|
||||||
attribute.GetClientValidationRules(metadata, GetMockControllerContext(url: null)).Single();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
Assert.Equal("remote", rule.ValidationType);
|
||||||
|
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||||
|
|
||||||
|
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||||
|
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
||||||
Assert.Equal("/Test/Controller/Action", rule.ValidationParameters["url"]);
|
Assert.Equal("/Test/Controller/Action", rule.ValidationParameters["url"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Current area is root in this case.
|
// Explicit reference to the (current) root area.
|
||||||
[Fact]
|
[Theory]
|
||||||
public void ActionControllerArea_WithEmptyArea_RemoteFindsControllerInCurrentArea()
|
[MemberData(nameof(NullOrEmptyNames))]
|
||||||
|
public void GetClientValidationRules_WithActionControllerArea_FindsControllerInRootArea(string areaName)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
|
var attribute = new RemoteAttribute("Action", "Controller", areaName);
|
||||||
containerType: typeof(string), propertyName: "Length");
|
var context = GetValidationContextWithArea(currentArea: null);
|
||||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", "");
|
|
||||||
attribute.HttpMethod = "POST";
|
|
||||||
|
|
||||||
var context = new AreaRegistrationContext("Test", attribute.RouteTable);
|
// Act & Assert
|
||||||
context.MapRoute(name: null, url: "Test/{controller}/{action}");
|
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||||
|
|
||||||
attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
|
|
||||||
|
|
||||||
// Act
|
|
||||||
ModelClientValidationRule rule =
|
|
||||||
attribute.GetClientValidationRules(metadata, GetMockControllerContext(url: null)).Single();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
Assert.Equal("remote", rule.ValidationType);
|
||||||
|
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||||
|
|
||||||
|
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||||
|
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
||||||
Assert.Equal("/Controller/Action", rule.ValidationParameters["url"]);
|
Assert.Equal("/Controller/Action", rule.ValidationParameters["url"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Current area is root in this case.
|
// Test area is current in this case.
|
||||||
[Fact]
|
[Theory]
|
||||||
public void ActionControllerAreaReference_WithUseCurrent_RemoteFindsControllerInCurrentArea()
|
[MemberData(nameof(NullOrEmptyNames))]
|
||||||
|
public void GetClientValidationRules_WithActionControllerAreaInArea_FindsControllerInRootArea(string areaName)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
|
var attribute = new RemoteAttribute("Action", "Controller", areaName);
|
||||||
containerType: typeof(string), propertyName: "Length");
|
var context = GetValidationContextWithArea(currentArea: "Test");
|
||||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", AreaReference.UseCurrent);
|
|
||||||
attribute.HttpMethod = "POST";
|
|
||||||
|
|
||||||
var context = new AreaRegistrationContext("Test", attribute.RouteTable);
|
// Act & Assert
|
||||||
context.MapRoute(name: null, url: "Test/{controller}/{action}");
|
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||||
|
|
||||||
attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
|
|
||||||
|
|
||||||
// Act
|
|
||||||
ModelClientValidationRule rule =
|
|
||||||
attribute.GetClientValidationRules(metadata, GetMockControllerContext(url: null)).Single();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
Assert.Equal("remote", rule.ValidationType);
|
||||||
|
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||||
|
|
||||||
|
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||||
|
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
||||||
Assert.Equal("/Controller/Action", rule.ValidationParameters["url"]);
|
Assert.Equal("/Controller/Action", rule.ValidationParameters["url"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Root area is current in this case.
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ActionControllerAreaReference_WithUseRoot_RemoteFindsControllerInRoot()
|
public void GetClientValidationRules_WithActionControllerArea_FindsControllerInNamedArea()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
|
var attribute = new RemoteAttribute("Action", "Controller", "Test");
|
||||||
containerType: typeof(string), propertyName: "Length");
|
var context = GetValidationContextWithArea(currentArea: null);
|
||||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", AreaReference.UseRoot);
|
|
||||||
attribute.HttpMethod = "POST";
|
|
||||||
|
|
||||||
var context = new AreaRegistrationContext("Test", attribute.RouteTable);
|
// Act & Assert
|
||||||
context.MapRoute(name: null, url: "Test/{controller}/{action}");
|
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||||
|
|
||||||
attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
|
|
||||||
|
|
||||||
// Act
|
|
||||||
ModelClientValidationRule rule =
|
|
||||||
attribute.GetClientValidationRules(metadata, GetMockControllerContext(url: null)).Single();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
Assert.Equal("remote", rule.ValidationType);
|
||||||
Assert.Equal("/Controller/Action", rule.ValidationParameters["url"]);
|
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||||
}
|
|
||||||
|
|
||||||
// Current area is Test in this case.
|
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||||
[Fact]
|
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
||||||
public void ActionController_InArea_RemoteFindsControllerInCurrentArea()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
|
|
||||||
containerType: typeof(string), propertyName: "Length");
|
|
||||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller");
|
|
||||||
attribute.HttpMethod = "POST";
|
|
||||||
|
|
||||||
var context = new AreaRegistrationContext("Test", attribute.RouteTable);
|
|
||||||
context.MapRoute(name: null, url: "Test/{controller}/{action}");
|
|
||||||
|
|
||||||
attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
|
|
||||||
|
|
||||||
// Act
|
|
||||||
ModelClientValidationRule rule =
|
|
||||||
attribute.GetClientValidationRules(metadata, GetMockControllerContextWithArea(url: null, areaName: "Test"))
|
|
||||||
.Single();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
|
||||||
Assert.Equal("/Test/Controller/Action", rule.ValidationParameters["url"]);
|
Assert.Equal("/Test/Controller/Action", rule.ValidationParameters["url"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explicit reference to the Test area.
|
// Explicit reference to the current (Test) area.
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ActionControllerArea_InSameArea_RemoteFindsControllerInNamedArea()
|
public void GetClientValidationRules_WithActionControllerAreaInArea_FindsControllerInNamedArea()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
|
var attribute = new RemoteAttribute("Action", "Controller", "Test");
|
||||||
containerType: typeof(string), propertyName: "Length");
|
var context = GetValidationContextWithArea(currentArea: "Test");
|
||||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", "Test");
|
|
||||||
attribute.HttpMethod = "POST";
|
|
||||||
|
|
||||||
var context = new AreaRegistrationContext("Test", attribute.RouteTable);
|
// Act & Assert
|
||||||
context.MapRoute(name: null, url: "Test/{controller}/{action}");
|
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||||
|
|
||||||
attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
|
|
||||||
|
|
||||||
// Act
|
|
||||||
ModelClientValidationRule rule =
|
|
||||||
attribute.GetClientValidationRules(metadata, GetMockControllerContextWithArea(url: null, areaName: "Test"))
|
|
||||||
.Single();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
Assert.Equal("remote", rule.ValidationType);
|
||||||
|
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||||
|
|
||||||
|
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||||
|
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
||||||
Assert.Equal("/Test/Controller/Action", rule.ValidationParameters["url"]);
|
Assert.Equal("/Test/Controller/Action", rule.ValidationParameters["url"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test area is current in this case.
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ActionControllerArea_InArea_RemoteFindsControllerInNamedArea()
|
public void GetClientValidationRules_WithActionControllerAreaInArea_FindsControllerInDifferentArea()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
|
var attribute = new RemoteAttribute("Action", "Controller", "AnotherArea");
|
||||||
containerType: typeof(string), propertyName: "Length");
|
var context = GetValidationContextWithArea(currentArea: "Test");
|
||||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", "AnotherArea");
|
|
||||||
attribute.HttpMethod = "POST";
|
|
||||||
|
|
||||||
var context = new AreaRegistrationContext("Test", attribute.RouteTable);
|
// Act & Assert
|
||||||
context.MapRoute(name: null, url: "Test/{controller}/{action}");
|
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||||
context = new AreaRegistrationContext("AnotherArea", attribute.RouteTable);
|
|
||||||
context.MapRoute(name: null, url: "AnotherArea/{controller}/{action}");
|
|
||||||
|
|
||||||
attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
|
|
||||||
|
|
||||||
// Act
|
|
||||||
ModelClientValidationRule rule =
|
|
||||||
attribute.GetClientValidationRules(metadata, GetMockControllerContextWithArea(url: null, areaName: "Test"))
|
|
||||||
.Single();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
Assert.Equal("remote", rule.ValidationType);
|
||||||
|
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||||
|
|
||||||
|
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||||
|
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
||||||
Assert.Equal("/AnotherArea/Controller/Action", rule.ValidationParameters["url"]);
|
Assert.Equal("/AnotherArea/Controller/Action", rule.ValidationParameters["url"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Current area is Test in this case.
|
private static ClientModelValidationContext GetValidationContext(IUrlHelper urlHelper)
|
||||||
[Fact]
|
|
||||||
public void ActionControllerArea_WithEmptyAreaInArea_RemoteFindsControllerInCurrentArea()
|
|
||||||
{
|
{
|
||||||
// Arrange
|
var serviceCollection = GetServiceCollection();
|
||||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
|
serviceCollection.AddInstance<IUrlHelper>(urlHelper);
|
||||||
containerType: typeof(string), propertyName: "Length");
|
var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", "");
|
|
||||||
attribute.HttpMethod = "POST";
|
|
||||||
|
|
||||||
var context = new AreaRegistrationContext("Test", attribute.RouteTable);
|
return new ClientModelValidationContext(_metadata, _metadataProvider, serviceProvider);
|
||||||
context.MapRoute(name: null, url: "Test/{controller}/{action}");
|
|
||||||
|
|
||||||
attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
|
|
||||||
|
|
||||||
// Act
|
|
||||||
ModelClientValidationRule rule =
|
|
||||||
attribute.GetClientValidationRules(metadata, GetMockControllerContextWithArea(url: null, areaName: "Test"))
|
|
||||||
.Single();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
|
||||||
Assert.Equal("/Test/Controller/Action", rule.ValidationParameters["url"]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Current area is Test in this case.
|
private static ClientModelValidationContext GetValidationContextWithArea(string currentArea)
|
||||||
[Fact]
|
|
||||||
public void ActionControllerAreaReference_WithUseCurrentInArea_RemoteFindsControllerInCurrentArea()
|
|
||||||
{
|
{
|
||||||
// Arrange
|
var serviceCollection = GetServiceCollection();
|
||||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
|
var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||||
containerType: typeof(string), propertyName: "Length");
|
var routeCollection = GetRouteCollectionWithArea(serviceProvider);
|
||||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", AreaReference.UseCurrent);
|
var routeData = new RouteData
|
||||||
attribute.HttpMethod = "POST";
|
{
|
||||||
|
Routers =
|
||||||
|
{
|
||||||
|
routeCollection,
|
||||||
|
},
|
||||||
|
Values =
|
||||||
|
{
|
||||||
|
{ "action", "Index" },
|
||||||
|
{ "controller", "Home" },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (!string.IsNullOrEmpty(currentArea))
|
||||||
|
{
|
||||||
|
routeData.Values["area"] = currentArea;
|
||||||
|
}
|
||||||
|
|
||||||
var context = new AreaRegistrationContext("Test", attribute.RouteTable);
|
var contextAccessor = GetContextAccessor(serviceProvider, routeData);
|
||||||
context.MapRoute(name: null, url: "Test/{controller}/{action}");
|
var actionSelector = new Mock<IActionSelector>(MockBehavior.Strict);
|
||||||
|
var urlHelper = new UrlHelper(contextAccessor, actionSelector.Object);
|
||||||
|
serviceCollection.AddInstance<IUrlHelper>(urlHelper);
|
||||||
|
serviceProvider = serviceCollection.BuildServiceProvider();
|
||||||
|
|
||||||
attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
|
return new ClientModelValidationContext(_metadata, _metadataProvider, serviceProvider);
|
||||||
|
|
||||||
// Act
|
|
||||||
ModelClientValidationRule rule =
|
|
||||||
attribute.GetClientValidationRules(metadata, GetMockControllerContextWithArea(url: null, areaName: "Test"))
|
|
||||||
.Single();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
|
||||||
Assert.Equal("/Test/Controller/Action", rule.ValidationParameters["url"]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
private static ClientModelValidationContext GetValidationContextWithNoController()
|
||||||
public void ActionControllerAreaReference_WithUseRootInArea_RemoteFindsControllerInRoot()
|
|
||||||
{
|
{
|
||||||
// Arrange
|
var serviceCollection = GetServiceCollection();
|
||||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
|
var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||||
containerType: typeof(string), propertyName: "Length");
|
var routeCollection = GetRouteCollectionWithNoController(serviceProvider);
|
||||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", AreaReference.UseRoot);
|
var routeData = new RouteData
|
||||||
attribute.HttpMethod = "POST";
|
{
|
||||||
|
Routers =
|
||||||
|
{
|
||||||
|
routeCollection,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
var context = new AreaRegistrationContext("Test", attribute.RouteTable);
|
var contextAccessor = GetContextAccessor(serviceProvider, routeData);
|
||||||
context.MapRoute(name: null, url: "Test/{controller}/{action}");
|
var actionSelector = new Mock<IActionSelector>(MockBehavior.Strict);
|
||||||
|
var urlHelper = new UrlHelper(contextAccessor, actionSelector.Object);
|
||||||
|
serviceCollection.AddInstance<IUrlHelper>(urlHelper);
|
||||||
|
serviceProvider = serviceCollection.BuildServiceProvider();
|
||||||
|
|
||||||
attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
|
return new ClientModelValidationContext(_metadata, _metadataProvider, serviceProvider);
|
||||||
|
|
||||||
// Act
|
|
||||||
ModelClientValidationRule rule =
|
|
||||||
attribute.GetClientValidationRules(metadata, GetMockControllerContextWithArea(url: null, areaName: "Test"))
|
|
||||||
.Single();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal("remote", rule.ValidationType);
|
|
||||||
Assert.Equal("/Controller/Action", rule.ValidationParameters["url"]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ControllerContext GetMockControllerContext(string url)
|
private static IRouter GetRouteCollectionWithArea(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
Mock<ControllerContext> context = new Mock<ControllerContext>();
|
var builder = GetRouteBuilder(serviceProvider, isBound: true);
|
||||||
context.Setup(c => c.HttpContext.Request.ApplicationPath)
|
|
||||||
.Returns("/");
|
|
||||||
context.Setup(c => c.HttpContext.Response.ApplyAppPathModifier(It.IsAny<string>()))
|
|
||||||
.Callback<string>(vpath => url = vpath)
|
|
||||||
.Returns(() => url);
|
|
||||||
|
|
||||||
return context.Object;
|
// Setting IsBound to true makes order more important than usual. First try the route that requires the
|
||||||
|
// area value. Skip usual "area:exists" constraint because that isn't relevant for link generation and it
|
||||||
|
// complicates the setup significantly.
|
||||||
|
builder.MapRoute("areaRoute", "{area}/{controller}/{action}");
|
||||||
|
builder.MapRoute("default", "{controller}/{action}", new { controller = "Home", action = "Index" });
|
||||||
|
|
||||||
|
return builder.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ControllerContext GetMockControllerContextWithArea(string url, string areaName)
|
private static IRouter GetRouteCollectionWithNoController(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
Mock<ControllerContext> context = new Mock<ControllerContext>();
|
var builder = GetRouteBuilder(serviceProvider, isBound: false);
|
||||||
context.Setup(c => c.HttpContext.Request.ApplicationPath)
|
builder.MapRoute("default", "static/route");
|
||||||
.Returns("/");
|
|
||||||
context.Setup(c => c.HttpContext.Response.ApplyAppPathModifier(It.IsAny<string>()))
|
|
||||||
.Callback<string>(vpath => url = vpath)
|
|
||||||
.Returns(() => url);
|
|
||||||
|
|
||||||
var controllerContext = context.Object;
|
return builder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
controllerContext.RequestContext.RouteData.DataTokens.Add("area", areaName);
|
private static RouteBuilder GetRouteBuilder(IServiceProvider serviceProvider, bool isBound)
|
||||||
|
{
|
||||||
|
var builder = new RouteBuilder
|
||||||
|
{
|
||||||
|
ServiceProvider = serviceProvider,
|
||||||
|
};
|
||||||
|
|
||||||
return controllerContext;
|
var handler = new Mock<IRouter>(MockBehavior.Strict);
|
||||||
|
handler
|
||||||
|
.Setup(router => router.GetVirtualPath(It.IsAny<VirtualPathContext>()))
|
||||||
|
.Callback<VirtualPathContext>(context => context.IsBound = isBound)
|
||||||
|
.Returns((string)null);
|
||||||
|
builder.DefaultHandler = handler.Object;
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IScopedInstance<ActionContext> GetContextAccessor(
|
||||||
|
IServiceProvider serviceProvider,
|
||||||
|
RouteData routeData = null)
|
||||||
|
{
|
||||||
|
// Set IServiceProvider properties because TemplateRoute gets services (e.g. an ILoggerFactory instance)
|
||||||
|
// through the HttpContext.
|
||||||
|
var httpContext = new DefaultHttpContext
|
||||||
|
{
|
||||||
|
ApplicationServices = serviceProvider,
|
||||||
|
RequestServices = serviceProvider,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (routeData == null)
|
||||||
|
{
|
||||||
|
routeData = new RouteData
|
||||||
|
{
|
||||||
|
Routers = { Mock.Of<IRouter>(), },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var actionContext = new ActionContext(httpContext, routeData, new ActionDescriptor());
|
||||||
|
var contextAccessor = new Mock<IScopedInstance<ActionContext>>();
|
||||||
|
contextAccessor
|
||||||
|
.SetupGet(accessor => accessor.Value)
|
||||||
|
.Returns(actionContext);
|
||||||
|
|
||||||
|
return contextAccessor.Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ServiceCollection GetServiceCollection()
|
||||||
|
{
|
||||||
|
var serviceCollection = new ServiceCollection();
|
||||||
|
serviceCollection.AddInstance<ILoggerFactory>(new NullLoggerFactory());
|
||||||
|
|
||||||
|
var routeOptions = new RouteOptions();
|
||||||
|
var accessor = new Mock<IOptions<RouteOptions>>();
|
||||||
|
accessor
|
||||||
|
.SetupGet(options => options.Options)
|
||||||
|
.Returns(routeOptions);
|
||||||
|
|
||||||
|
// DefaultInlineConstraintResolver constructor does not currently check its serviceProvider parameter (e.g.
|
||||||
|
// for null) and the class does not look up any services.
|
||||||
|
serviceCollection.AddInstance<IInlineConstraintResolver>(new DefaultInlineConstraintResolver(
|
||||||
|
serviceProvider: null,
|
||||||
|
routeOptions: accessor.Object));
|
||||||
|
|
||||||
|
return serviceCollection;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MockUrlHelper : IUrlHelper
|
||||||
|
{
|
||||||
|
private readonly string _routeName;
|
||||||
|
private readonly string _url;
|
||||||
|
|
||||||
|
public MockUrlHelper(string url, string routeName)
|
||||||
|
{
|
||||||
|
_routeName = routeName;
|
||||||
|
_url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object RouteValues { get; private set; }
|
||||||
|
|
||||||
|
public string Action(
|
||||||
|
string action,
|
||||||
|
string controller,
|
||||||
|
object values,
|
||||||
|
string protocol,
|
||||||
|
string host,
|
||||||
|
string fragment)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Content(string contentPath)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsLocalUrl(string url)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string RouteUrl(string routeName, object values, string protocol, string host, string fragment)
|
||||||
|
{
|
||||||
|
Assert.Equal(_routeName, routeName);
|
||||||
|
Assert.Null(protocol);
|
||||||
|
Assert.Null(host);
|
||||||
|
Assert.Null(fragment);
|
||||||
|
|
||||||
|
RouteValues = values;
|
||||||
|
|
||||||
|
return _url;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestableRemoteAttribute : RemoteAttribute
|
private class TestableRemoteAttribute : RemoteAttribute
|
||||||
{
|
{
|
||||||
public RouteCollection RouteTable = new RouteCollection();
|
public TestableRemoteAttribute(string routeName)
|
||||||
|
: base(routeName)
|
||||||
public TestableRemoteAttribute(string action, string controller, AreaReference areaReference)
|
|
||||||
: base(action, controller, areaReference)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public TestableRemoteAttribute(string action, string controller, string areaName)
|
|
||||||
: base(action, controller, areaName)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -453,14 +645,25 @@ namespace System.Web.Mvc.Test
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public TestableRemoteAttribute(string routeName)
|
public TestableRemoteAttribute(string action, string controller, string areaName)
|
||||||
: base(routeName)
|
: base(action, controller, areaName)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override RouteCollection Routes
|
public new string RouteName
|
||||||
{
|
{
|
||||||
get { return RouteTable; }
|
get
|
||||||
|
{
|
||||||
|
return base.RouteName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public new RouteValueDictionary RouteData
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return base.RouteData;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ using Microsoft.Framework.OptionsModel;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.Core.Test
|
namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
public class UrlHelperTest
|
public class UrlHelperTest
|
||||||
{
|
{
|
||||||
|
|
@ -695,7 +695,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
private static UrlHelper CreateUrlHelperWithRouteCollection(string appPrefix)
|
private static UrlHelper CreateUrlHelperWithRouteCollection(string appPrefix)
|
||||||
{
|
{
|
||||||
var routeCollection = GetRouter();
|
var routeCollection = GetRouter();
|
||||||
return CreateUrlHelper("/app", routeCollection);
|
return CreateUrlHelper(appPrefix, routeCollection);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IRouter GetRouter()
|
private static IRouter GetRouter()
|
||||||
|
|
@ -708,13 +708,9 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
||||||
var rt = new RouteBuilder();
|
var rt = new RouteBuilder();
|
||||||
var target = new Mock<IRouter>(MockBehavior.Strict);
|
var target = new Mock<IRouter>(MockBehavior.Strict);
|
||||||
target
|
target
|
||||||
.Setup(e => e.GetVirtualPath(It.IsAny<VirtualPathContext>()))
|
.Setup(router => router.GetVirtualPath(It.IsAny<VirtualPathContext>()))
|
||||||
.Callback<VirtualPathContext>(c =>
|
.Callback<VirtualPathContext>(context => context.IsBound = true)
|
||||||
{
|
.Returns<VirtualPathContext>(context => null);
|
||||||
rt.ToString();
|
|
||||||
c.IsBound = true;
|
|
||||||
})
|
|
||||||
.Returns<VirtualPathContext>(rc => null);
|
|
||||||
rt.DefaultHandler = target.Object;
|
rt.DefaultHandler = target.Object;
|
||||||
var serviceProviderMock = new Mock<IServiceProvider>();
|
var serviceProviderMock = new Mock<IServiceProvider>();
|
||||||
var accessorMock = new Mock<IOptions<RouteOptions>>();
|
var accessorMock = new Mock<IOptions<RouteOptions>>();
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNet.Testing;
|
using Microsoft.AspNet.Testing;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
@ -17,7 +19,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||||
var metadata = metadataProvider.GetMetadataForProperty(() => null, typeof(PropertyDisplayNameModel), "MyProperty");
|
var metadata = metadataProvider.GetMetadataForProperty(() => null, typeof(PropertyDisplayNameModel), "MyProperty");
|
||||||
var attribute = new CompareAttribute("OtherProperty");
|
var attribute = new CompareAttribute("OtherProperty");
|
||||||
var context = new ClientModelValidationContext(metadata, metadataProvider);
|
var serviceCollection = new ServiceCollection();
|
||||||
|
var requestServices = serviceCollection.BuildServiceProvider();
|
||||||
|
var context = new ClientModelValidationContext(metadata, metadataProvider, requestServices);
|
||||||
var adapter = new CompareAttributeAdapter(attribute);
|
var adapter = new CompareAttributeAdapter(attribute);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -36,7 +40,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||||
var metadata = metadataProvider.GetMetadataForProperty(() => null, typeof(PropertyNameModel), "MyProperty");
|
var metadata = metadataProvider.GetMetadataForProperty(() => null, typeof(PropertyNameModel), "MyProperty");
|
||||||
var attribute = new CompareAttribute("OtherProperty");
|
var attribute = new CompareAttribute("OtherProperty");
|
||||||
var context = new ClientModelValidationContext(metadata, metadataProvider);
|
var serviceCollection = new ServiceCollection();
|
||||||
|
var requestServices = serviceCollection.BuildServiceProvider();
|
||||||
|
var context = new ClientModelValidationContext(metadata, metadataProvider, requestServices);
|
||||||
var adapter = new CompareAttributeAdapter(attribute);
|
var adapter = new CompareAttributeAdapter(attribute);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -57,7 +63,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
ErrorMessage = "Hello '{0}', goodbye '{1}'."
|
ErrorMessage = "Hello '{0}', goodbye '{1}'."
|
||||||
};
|
};
|
||||||
var context = new ClientModelValidationContext(metadata, metadataProvider);
|
var serviceCollection = new ServiceCollection();
|
||||||
|
var requestServices = serviceCollection.BuildServiceProvider();
|
||||||
|
var context = new ClientModelValidationContext(metadata, metadataProvider, requestServices);
|
||||||
var adapter = new CompareAttributeAdapter(attribute);
|
var adapter = new CompareAttributeAdapter(attribute);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -85,7 +93,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
ErrorMessageResourceName = "CompareAttributeTestResource",
|
ErrorMessageResourceName = "CompareAttributeTestResource",
|
||||||
ErrorMessageResourceType = typeof(Test.Resources),
|
ErrorMessageResourceType = typeof(Test.Resources),
|
||||||
};
|
};
|
||||||
var context = new ClientModelValidationContext(metadata, metadataProvider);
|
var serviceCollection = new ServiceCollection();
|
||||||
|
var requestServices = serviceCollection.BuildServiceProvider();
|
||||||
|
var context = new ClientModelValidationContext(metadata, metadataProvider, requestServices);
|
||||||
var adapter = new CompareAttributeAdapter(attribute);
|
var adapter = new CompareAttributeAdapter(attribute);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
// Copyright (c) Microsoft Open Technologies, Inc. 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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
#if ASPNET50
|
#if ASPNET50
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
#endif
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||||
|
#if ASPNET50
|
||||||
using Moq;
|
using Moq;
|
||||||
using Moq.Protected;
|
using Moq.Protected;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -196,6 +201,51 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetClientValidationRules_ReturnsEmptyRuleSet()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var attribute = new FileExtensionsAttribute();
|
||||||
|
var validator = new DataAnnotationsModelValidator<FileExtensionsAttribute>(attribute);
|
||||||
|
var metadata = _metadataProvider.GetMetadataForProperty(
|
||||||
|
modelAccessor: null,
|
||||||
|
containerType: typeof(string),
|
||||||
|
propertyName: nameof(string.Length));
|
||||||
|
var serviceCollection = new ServiceCollection();
|
||||||
|
var requestServices = serviceCollection.BuildServiceProvider();
|
||||||
|
var context = new ClientModelValidationContext(metadata, _metadataProvider, requestServices);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var results = validator.GetClientValidationRules(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Empty(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetClientValidationRules_WithIClientModelValidator_CallsAttribute()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var attribute = new TestableAttribute();
|
||||||
|
var validator = new DataAnnotationsModelValidator<TestableAttribute>(attribute);
|
||||||
|
var metadata = _metadataProvider.GetMetadataForProperty(
|
||||||
|
modelAccessor: null,
|
||||||
|
containerType: typeof(string),
|
||||||
|
propertyName: nameof(string.Length));
|
||||||
|
var serviceCollection = new ServiceCollection();
|
||||||
|
var requestServices = serviceCollection.BuildServiceProvider();
|
||||||
|
var context = new ClientModelValidationContext(metadata, _metadataProvider, requestServices);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var results = validator.GetClientValidationRules(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var rule = Assert.Single(results);
|
||||||
|
Assert.Equal("an error", rule.ErrorMessage);
|
||||||
|
Assert.Empty(rule.ValidationParameters);
|
||||||
|
Assert.Equal("testable", rule.ValidationType);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void IsRequiredTests()
|
public void IsRequiredTests()
|
||||||
{
|
{
|
||||||
|
|
@ -221,5 +271,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TestableAttribute : ValidationAttribute, IClientModelValidator
|
||||||
|
{
|
||||||
|
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ClientModelValidationContext context)
|
||||||
|
{
|
||||||
|
return new[] { new ModelClientValidationRule(validationType: "testable", errorMessage: "an error") };
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNet.Testing;
|
using Microsoft.AspNet.Testing;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
@ -18,7 +20,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
|
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
|
||||||
var attribute = new MaxLengthAttribute(10);
|
var attribute = new MaxLengthAttribute(10);
|
||||||
var adapter = new MaxLengthAttributeAdapter(attribute);
|
var adapter = new MaxLengthAttributeAdapter(attribute);
|
||||||
var context = new ClientModelValidationContext(metadata, provider);
|
var serviceCollection = new ServiceCollection();
|
||||||
|
var requestServices = serviceCollection.BuildServiceProvider();
|
||||||
|
var context = new ClientModelValidationContext(metadata, provider, requestServices);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
var rules = adapter.GetClientValidationRules(context);
|
||||||
|
|
@ -42,7 +46,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), propertyName);
|
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), propertyName);
|
||||||
var attribute = new MaxLengthAttribute(5) { ErrorMessage = message };
|
var attribute = new MaxLengthAttribute(5) { ErrorMessage = message };
|
||||||
var adapter = new MaxLengthAttributeAdapter(attribute);
|
var adapter = new MaxLengthAttributeAdapter(attribute);
|
||||||
var context = new ClientModelValidationContext(metadata, provider);
|
var serviceCollection = new ServiceCollection();
|
||||||
|
var requestServices = serviceCollection.BuildServiceProvider();
|
||||||
|
var context = new ClientModelValidationContext(metadata, provider, requestServices);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
var rules = adapter.GetClientValidationRules(context);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNet.Testing;
|
using Microsoft.AspNet.Testing;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
@ -18,7 +20,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
|
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
|
||||||
var attribute = new MinLengthAttribute(6);
|
var attribute = new MinLengthAttribute(6);
|
||||||
var adapter = new MinLengthAttributeAdapter(attribute);
|
var adapter = new MinLengthAttributeAdapter(attribute);
|
||||||
var context = new ClientModelValidationContext(metadata, provider);
|
var serviceCollection = new ServiceCollection();
|
||||||
|
var requestServices = serviceCollection.BuildServiceProvider();
|
||||||
|
var context = new ClientModelValidationContext(metadata, provider, requestServices);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
var rules = adapter.GetClientValidationRules(context);
|
||||||
|
|
@ -42,7 +46,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), propertyName);
|
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), propertyName);
|
||||||
var attribute = new MinLengthAttribute(2) { ErrorMessage = message };
|
var attribute = new MinLengthAttribute(2) { ErrorMessage = message };
|
||||||
var adapter = new MinLengthAttributeAdapter(attribute);
|
var adapter = new MinLengthAttributeAdapter(attribute);
|
||||||
var context = new ClientModelValidationContext(metadata, provider);
|
var serviceCollection = new ServiceCollection();
|
||||||
|
var requestServices = serviceCollection.BuildServiceProvider();
|
||||||
|
var context = new ClientModelValidationContext(metadata, provider, requestServices);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
var rules = adapter.GetClientValidationRules(context);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNet.Testing;
|
using Microsoft.AspNet.Testing;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
@ -18,7 +20,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
|
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
|
||||||
var attribute = new RangeAttribute(typeof(decimal), "0", "100");
|
var attribute = new RangeAttribute(typeof(decimal), "0", "100");
|
||||||
var adapter = new RangeAttributeAdapter(attribute);
|
var adapter = new RangeAttributeAdapter(attribute);
|
||||||
var context = new ClientModelValidationContext(metadata, provider);
|
var serviceCollection = new ServiceCollection();
|
||||||
|
var requestServices = serviceCollection.BuildServiceProvider();
|
||||||
|
var context = new ClientModelValidationContext(metadata, provider, requestServices);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
var rules = adapter.GetClientValidationRules(context);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNet.Testing;
|
using Microsoft.AspNet.Testing;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
@ -19,7 +21,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
|
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
|
||||||
var attribute = new RequiredAttribute();
|
var attribute = new RequiredAttribute();
|
||||||
var adapter = new RequiredAttributeAdapter(attribute);
|
var adapter = new RequiredAttributeAdapter(attribute);
|
||||||
var context = new ClientModelValidationContext(metadata, provider);
|
var serviceCollection = new ServiceCollection();
|
||||||
|
var requestServices = serviceCollection.BuildServiceProvider();
|
||||||
|
var context = new ClientModelValidationContext(metadata, provider, requestServices);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
var rules = adapter.GetClientValidationRules(context);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Microsoft.AspNet.Testing;
|
using Microsoft.AspNet.Testing;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
@ -18,7 +20,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
|
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
|
||||||
var attribute = new StringLengthAttribute(8);
|
var attribute = new StringLengthAttribute(8);
|
||||||
var adapter = new StringLengthAttributeAdapter(attribute);
|
var adapter = new StringLengthAttributeAdapter(attribute);
|
||||||
var context = new ClientModelValidationContext(metadata, provider);
|
var serviceCollection = new ServiceCollection();
|
||||||
|
var requestServices = serviceCollection.BuildServiceProvider();
|
||||||
|
var context = new ClientModelValidationContext(metadata, provider, requestServices);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
var rules = adapter.GetClientValidationRules(context);
|
||||||
|
|
@ -40,7 +44,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
|
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
|
||||||
var attribute = new StringLengthAttribute(10) { MinimumLength = 3 };
|
var attribute = new StringLengthAttribute(10) { MinimumLength = 3 };
|
||||||
var adapter = new StringLengthAttributeAdapter(attribute);
|
var adapter = new StringLengthAttributeAdapter(attribute);
|
||||||
var context = new ClientModelValidationContext(metadata, provider);
|
var serviceCollection = new ServiceCollection();
|
||||||
|
var requestServices = serviceCollection.BuildServiceProvider();
|
||||||
|
var context = new ClientModelValidationContext(metadata, provider, requestServices);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var rules = adapter.GetClientValidationRules(context);
|
var rules = adapter.GetClientValidationRules(context);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue