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.Runtime.CompilerServices;
|
||||
using System;
|
||||
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
|
||||
{
|
||||
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", Justification = "The value is a not a regular URL since it may contain ~/ ASP.NET-specific characters")]
|
||||
public ModelClientValidationRemoteRule(string errorMessage, string url, string httpMethod, string additionalFields)
|
||||
{
|
||||
ErrorMessage = errorMessage;
|
||||
ValidationType = "remote";
|
||||
ValidationParameters["url"] = url;
|
||||
private const string RemoteValidationType = "remote";
|
||||
private const string AdditionalFieldsValidationParameter = "additionalfields";
|
||||
private const string TypeValidationParameter = "type";
|
||||
private const string UrlValidationParameter = "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);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
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.ComponentModel.DataAnnotations;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Web.Mvc.Properties;
|
||||
using System.Web.Routing;
|
||||
using System.Linq;
|
||||
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)]
|
||||
[SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments", Justification = "The constructor parameters are used to feed RouteData, which is public.")]
|
||||
[SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes", Justification = "This attribute is designed to be a base class for other attributes.")]
|
||||
public class RemoteAttribute : ValidationAttribute, IClientValidatable
|
||||
/// <summary>
|
||||
/// A <see cref="ValidationAttribute"/> which configures Unobtrusive validation to send an Ajax request to the
|
||||
/// web site. The invoked action should return JSON indicating whether the value is valid.
|
||||
/// </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[] _additonalFieldsSplit = new string[0];
|
||||
private string _additionalFields = string.Empty;
|
||||
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()
|
||||
: base(MvcResources.RemoteAttribute_RemoteValidationFailed)
|
||||
: base(Resources.RemoteAttribute_RemoteValidationFailed)
|
||||
{
|
||||
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)
|
||||
: this()
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(routeName))
|
||||
{
|
||||
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "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)
|
||||
:
|
||||
this(action, controller, null /* areaName */)
|
||||
{
|
||||
}
|
||||
|
||||
public RemoteAttribute(string action, string controller, string areaName)
|
||||
: this()
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(action))
|
||||
if (action != null)
|
||||
{
|
||||
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "action");
|
||||
}
|
||||
if (String.IsNullOrWhiteSpace(controller))
|
||||
{
|
||||
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controller");
|
||||
RouteData["action"] = action;
|
||||
}
|
||||
|
||||
RouteData["controller"] = controller;
|
||||
RouteData["action"] = action;
|
||||
|
||||
if (!String.IsNullOrWhiteSpace(areaName))
|
||||
if (controller != null)
|
||||
{
|
||||
RouteData["area"] = areaName;
|
||||
RouteData["controller"] = controller;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RemoteAttribute"/> class.
|
||||
/// </summary>
|
||||
/// <param name="action">The route name.</param>
|
||||
/// <param name="controller">The name of the controller.</param>
|
||||
/// <param name="areaReference">
|
||||
/// Find the controller in the root if <see cref="AreaReference.UseRoot"/>. Otherwise look in the current area.
|
||||
/// <param name="action">
|
||||
/// The action name used when generating the URL where client should send a validation request.
|
||||
/// </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)
|
||||
{
|
||||
if (areaReference == AreaReference.UseRoot)
|
||||
{
|
||||
RouteData["area"] = null;
|
||||
}
|
||||
RouteData["area"] = areaName;
|
||||
}
|
||||
|
||||
/// <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; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the comma-separated names of fields the client should include in a validation request.
|
||||
/// </summary>
|
||||
public string AdditionalFields
|
||||
{
|
||||
get { return _additionalFields ?? String.Empty; }
|
||||
get { return _additionalFields; }
|
||||
set
|
||||
{
|
||||
_additionalFields = value;
|
||||
_additonalFieldsSplit = AuthorizeAttribute.SplitString(value);
|
||||
_additionalFields = value ?? string.Empty;
|
||||
_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 virtual RouteCollection Routes
|
||||
{
|
||||
get { return RouteTable.Routes; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Formats <paramref name="property"/> and <see cref="AdditionalFields"/> for use in generated HTML.
|
||||
/// </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)
|
||||
{
|
||||
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);
|
||||
|
||||
foreach (string field in _additonalFieldsSplit)
|
||||
var delimitedAdditionalFields = string.Join(",", _additionalFieldsSplit);
|
||||
if (!string.IsNullOrEmpty(delimitedAdditionalFields))
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (String.IsNullOrEmpty(property))
|
||||
if (string.IsNullOrEmpty(property))
|
||||
{
|
||||
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "property");
|
||||
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, "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")]
|
||||
protected virtual string GetUrl(ControllerContext controllerContext)
|
||||
/// <summary>
|
||||
/// 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,
|
||||
RouteName,
|
||||
RouteData);
|
||||
|
||||
if (pathData == null)
|
||||
var urlHelper = context.RequestServices.GetRequiredService<IUrlHelper>();
|
||||
var url = urlHelper.RouteUrl(RouteName, values: RouteData, protocol: null, host: null, fragment: null);
|
||||
if (url == 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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
var validatorProvider = _bindingContextAccessor.Value.ValidatorProvider;
|
||||
|
||||
metadata = metadata ??
|
||||
ExpressionMetadataProvider.FromStringExpression(name, viewContext.ViewData, _metadataProvider);
|
||||
var validationContext =
|
||||
new ClientModelValidationContext(metadata, _metadataProvider, viewContext.HttpContext.RequestServices);
|
||||
|
||||
return
|
||||
validatorProvider
|
||||
return validatorProvider
|
||||
.GetValidators(metadata)
|
||||
.OfType<IClientModelValidator>()
|
||||
.SelectMany(v => v.GetClientValidationRules(
|
||||
new ClientModelValidationContext(metadata, _metadataProvider)));
|
||||
.SelectMany(v => v.GetClientValidationRules(validationContext));
|
||||
}
|
||||
|
||||
internal static string EvalString(ViewContext viewContext, string key, string format)
|
||||
|
|
|
|||
|
|
@ -445,4 +445,10 @@
|
|||
<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>
|
||||
</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>
|
||||
|
|
@ -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 readonly IPropertyBindingPredicateProvider[] _providers;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,25 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class ClientModelValidationContext
|
||||
{
|
||||
public ClientModelValidationContext([NotNull] ModelMetadata metadata,
|
||||
[NotNull] IModelMetadataProvider metadataProvider)
|
||||
[NotNull] IModelMetadataProvider metadataProvider,
|
||||
[NotNull] IServiceProvider requestServices)
|
||||
{
|
||||
ModelMetadata = metadata;
|
||||
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(
|
||||
[NotNull] ClientModelValidationContext context)
|
||||
{
|
||||
var customValidator = Attribute as IClientModelValidator;
|
||||
if (customValidator != null)
|
||||
{
|
||||
return customValidator.GetClientValidationRules(context);
|
||||
}
|
||||
|
||||
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.Linq;
|
||||
using System.Web.Routing;
|
||||
using Microsoft.TestCommon;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.Http.Core;
|
||||
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 Xunit;
|
||||
|
||||
namespace System.Web.Mvc.Test
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
public class RemoteAttributeTest
|
||||
{
|
||||
// Good route name, bad route name
|
||||
// Controller + Action
|
||||
private static readonly IModelMetadataProvider _metadataProvider = new EmptyModelMetadataProvider();
|
||||
private static readonly ModelMetadata _metadata = _metadataProvider.GetMetadataForProperty(
|
||||
modelAccessor: null,
|
||||
containerType: typeof(string),
|
||||
propertyName: "Length");
|
||||
|
||||
[Fact]
|
||||
public void GuardClauses()
|
||||
public static TheoryData<string> SomeNames
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.ThrowsArgumentNullOrEmpty(
|
||||
() => new RemoteAttribute(null, "controller"),
|
||||
"action");
|
||||
Assert.ThrowsArgumentNullOrEmpty(
|
||||
() => new RemoteAttribute("action", null),
|
||||
"controller");
|
||||
Assert.ThrowsArgumentNullOrEmpty(
|
||||
() => new RemoteAttribute(null),
|
||||
"routeName");
|
||||
Assert.ThrowsArgumentNullOrEmpty(
|
||||
() => RemoteAttribute.FormatPropertyForClientValidation(String.Empty),
|
||||
"property");
|
||||
Assert.ThrowsArgumentNullOrEmpty(
|
||||
() => new RemoteAttribute("foo").FormatAdditionalFieldsForClientValidation(String.Empty),
|
||||
"property");
|
||||
get
|
||||
{
|
||||
return new TheoryData<string>
|
||||
{
|
||||
string.Empty,
|
||||
"Action",
|
||||
"In a controller",
|
||||
" slightly\t odd\t whitespace\t\r\n",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Null or empty property names are invalid. (Those containing just whitespace are legal.)
|
||||
public static TheoryData<string> NullOrEmptyNames
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<string>
|
||||
{
|
||||
null,
|
||||
string.Empty,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -43,408 +60,583 @@ namespace System.Web.Mvc.Test
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void BadRouteNameThrows()
|
||||
public void Constructor_WithNullAction_IgnoresArgument()
|
||||
{
|
||||
// Arrange
|
||||
ControllerContext context = new ControllerContext();
|
||||
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();
|
||||
// Arrange & Act
|
||||
var attribute = new TestableRemoteAttribute(action: null, controller: "AController");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("remote", rule.ValidationType);
|
||||
Assert.Equal("'Length' is invalid.", rule.ErrorMessage);
|
||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||
Assert.Equal("/my/url", rule.ValidationParameters["url"]);
|
||||
var keyValuePair = Assert.Single(attribute.RouteData);
|
||||
Assert.Equal(keyValuePair.Key, "controller");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ActionControllerReturnsCorrectClientDataWithoutNamedParameters()
|
||||
public void Constructor_WithNullController_IgnoresArgument()
|
||||
{
|
||||
// Arrange
|
||||
string url = 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();
|
||||
// Arrange & Act
|
||||
var attribute = new TestableRemoteAttribute("AnAction", controller: null);
|
||||
|
||||
// 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("'Length' is invalid.", rule.ErrorMessage);
|
||||
|
||||
Assert.Equal(2, rule.ValidationParameters.Count);
|
||||
Assert.Equal("/Controller/Action", rule.ValidationParameters["url"]);
|
||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
||||
Assert.Throws<KeyNotFoundException>(
|
||||
() => rule.ValidationParameters["type"],
|
||||
"The given key was not present in the dictionary.");
|
||||
Assert.Equal(url, rule.ValidationParameters["url"]);
|
||||
|
||||
var routeDictionary = Assert.IsType<RouteValueDictionary>(urlHelper.RouteValues);
|
||||
Assert.Empty(routeDictionary);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ActionControllerReturnsCorrectClientDataWithNamedParameters()
|
||||
public void GetClientValidationRules_WithActionController_CallsUrlHelperWithExpectedValues()
|
||||
{
|
||||
// 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");
|
||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller");
|
||||
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
|
||||
// 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(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("/Controller/Action", rule.ValidationParameters["url"]);
|
||||
Assert.Equal("*.Length,*.Password,*.ConfirmPassword", rule.ValidationParameters["additionalfields"]);
|
||||
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]
|
||||
public void ActionController_RemoteFindsControllerInCurrentArea()
|
||||
public void GetClientValidationRules_WithActionControllerArea_CallsUrlHelperWithExpectedValues()
|
||||
{
|
||||
// Arrange
|
||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
|
||||
containerType: typeof(string), propertyName: "Length");
|
||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller");
|
||||
attribute.HttpMethod = "POST";
|
||||
var attribute = new RemoteAttribute("Action", "Controller", "Test")
|
||||
{
|
||||
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);
|
||||
context.MapRoute(name: null, url: "Test/{controller}/{action}");
|
||||
|
||||
attribute.RouteTable.Add(new Route("{controller}/{action}", new MvcRouteHandler()));
|
||||
|
||||
// Act
|
||||
ModelClientValidationRule rule =
|
||||
attribute.GetClientValidationRules(metadata, GetMockControllerContext(url: null)).Single();
|
||||
|
||||
// Assert
|
||||
// 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("*.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"]);
|
||||
}
|
||||
|
||||
// Test area is current in this case.
|
||||
[Fact]
|
||||
public void ActionControllerArea_RemoteFindsControllerInNamedArea()
|
||||
public void GetClientValidationRules_WithActionControllerInArea_FindsControllerInCurrentArea()
|
||||
{
|
||||
// Arrange
|
||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
|
||||
containerType: typeof(string), propertyName: "Length");
|
||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", "Test");
|
||||
attribute.HttpMethod = "POST";
|
||||
var attribute = new RemoteAttribute("Action", "Controller");
|
||||
var context = GetValidationContextWithArea(currentArea: "Test");
|
||||
|
||||
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, GetMockControllerContext(url: null)).Single();
|
||||
|
||||
// Assert
|
||||
// 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("/Test/Controller/Action", rule.ValidationParameters["url"]);
|
||||
}
|
||||
|
||||
// Current area is root in this case.
|
||||
[Fact]
|
||||
public void ActionControllerArea_WithEmptyArea_RemoteFindsControllerInCurrentArea()
|
||||
// Explicit reference to the (current) root area.
|
||||
[Theory]
|
||||
[MemberData(nameof(NullOrEmptyNames))]
|
||||
public void GetClientValidationRules_WithActionControllerArea_FindsControllerInRootArea(string areaName)
|
||||
{
|
||||
// Arrange
|
||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
|
||||
containerType: typeof(string), propertyName: "Length");
|
||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", "");
|
||||
attribute.HttpMethod = "POST";
|
||||
var attribute = new RemoteAttribute("Action", "Controller", areaName);
|
||||
var context = GetValidationContextWithArea(currentArea: null);
|
||||
|
||||
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, GetMockControllerContext(url: null)).Single();
|
||||
|
||||
// Assert
|
||||
// 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"]);
|
||||
}
|
||||
|
||||
// Current area is root in this case.
|
||||
[Fact]
|
||||
public void ActionControllerAreaReference_WithUseCurrent_RemoteFindsControllerInCurrentArea()
|
||||
// Test area is current in this case.
|
||||
[Theory]
|
||||
[MemberData(nameof(NullOrEmptyNames))]
|
||||
public void GetClientValidationRules_WithActionControllerAreaInArea_FindsControllerInRootArea(string areaName)
|
||||
{
|
||||
// Arrange
|
||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
|
||||
containerType: typeof(string), propertyName: "Length");
|
||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", AreaReference.UseCurrent);
|
||||
attribute.HttpMethod = "POST";
|
||||
var attribute = new RemoteAttribute("Action", "Controller", areaName);
|
||||
var context = GetValidationContextWithArea(currentArea: "Test");
|
||||
|
||||
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, GetMockControllerContext(url: null)).Single();
|
||||
|
||||
// Assert
|
||||
// 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"]);
|
||||
}
|
||||
|
||||
// Root area is current in this case.
|
||||
[Fact]
|
||||
public void ActionControllerAreaReference_WithUseRoot_RemoteFindsControllerInRoot()
|
||||
public void GetClientValidationRules_WithActionControllerArea_FindsControllerInNamedArea()
|
||||
{
|
||||
// Arrange
|
||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
|
||||
containerType: typeof(string), propertyName: "Length");
|
||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", AreaReference.UseRoot);
|
||||
attribute.HttpMethod = "POST";
|
||||
var attribute = new RemoteAttribute("Action", "Controller", "Test");
|
||||
var context = GetValidationContextWithArea(currentArea: null);
|
||||
|
||||
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, GetMockControllerContext(url: null)).Single();
|
||||
|
||||
// Assert
|
||||
// Act & Assert
|
||||
var rule = Assert.Single(attribute.GetClientValidationRules(context));
|
||||
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.
|
||||
[Fact]
|
||||
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(2, rule.ValidationParameters.Count);
|
||||
Assert.Equal("*.Length", rule.ValidationParameters["additionalfields"]);
|
||||
Assert.Equal("/Test/Controller/Action", rule.ValidationParameters["url"]);
|
||||
}
|
||||
|
||||
// Explicit reference to the Test area.
|
||||
// Explicit reference to the current (Test) area.
|
||||
[Fact]
|
||||
public void ActionControllerArea_InSameArea_RemoteFindsControllerInNamedArea()
|
||||
public void GetClientValidationRules_WithActionControllerAreaInArea_FindsControllerInNamedArea()
|
||||
{
|
||||
// Arrange
|
||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
|
||||
containerType: typeof(string), propertyName: "Length");
|
||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", "Test");
|
||||
attribute.HttpMethod = "POST";
|
||||
var attribute = new RemoteAttribute("Action", "Controller", "Test");
|
||||
var context = GetValidationContextWithArea(currentArea: "Test");
|
||||
|
||||
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
|
||||
// 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("/Test/Controller/Action", rule.ValidationParameters["url"]);
|
||||
}
|
||||
|
||||
// Test area is current in this case.
|
||||
[Fact]
|
||||
public void ActionControllerArea_InArea_RemoteFindsControllerInNamedArea()
|
||||
public void GetClientValidationRules_WithActionControllerAreaInArea_FindsControllerInDifferentArea()
|
||||
{
|
||||
// Arrange
|
||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
|
||||
containerType: typeof(string), propertyName: "Length");
|
||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", "AnotherArea");
|
||||
attribute.HttpMethod = "POST";
|
||||
var attribute = new RemoteAttribute("Action", "Controller", "AnotherArea");
|
||||
var context = GetValidationContextWithArea(currentArea: "Test");
|
||||
|
||||
var context = new AreaRegistrationContext("Test", attribute.RouteTable);
|
||||
context.MapRoute(name: null, url: "Test/{controller}/{action}");
|
||||
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
|
||||
// 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("/AnotherArea/Controller/Action", rule.ValidationParameters["url"]);
|
||||
}
|
||||
|
||||
// Current area is Test in this case.
|
||||
[Fact]
|
||||
public void ActionControllerArea_WithEmptyAreaInArea_RemoteFindsControllerInCurrentArea()
|
||||
private static ClientModelValidationContext GetValidationContext(IUrlHelper urlHelper)
|
||||
{
|
||||
// Arrange
|
||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
|
||||
containerType: typeof(string), propertyName: "Length");
|
||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", "");
|
||||
attribute.HttpMethod = "POST";
|
||||
var serviceCollection = GetServiceCollection();
|
||||
serviceCollection.AddInstance<IUrlHelper>(urlHelper);
|
||||
var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
|
||||
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"]);
|
||||
return new ClientModelValidationContext(_metadata, _metadataProvider, serviceProvider);
|
||||
}
|
||||
|
||||
// Current area is Test in this case.
|
||||
[Fact]
|
||||
public void ActionControllerAreaReference_WithUseCurrentInArea_RemoteFindsControllerInCurrentArea()
|
||||
private static ClientModelValidationContext GetValidationContextWithArea(string currentArea)
|
||||
{
|
||||
// Arrange
|
||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
|
||||
containerType: typeof(string), propertyName: "Length");
|
||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", AreaReference.UseCurrent);
|
||||
attribute.HttpMethod = "POST";
|
||||
var serviceCollection = GetServiceCollection();
|
||||
var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
var routeCollection = GetRouteCollectionWithArea(serviceProvider);
|
||||
var routeData = new RouteData
|
||||
{
|
||||
Routers =
|
||||
{
|
||||
routeCollection,
|
||||
},
|
||||
Values =
|
||||
{
|
||||
{ "action", "Index" },
|
||||
{ "controller", "Home" },
|
||||
},
|
||||
};
|
||||
if (!string.IsNullOrEmpty(currentArea))
|
||||
{
|
||||
routeData.Values["area"] = currentArea;
|
||||
}
|
||||
|
||||
var context = new AreaRegistrationContext("Test", attribute.RouteTable);
|
||||
context.MapRoute(name: null, url: "Test/{controller}/{action}");
|
||||
var contextAccessor = GetContextAccessor(serviceProvider, routeData);
|
||||
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()));
|
||||
|
||||
// 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"]);
|
||||
return new ClientModelValidationContext(_metadata, _metadataProvider, serviceProvider);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ActionControllerAreaReference_WithUseRootInArea_RemoteFindsControllerInRoot()
|
||||
private static ClientModelValidationContext GetValidationContextWithNoController()
|
||||
{
|
||||
// Arrange
|
||||
ModelMetadata metadata = ModelMetadataProviders.Current.GetMetadataForProperty(modelAccessor: null,
|
||||
containerType: typeof(string), propertyName: "Length");
|
||||
TestableRemoteAttribute attribute = new TestableRemoteAttribute("Action", "Controller", AreaReference.UseRoot);
|
||||
attribute.HttpMethod = "POST";
|
||||
var serviceCollection = GetServiceCollection();
|
||||
var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
var routeCollection = GetRouteCollectionWithNoController(serviceProvider);
|
||||
var routeData = new RouteData
|
||||
{
|
||||
Routers =
|
||||
{
|
||||
routeCollection,
|
||||
},
|
||||
};
|
||||
|
||||
var context = new AreaRegistrationContext("Test", attribute.RouteTable);
|
||||
context.MapRoute(name: null, url: "Test/{controller}/{action}");
|
||||
var contextAccessor = GetContextAccessor(serviceProvider, routeData);
|
||||
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()));
|
||||
|
||||
// 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"]);
|
||||
return new ClientModelValidationContext(_metadata, _metadataProvider, serviceProvider);
|
||||
}
|
||||
|
||||
private ControllerContext GetMockControllerContext(string url)
|
||||
private static IRouter GetRouteCollectionWithArea(IServiceProvider serviceProvider)
|
||||
{
|
||||
Mock<ControllerContext> context = new Mock<ControllerContext>();
|
||||
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);
|
||||
var builder = GetRouteBuilder(serviceProvider, isBound: true);
|
||||
|
||||
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>();
|
||||
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);
|
||||
var builder = GetRouteBuilder(serviceProvider, isBound: false);
|
||||
builder.MapRoute("default", "static/route");
|
||||
|
||||
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
|
||||
{
|
||||
public RouteCollection RouteTable = new RouteCollection();
|
||||
|
||||
public TestableRemoteAttribute(string action, string controller, AreaReference areaReference)
|
||||
: base(action, controller, areaReference)
|
||||
{
|
||||
}
|
||||
|
||||
public TestableRemoteAttribute(string action, string controller, string areaName)
|
||||
: base(action, controller, areaName)
|
||||
public TestableRemoteAttribute(string routeName)
|
||||
: base(routeName)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -453,14 +645,25 @@ namespace System.Web.Mvc.Test
|
|||
{
|
||||
}
|
||||
|
||||
public TestableRemoteAttribute(string routeName)
|
||||
: base(routeName)
|
||||
public TestableRemoteAttribute(string action, string controller, string areaName)
|
||||
: 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.Core.Test
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
public class UrlHelperTest
|
||||
{
|
||||
|
|
@ -695,7 +695,7 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
|||
private static UrlHelper CreateUrlHelperWithRouteCollection(string appPrefix)
|
||||
{
|
||||
var routeCollection = GetRouter();
|
||||
return CreateUrlHelper("/app", routeCollection);
|
||||
return CreateUrlHelper(appPrefix, routeCollection);
|
||||
}
|
||||
|
||||
private static IRouter GetRouter()
|
||||
|
|
@ -708,13 +708,9 @@ namespace Microsoft.AspNet.Mvc.Core.Test
|
|||
var rt = new RouteBuilder();
|
||||
var target = new Mock<IRouter>(MockBehavior.Strict);
|
||||
target
|
||||
.Setup(e => e.GetVirtualPath(It.IsAny<VirtualPathContext>()))
|
||||
.Callback<VirtualPathContext>(c =>
|
||||
{
|
||||
rt.ToString();
|
||||
c.IsBound = true;
|
||||
})
|
||||
.Returns<VirtualPathContext>(rc => null);
|
||||
.Setup(router => router.GetVirtualPath(It.IsAny<VirtualPathContext>()))
|
||||
.Callback<VirtualPathContext>(context => context.IsBound = true)
|
||||
.Returns<VirtualPathContext>(context => null);
|
||||
rt.DefaultHandler = target.Object;
|
||||
var serviceProviderMock = new Mock<IServiceProvider>();
|
||||
var accessorMock = new Mock<IOptions<RouteOptions>>();
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Testing;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
|
|
@ -17,7 +19,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
var metadata = metadataProvider.GetMetadataForProperty(() => null, typeof(PropertyDisplayNameModel), "MyProperty");
|
||||
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);
|
||||
|
||||
// Act
|
||||
|
|
@ -36,7 +40,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||
var metadata = metadataProvider.GetMetadataForProperty(() => null, typeof(PropertyNameModel), "MyProperty");
|
||||
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);
|
||||
|
||||
// Act
|
||||
|
|
@ -57,7 +63,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
{
|
||||
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);
|
||||
|
||||
// Act
|
||||
|
|
@ -85,7 +93,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
ErrorMessageResourceName = "CompareAttributeTestResource",
|
||||
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);
|
||||
|
||||
// Act
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
// 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.ComponentModel.DataAnnotations;
|
||||
#if ASPNET50
|
||||
using System.Linq;
|
||||
#endif
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||
#if ASPNET50
|
||||
using Moq;
|
||||
using Moq.Protected;
|
||||
#endif
|
||||
|
|
@ -196,6 +201,51 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
#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]
|
||||
public void IsRequiredTests()
|
||||
{
|
||||
|
|
@ -221,5 +271,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
{
|
||||
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 Microsoft.AspNet.Testing;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
|
|
@ -18,7 +20,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
|
||||
var attribute = new MaxLengthAttribute(10);
|
||||
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
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
|
|
@ -42,7 +46,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), propertyName);
|
||||
var attribute = new MaxLengthAttribute(5) { ErrorMessage = message };
|
||||
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
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Testing;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
|
|
@ -18,7 +20,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
|
||||
var attribute = new MinLengthAttribute(6);
|
||||
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
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
|
|
@ -42,7 +46,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), propertyName);
|
||||
var attribute = new MinLengthAttribute(2) { ErrorMessage = message };
|
||||
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
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Testing;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
|
|
@ -18,7 +20,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
|
||||
var attribute = new RangeAttribute(typeof(decimal), "0", "100");
|
||||
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
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Testing;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
|
|
@ -19,7 +21,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
|
||||
var attribute = new RequiredAttribute();
|
||||
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
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNet.Testing;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
|
|
@ -18,7 +20,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
|
||||
var attribute = new StringLengthAttribute(8);
|
||||
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
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
|
|
@ -40,7 +44,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var metadata = provider.GetMetadataForProperty(() => null, typeof(string), "Length");
|
||||
var attribute = new StringLengthAttribute(10) { MinimumLength = 3 };
|
||||
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
|
||||
var rules = adapter.GetClientValidationRules(context);
|
||||
|
|
|
|||
Loading…
Reference in New Issue