Rewrite of validation
This commit is contained in:
parent
0889b18f95
commit
8a502dbe5d
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
|
|
@ -59,6 +60,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
ModelState = modelState,
|
ModelState = modelState,
|
||||||
OperationBindingContext = operationBindingContext,
|
OperationBindingContext = operationBindingContext,
|
||||||
ValueProvider = operationBindingContext.ValueProvider,
|
ValueProvider = operationBindingContext.ValueProvider,
|
||||||
|
|
||||||
|
ValidationState = new ValidationStateDictionary(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,6 +77,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
ModelState = parent.ModelState,
|
ModelState = parent.ModelState,
|
||||||
OperationBindingContext = parent.OperationBindingContext,
|
OperationBindingContext = parent.OperationBindingContext,
|
||||||
ValueProvider = parent.ValueProvider,
|
ValueProvider = parent.ValueProvider,
|
||||||
|
ValidationState = parent.ValidationState,
|
||||||
|
|
||||||
Model = model,
|
Model = model,
|
||||||
ModelMetadata = modelMetadata,
|
ModelMetadata = modelMetadata,
|
||||||
|
|
@ -174,5 +178,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
/// is eligible for model binding.
|
/// is eligible for model binding.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<ModelBindingContext, string, bool> PropertyFilter { get; set; }
|
public Func<ModelBindingContext, string, bool> PropertyFilter { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the <see cref="ValidationStateDictionary"/>. Used for tracking validation state to
|
||||||
|
/// customize validation behavior for a model object.
|
||||||
|
/// </summary>
|
||||||
|
public ValidationStateDictionary ValidationState { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
/// <returns>A <see cref="ModelBindingResult"/> representing a failed model binding operation.</returns>
|
/// <returns>A <see cref="ModelBindingResult"/> representing a failed model binding operation.</returns>
|
||||||
public static ModelBindingResult Failed([NotNull] string key)
|
public static ModelBindingResult Failed([NotNull] string key)
|
||||||
{
|
{
|
||||||
return new ModelBindingResult(key, model: null, isModelSet: false, validationNode: null);
|
return new ModelBindingResult(key, model: null, isModelSet: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -49,14 +49,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key">The key of the current model binding operation.</param>
|
/// <param name="key">The key of the current model binding operation.</param>
|
||||||
/// <param name="model">The model value. May be <c>null.</c></param>
|
/// <param name="model">The model value. May be <c>null.</c></param>
|
||||||
/// <param name="validationNode">The <see cref="ModelValidationNode"/>. May be <c>null</c>.</param>
|
|
||||||
/// <returns>A <see cref="ModelBindingResult"/> representing a successful model bind.</returns>
|
/// <returns>A <see cref="ModelBindingResult"/> representing a successful model bind.</returns>
|
||||||
public static ModelBindingResult Success(
|
public static ModelBindingResult Success(
|
||||||
[NotNull] string key,
|
[NotNull] string key,
|
||||||
object model,
|
object model)
|
||||||
ModelValidationNode validationNode)
|
|
||||||
{
|
{
|
||||||
return new ModelBindingResult(key, model, isModelSet: true, validationNode: validationNode);
|
return new ModelBindingResult(key, model, isModelSet: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -65,22 +63,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="key">The key of the current model binding operation.</param>
|
/// <param name="key">The key of the current model binding operation.</param>
|
||||||
/// <param name="model">The model value. May be <c>null.</c></param>
|
/// <param name="model">The model value. May be <c>null.</c></param>
|
||||||
/// <param name="validationNode">The <see cref="ModelValidationNode"/>. May be <c>null</c>.</param>
|
|
||||||
/// <returns>A completed <see cref="Task{ModelBindingResult}"/> representing a successful model bind.</returns>
|
/// <returns>A completed <see cref="Task{ModelBindingResult}"/> representing a successful model bind.</returns>
|
||||||
public static Task<ModelBindingResult> SuccessAsync(
|
public static Task<ModelBindingResult> SuccessAsync(
|
||||||
[NotNull] string key,
|
[NotNull] string key,
|
||||||
object model,
|
object model)
|
||||||
ModelValidationNode validationNode)
|
|
||||||
{
|
{
|
||||||
return Task.FromResult(Success(key, model, validationNode));
|
return Task.FromResult(Success(key, model));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ModelBindingResult(string key, object model, bool isModelSet, ModelValidationNode validationNode)
|
private ModelBindingResult(string key, object model, bool isModelSet)
|
||||||
{
|
{
|
||||||
Key = key;
|
Key = key;
|
||||||
Model = model;
|
Model = model;
|
||||||
IsModelSet = isModelSet;
|
IsModelSet = isModelSet;
|
||||||
ValidationNode = validationNode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -109,11 +104,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsModelSet { get; }
|
public bool IsModelSet { get; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A <see cref="ModelValidationNode"/> associated with the current <see cref="ModelBindingResult"/>.
|
|
||||||
/// </summary>
|
|
||||||
public ModelValidationNode ValidationNode { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
|
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
|
@ -14,6 +15,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A metadata representation of a model type, property or parameter.
|
/// A metadata representation of a model type, property or parameter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[DebuggerDisplay("{DebuggerToString(),nq}")]
|
||||||
public abstract class ModelMetadata
|
public abstract class ModelMetadata
|
||||||
{
|
{
|
||||||
private bool? _isComplexType;
|
private bool? _isComplexType;
|
||||||
|
|
@ -407,5 +409,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
return DisplayName ?? PropertyName ?? ModelType.Name;
|
return DisplayName ?? PropertyName ?? ModelType.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string DebuggerToString()
|
||||||
|
{
|
||||||
|
if (Identity.MetadataKind == ModelMetadataKind.Type)
|
||||||
|
{
|
||||||
|
return $"ModelMetadata (Type: '{ModelType.Name}')";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return $"ModelMetadata (Property: '{ContainerType.Name}.{PropertyName}' Type: '{ModelType.Name}')";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,76 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.Framework.Internal;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Captures the validation information for a particular model.
|
|
||||||
/// </summary>
|
|
||||||
public class ModelValidationNode
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new instance of <see cref="ModelValidationNode"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">The key that will be used by the validation system to find <see cref="ModelState"/>
|
|
||||||
/// entries.</param>
|
|
||||||
/// <param name="modelMetadata">The <see cref="ModelMetadata"/> for the <paramref name="model"/>.</param>
|
|
||||||
/// <param name="model">The model object which is to be validated.</param>
|
|
||||||
public ModelValidationNode([NotNull] string key, [NotNull] ModelMetadata modelMetadata, object model)
|
|
||||||
: this (key, modelMetadata, model, new List<ModelValidationNode>())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a new instance of <see cref="ModelValidationNode"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="key">The key that will be used by the validation system to add
|
|
||||||
/// <see cref="ModelStateDictionary"/> entries.</param>
|
|
||||||
/// <param name="modelMetadata">The <see cref="ModelMetadata"/> for the <paramref name="model"/>.</param>
|
|
||||||
/// <param name="model">The model object which will be validated.</param>
|
|
||||||
/// <param name="childNodes">A collection of child nodes.</param>
|
|
||||||
public ModelValidationNode(
|
|
||||||
[NotNull] string key,
|
|
||||||
[NotNull] ModelMetadata modelMetadata,
|
|
||||||
object model,
|
|
||||||
[NotNull] IList<ModelValidationNode> childNodes)
|
|
||||||
{
|
|
||||||
Key = key;
|
|
||||||
ModelMetadata = modelMetadata;
|
|
||||||
ChildNodes = childNodes;
|
|
||||||
Model = model;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the key used for adding <see cref="ModelStateDictionary"/> entries.
|
|
||||||
/// </summary>
|
|
||||||
public string Key { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the <see cref="ModelMetadata"/>.
|
|
||||||
/// </summary>
|
|
||||||
public ModelMetadata ModelMetadata { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the model instance which is to be validated.
|
|
||||||
/// </summary>
|
|
||||||
public object Model { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the child nodes.
|
|
||||||
/// </summary>
|
|
||||||
public IList<ModelValidationNode> ChildNodes { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value that indicates whether all properties of the model should be validated.
|
|
||||||
/// </summary>
|
|
||||||
public bool ValidateAllProperties { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets a value that indicates whether validation should be suppressed.
|
|
||||||
/// </summary>
|
|
||||||
public bool SuppressValidation { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines a strategy for enumerating the child entries of a model object which should be validated.
|
||||||
|
/// </summary>
|
||||||
|
public interface IValidationStrategy
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an <see cref="IEnumerator{ValidationEntry}"/> containing a <see cref="ValidationEntry"/> for
|
||||||
|
/// each child entry of the model object to be validated.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="metadata">The <see cref="ModelMetadata"/> associated with <paramref name="model"/>.</param>
|
||||||
|
/// <param name="key">The model prefix associated with <paramref name="model"/>.</param>
|
||||||
|
/// <param name="model">The model object.</param>
|
||||||
|
/// <returns>An <see cref="IEnumerator{ValidationEntry}"/>.</returns>
|
||||||
|
IEnumerator<ValidationEntry> GetChildren(ModelMetadata metadata, string key, object model);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,64 +1,26 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.Framework.Internal;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A context object for <see cref="IModelValidator"/>.
|
||||||
|
/// </summary>
|
||||||
public class ModelValidationContext
|
public class ModelValidationContext
|
||||||
{
|
{
|
||||||
public ModelValidationContext(
|
/// <summary>
|
||||||
[NotNull] ModelBindingContext bindingContext,
|
/// Gets or sets the model object.
|
||||||
[NotNull] ModelExplorer modelExplorer)
|
/// </summary>
|
||||||
: this(
|
public object Model { get; set; }
|
||||||
bindingContext.BindingSource,
|
|
||||||
bindingContext.OperationBindingContext.ValidatorProvider,
|
|
||||||
bindingContext.ModelState,
|
|
||||||
modelExplorer)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public ModelValidationContext(
|
|
||||||
BindingSource bindingSource,
|
|
||||||
[NotNull] IModelValidatorProvider validatorProvider,
|
|
||||||
[NotNull] ModelStateDictionary modelState,
|
|
||||||
[NotNull] ModelExplorer modelExplorer)
|
|
||||||
{
|
|
||||||
ModelState = modelState;
|
|
||||||
ValidatorProvider = validatorProvider;
|
|
||||||
ModelExplorer = modelExplorer;
|
|
||||||
BindingSource = bindingSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructs a new instance of the <see cref="ModelValidationContext"/> class using the
|
/// Gets or sets the model container object.
|
||||||
/// <paramref name="parentContext" /> and <paramref name="modelExplorer"/>.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="parentContext">Existing <see cref="ModelValidationContext"/>.</param>
|
public object Container { get; set; }
|
||||||
/// <param name="modelExplorer">
|
|
||||||
/// <see cref="ModelExplorer"/> associated with the new <see cref="ModelValidationContext"/>.
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
/// A new instance of the <see cref="ModelValidationContext"/> class using the
|
|
||||||
/// <paramref name="parentContext" /> and <paramref name="modelExplorer"/>.
|
|
||||||
/// </returns>
|
|
||||||
public static ModelValidationContext GetChildValidationContext(
|
|
||||||
[NotNull] ModelValidationContext parentContext,
|
|
||||||
[NotNull] ModelExplorer modelExplorer)
|
|
||||||
{
|
|
||||||
return new ModelValidationContext(
|
|
||||||
modelExplorer.Metadata.BindingSource,
|
|
||||||
parentContext.ValidatorProvider,
|
|
||||||
parentContext.ModelState,
|
|
||||||
modelExplorer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ModelExplorer ModelExplorer { get; }
|
/// <summary>
|
||||||
|
/// Gets or sets the <see cref="ModelMetadata"/> associated with <see cref="Model"/>.
|
||||||
public ModelStateDictionary ModelState { get; }
|
/// </summary>
|
||||||
|
public ModelMetadata Metadata { get; set; }
|
||||||
public BindingSource BindingSource { get; set; }
|
|
||||||
|
|
||||||
public IModelValidatorProvider ValidatorProvider { get; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains data needed for validating a child entry of a model object. See <see cref="IValidationStrategy"/>.
|
||||||
|
/// </summary>
|
||||||
|
public struct ValidationEntry
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="ValidationEntry"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="metadata">The <see cref="ModelMetadata"/> associated with <paramref name="model"/>.</param>
|
||||||
|
/// <param name="key">The model prefix associated with <paramref name="model"/>.</param>
|
||||||
|
/// <param name="model">The model object.</param>
|
||||||
|
public ValidationEntry(ModelMetadata metadata, string key, object model)
|
||||||
|
{
|
||||||
|
if (metadata == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(metadata));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
Metadata = metadata;
|
||||||
|
Key = key;
|
||||||
|
Model = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The model prefix associated with <see cref="Model"/>.
|
||||||
|
/// </summary>
|
||||||
|
public string Key { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="ModelMetadata"/> associated with <see cref="Model"/>.
|
||||||
|
/// </summary>
|
||||||
|
public ModelMetadata Metadata { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The model object.
|
||||||
|
/// </summary>
|
||||||
|
public object Model { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,179 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Used for tracking validation state to customize validation behavior for a model object.
|
||||||
|
/// </summary>
|
||||||
|
public class ValidationStateDictionary :
|
||||||
|
IDictionary<object, ValidationStateEntry>,
|
||||||
|
IReadOnlyDictionary<object, ValidationStateEntry>
|
||||||
|
{
|
||||||
|
private readonly Dictionary<object, ValidationStateEntry> _inner;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="ValidationStateDictionary"/>.
|
||||||
|
/// </summary>
|
||||||
|
public ValidationStateDictionary()
|
||||||
|
{
|
||||||
|
_inner = new Dictionary<object, ValidationStateEntry>(ReferenceEqualityComparer.Instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ValidationStateEntry this[object key]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
ValidationStateEntry entry;
|
||||||
|
TryGetValue(key, out entry);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_inner[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public int Count
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _inner.Count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IsReadOnly
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ((IDictionary<object, ValidationStateEntry>)_inner).IsReadOnly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ICollection<object> Keys
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ((IDictionary<object, ValidationStateEntry>)_inner).Keys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public ICollection<ValidationStateEntry> Values
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ((IDictionary<object, ValidationStateEntry>)_inner).Values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
IEnumerable<object> IReadOnlyDictionary<object, ValidationStateEntry>.Keys
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ((IReadOnlyDictionary<object, ValidationStateEntry>)_inner).Keys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
IEnumerable<ValidationStateEntry> IReadOnlyDictionary<object, ValidationStateEntry>.Values
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ((IReadOnlyDictionary<object, ValidationStateEntry>)_inner).Values;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Add(KeyValuePair<object, ValidationStateEntry> item)
|
||||||
|
{
|
||||||
|
((IDictionary<object, ValidationStateEntry>)_inner).Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Add(object key, ValidationStateEntry value)
|
||||||
|
{
|
||||||
|
_inner.Add(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
_inner.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool Contains(KeyValuePair<object, ValidationStateEntry> item)
|
||||||
|
{
|
||||||
|
return ((IDictionary<object, ValidationStateEntry>)_inner).Contains(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool ContainsKey(object key)
|
||||||
|
{
|
||||||
|
return _inner.ContainsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void CopyTo(KeyValuePair<object, ValidationStateEntry>[] array, int arrayIndex)
|
||||||
|
{
|
||||||
|
((IDictionary<object, ValidationStateEntry>)_inner).CopyTo(array, arrayIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IEnumerator<KeyValuePair<object, ValidationStateEntry>> GetEnumerator()
|
||||||
|
{
|
||||||
|
return ((IDictionary<object, ValidationStateEntry>)_inner).GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool Remove(KeyValuePair<object, ValidationStateEntry> item)
|
||||||
|
{
|
||||||
|
return _inner.Remove(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool Remove(object key)
|
||||||
|
{
|
||||||
|
return _inner.Remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool TryGetValue(object key, out ValidationStateEntry value)
|
||||||
|
{
|
||||||
|
return _inner.TryGetValue(key, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return ((IDictionary<object, ValidationStateEntry>)_inner).GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ReferenceEqualityComparer : IEqualityComparer<object>
|
||||||
|
{
|
||||||
|
public static readonly ReferenceEqualityComparer Instance = new ReferenceEqualityComparer();
|
||||||
|
|
||||||
|
public new bool Equals(object x, object y)
|
||||||
|
{
|
||||||
|
return Object.ReferenceEquals(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetHashCode(object obj)
|
||||||
|
{
|
||||||
|
return RuntimeHelpers.GetHashCode(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An entry in a <see cref="ValidationStateDictionary"/>. Records state information to override the default
|
||||||
|
/// behavior of validation for an object.
|
||||||
|
/// </summary>
|
||||||
|
public class ValidationStateEntry
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the model prefix associated with the entry.
|
||||||
|
/// </summary>
|
||||||
|
public string Key { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the <see cref="ModelMetadata"/> associated with the entry.
|
||||||
|
/// </summary>
|
||||||
|
public ModelMetadata Metadata { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether the associated model object should be validated.
|
||||||
|
/// </summary>
|
||||||
|
public bool SuppressValidation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets an <see cref="IValidationStrategy"/> for enumerating child entries of the associated
|
||||||
|
/// model object.
|
||||||
|
/// </summary>
|
||||||
|
public IValidationStrategy Strategy { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -82,20 +82,14 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
parameter.Name);
|
parameter.Name);
|
||||||
|
|
||||||
var modelBindingResult = await operationContext.ModelBinder.BindModelAsync(modelBindingContext);
|
var modelBindingResult = await operationContext.ModelBinder.BindModelAsync(modelBindingContext);
|
||||||
if (modelBindingResult.IsModelSet &&
|
if (modelBindingResult.IsModelSet)
|
||||||
modelBindingResult.ValidationNode != null)
|
|
||||||
{
|
{
|
||||||
var modelExplorer = new ModelExplorer(
|
_validator.Validate(
|
||||||
_modelMetadataProvider,
|
|
||||||
metadata,
|
|
||||||
modelBindingResult.Model);
|
|
||||||
var validationContext = new ModelValidationContext(
|
|
||||||
modelBindingContext.BindingSource,
|
|
||||||
operationContext.ValidatorProvider,
|
operationContext.ValidatorProvider,
|
||||||
modelState,
|
modelState,
|
||||||
modelExplorer);
|
modelBindingContext.ValidationState,
|
||||||
|
modelBindingResult.Key,
|
||||||
_validator.Validate(validationContext, modelBindingResult.ValidationNode);
|
modelBindingResult.Model);
|
||||||
}
|
}
|
||||||
|
|
||||||
return modelBindingResult;
|
return modelBindingResult;
|
||||||
|
|
|
||||||
|
|
@ -87,12 +87,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
return ModelBindingResult.Failed(modelBindingKey);
|
return ModelBindingResult.Failed(modelBindingKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
var validationNode = new ModelValidationNode(modelBindingKey, bindingContext.ModelMetadata, model)
|
return ModelBindingResult.Success(modelBindingKey, model);
|
||||||
{
|
|
||||||
ValidateAllProperties = true
|
|
||||||
};
|
|
||||||
|
|
||||||
return ModelBindingResult.Success(modelBindingKey, model, validationNode);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -44,12 +44,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var model = Convert.FromBase64String(value);
|
var model = Convert.FromBase64String(value);
|
||||||
var validationNode = new ModelValidationNode(
|
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model);
|
||||||
bindingContext.ModelName,
|
|
||||||
bindingContext.ModelMetadata,
|
|
||||||
model);
|
|
||||||
|
|
||||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model, validationNode);
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
if (bindingContext.ModelType == typeof(CancellationToken))
|
if (bindingContext.ModelType == typeof(CancellationToken))
|
||||||
{
|
{
|
||||||
var model = bindingContext.OperationBindingContext.HttpContext.RequestAborted;
|
var model = bindingContext.OperationBindingContext.HttpContext.RequestAborted;
|
||||||
var validationNode =
|
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model);
|
||||||
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, model)
|
|
||||||
{
|
|
||||||
SuppressValidation = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model, validationNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ModelBindingResult.NoResultAsync;
|
return ModelBindingResult.NoResultAsync;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
#endif
|
#endif
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
@ -38,12 +39,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
model = CreateEmptyCollection(bindingContext.ModelType);
|
model = CreateEmptyCollection(bindingContext.ModelType);
|
||||||
}
|
}
|
||||||
|
|
||||||
var validationNode = new ModelValidationNode(
|
return ModelBindingResult.Success(bindingContext.ModelName, model);
|
||||||
bindingContext.ModelName,
|
|
||||||
bindingContext.ModelMetadata,
|
|
||||||
model);
|
|
||||||
|
|
||||||
return ModelBindingResult.Success(bindingContext.ModelName, model, validationNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ModelBindingResult.NoResult;
|
return ModelBindingResult.NoResult;
|
||||||
|
|
@ -72,6 +68,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
CopyToModel(model, boundCollection);
|
CopyToModel(model, boundCollection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Debug.Assert(model != null);
|
||||||
|
if (result.ValidationStrategy != null)
|
||||||
|
{
|
||||||
|
bindingContext.ValidationState.Add(model, new ValidationStateEntry()
|
||||||
|
{
|
||||||
|
Strategy = result.ValidationStrategy,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (valueProviderResult != ValueProviderResult.None)
|
if (valueProviderResult != ValueProviderResult.None)
|
||||||
{
|
{
|
||||||
// If we did simple binding, then modelstate should be updated to reflect what we bound for ModelName.
|
// If we did simple binding, then modelstate should be updated to reflect what we bound for ModelName.
|
||||||
|
|
@ -81,7 +86,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
valueProviderResult);
|
valueProviderResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ModelBindingResult.Success(bindingContext.ModelName, model, result.ValidationNode);
|
return ModelBindingResult.Success(bindingContext.ModelName, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -137,11 +142,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
|
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
|
||||||
var elementMetadata = metadataProvider.GetMetadataForType(typeof(TElement));
|
var elementMetadata = metadataProvider.GetMetadataForType(typeof(TElement));
|
||||||
|
|
||||||
var validationNode = new ModelValidationNode(
|
|
||||||
bindingContext.ModelName,
|
|
||||||
bindingContext.ModelMetadata,
|
|
||||||
boundCollection);
|
|
||||||
|
|
||||||
var innerBindingContext = ModelBindingContext.CreateChildBindingContext(
|
var innerBindingContext = ModelBindingContext.CreateChildBindingContext(
|
||||||
bindingContext,
|
bindingContext,
|
||||||
elementMetadata,
|
elementMetadata,
|
||||||
|
|
@ -164,18 +164,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
if (result != null && result.IsModelSet)
|
if (result != null && result.IsModelSet)
|
||||||
{
|
{
|
||||||
boundValue = result.Model;
|
boundValue = result.Model;
|
||||||
if (result.ValidationNode != null)
|
|
||||||
{
|
|
||||||
validationNode.ChildNodes.Add(result.ValidationNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
boundCollection.Add(ModelBindingHelper.CastOrDefault<TElement>(boundValue));
|
boundCollection.Add(ModelBindingHelper.CastOrDefault<TElement>(boundValue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CollectionResult
|
return new CollectionResult
|
||||||
{
|
{
|
||||||
ValidationNode = validationNode,
|
|
||||||
Model = boundCollection
|
Model = boundCollection
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -211,10 +205,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var elementMetadata = metadataProvider.GetMetadataForType(typeof(TElement));
|
var elementMetadata = metadataProvider.GetMetadataForType(typeof(TElement));
|
||||||
|
|
||||||
var boundCollection = new List<TElement>();
|
var boundCollection = new List<TElement>();
|
||||||
var validationNode = new ModelValidationNode(
|
|
||||||
bindingContext.ModelName,
|
|
||||||
bindingContext.ModelMetadata,
|
|
||||||
boundCollection);
|
|
||||||
foreach (var indexName in indexNames)
|
foreach (var indexName in indexNames)
|
||||||
{
|
{
|
||||||
var fullChildName = ModelNames.CreateIndexModelName(bindingContext.ModelName, indexName);
|
var fullChildName = ModelNames.CreateIndexModelName(bindingContext.ModelName, indexName);
|
||||||
|
|
@ -235,10 +226,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
didBind = true;
|
didBind = true;
|
||||||
boundValue = result.Model;
|
boundValue = result.Model;
|
||||||
if (result.ValidationNode != null)
|
|
||||||
{
|
|
||||||
validationNode.ChildNodes.Add(result.ValidationNode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// infinite size collection stops on first bind failure
|
// infinite size collection stops on first bind failure
|
||||||
|
|
@ -252,17 +239,26 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
||||||
return new CollectionResult
|
return new CollectionResult
|
||||||
{
|
{
|
||||||
ValidationNode = validationNode,
|
Model = boundCollection,
|
||||||
Model = boundCollection
|
|
||||||
|
// If we're working with a fixed set of indexes then this is the format like:
|
||||||
|
//
|
||||||
|
// ?parameter.index=zero,one,two¶meter[zero]=0&¶meter[one]=1¶meter[two]=2...
|
||||||
|
//
|
||||||
|
// We need to provide this data to the validation system so it can 'replay' the keys.
|
||||||
|
// But we can't just set ValidationState here, because it needs the 'real' model.
|
||||||
|
ValidationStrategy = indexNamesIsFinite ?
|
||||||
|
new ExplicitIndexCollectionValidationStrategy(indexNames) :
|
||||||
|
null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal for testing.
|
// Internal for testing.
|
||||||
internal class CollectionResult
|
internal class CollectionResult
|
||||||
{
|
{
|
||||||
public ModelValidationNode ValidationNode { get; set; }
|
|
||||||
|
|
||||||
public IEnumerable<TElement> Model { get; set; }
|
public IEnumerable<TElement> Model { get; set; }
|
||||||
|
|
||||||
|
public IValidationStrategy ValidationStrategy { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Mvc.Core;
|
using Microsoft.AspNet.Mvc.Core;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
@ -60,6 +61,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
if (result.IsModelSet ||
|
if (result.IsModelSet ||
|
||||||
bindingContext.ModelState.ContainsKey(bindingContext.ModelName))
|
bindingContext.ModelState.ContainsKey(bindingContext.ModelName))
|
||||||
{
|
{
|
||||||
|
if (bindingContext.IsTopLevelObject && result.Model != null)
|
||||||
|
{
|
||||||
|
ValidationStateEntry entry;
|
||||||
|
if (!bindingContext.ValidationState.TryGetValue(result.Model, out entry))
|
||||||
|
{
|
||||||
|
entry = new ValidationStateEntry();
|
||||||
|
bindingContext.ValidationState.Add(result.Model, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.Key = entry.Key ?? result.Key;
|
||||||
|
entry.Metadata = entry.Metadata ?? bindingContext.ModelMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,6 +137,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
BindingSource = oldBindingContext.BindingSource,
|
BindingSource = oldBindingContext.BindingSource,
|
||||||
BinderType = oldBindingContext.BinderType,
|
BinderType = oldBindingContext.BinderType,
|
||||||
IsTopLevelObject = oldBindingContext.IsTopLevelObject,
|
IsTopLevelObject = oldBindingContext.IsTopLevelObject,
|
||||||
|
ValidationState = oldBindingContext.ValidationState,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (bindingSource != null && bindingSource.IsGreedy)
|
if (bindingSource != null && bindingSource.IsGreedy)
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
#endif
|
#endif
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
@ -24,7 +25,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
public override async Task<ModelBindingResult> BindModelAsync([NotNull] ModelBindingContext bindingContext)
|
public override async Task<ModelBindingResult> BindModelAsync([NotNull] ModelBindingContext bindingContext)
|
||||||
{
|
{
|
||||||
var result = await base.BindModelAsync(bindingContext);
|
var result = await base.BindModelAsync(bindingContext);
|
||||||
if (result == null || !result.IsModelSet)
|
if (!result.IsModelSet)
|
||||||
{
|
{
|
||||||
// No match for the prefix at all.
|
// No match for the prefix at all.
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -65,8 +66,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
model: null);
|
model: null);
|
||||||
|
|
||||||
var modelBinder = bindingContext.OperationBindingContext.ModelBinder;
|
var modelBinder = bindingContext.OperationBindingContext.ModelBinder;
|
||||||
var validationNode = result.ValidationNode;
|
|
||||||
|
|
||||||
|
var keyMappings = new Dictionary<string, TKey>(StringComparer.Ordinal);
|
||||||
foreach (var kvp in keys)
|
foreach (var kvp in keys)
|
||||||
{
|
{
|
||||||
// Use InvariantCulture to convert the key since ExpressionHelper.GetExpressionText() would use
|
// Use InvariantCulture to convert the key since ExpressionHelper.GetExpressionText() would use
|
||||||
|
|
@ -79,12 +80,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
||||||
// Always add an entry to the dictionary but validate only if binding was successful.
|
// Always add an entry to the dictionary but validate only if binding was successful.
|
||||||
model[convertedKey] = ModelBindingHelper.CastOrDefault<TValue>(valueResult.Model);
|
model[convertedKey] = ModelBindingHelper.CastOrDefault<TValue>(valueResult.Model);
|
||||||
if (valueResult.IsModelSet)
|
keyMappings.Add(kvp.Key, convertedKey);
|
||||||
{
|
|
||||||
validationNode.ChildNodes.Add(valueResult.ValidationNode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bindingContext.ValidationState.Add(model, new ValidationStateEntry()
|
||||||
|
{
|
||||||
|
Strategy = new ShortFormDictionaryValidationStrategy<TKey, TValue>(keyMappings, valueMetadata),
|
||||||
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
using Microsoft.Framework.Primitives;
|
using Microsoft.Framework.Primitives;
|
||||||
|
|
||||||
|
|
@ -46,13 +47,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
model = new EmptyFormCollection();
|
model = new EmptyFormCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
var validationNode =
|
bindingContext.ValidationState.Add(model, new ValidationStateEntry() { SuppressValidation = true });
|
||||||
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, model)
|
return ModelBindingResult.Success(bindingContext.ModelName, model);
|
||||||
{
|
|
||||||
SuppressValidation = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
return ModelBindingResult.Success(bindingContext.ModelName, model, validationNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class EmptyFormCollection : IFormCollection
|
private class EmptyFormCollection : IFormCollection
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ using System.Reflection;
|
||||||
#endif
|
#endif
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Framework.Internal;
|
using Microsoft.Framework.Internal;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
|
@ -62,18 +63,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var validationNode =
|
bindingContext.ValidationState.Add(value, new ValidationStateEntry() { SuppressValidation = true });
|
||||||
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, value)
|
|
||||||
{
|
|
||||||
SuppressValidation = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
bindingContext.ModelState.SetModelValue(
|
bindingContext.ModelState.SetModelValue(
|
||||||
bindingContext.ModelName,
|
bindingContext.ModelName,
|
||||||
rawValue: null,
|
rawValue: null,
|
||||||
attemptedValue: null);
|
attemptedValue: null);
|
||||||
|
|
||||||
return ModelBindingResult.Success(bindingContext.ModelName, value, validationNode);
|
return ModelBindingResult.Success(bindingContext.ModelName, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,17 +63,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var validationNode = new ModelValidationNode(
|
|
||||||
bindingContext.ModelName,
|
|
||||||
bindingContext.ModelMetadata,
|
|
||||||
model);
|
|
||||||
|
|
||||||
bindingContext.ModelState.SetModelValue(
|
bindingContext.ModelState.SetModelValue(
|
||||||
bindingContext.ModelName,
|
bindingContext.ModelName,
|
||||||
request.Headers.GetCommaSeparatedValues(headerName),
|
request.Headers.GetCommaSeparatedValues(headerName),
|
||||||
request.Headers[headerName]);
|
request.Headers[headerName]);
|
||||||
|
|
||||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model, validationNode);
|
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
typeof(KeyValuePair<TKey, TValue>),
|
typeof(KeyValuePair<TKey, TValue>),
|
||||||
allowNullModel: true);
|
allowNullModel: true);
|
||||||
|
|
||||||
var childNodes = new List<ModelValidationNode>();
|
var keyResult = await TryBindStrongModel<TKey>(bindingContext, "Key");
|
||||||
var keyResult = await TryBindStrongModel<TKey>(bindingContext, "Key", childNodes);
|
var valueResult = await TryBindStrongModel<TValue>(bindingContext, "Value");
|
||||||
var valueResult = await TryBindStrongModel<TValue>(bindingContext, "Value", childNodes);
|
|
||||||
|
|
||||||
if (keyResult.IsModelSet && valueResult.IsModelSet)
|
if (keyResult.IsModelSet && valueResult.IsModelSet)
|
||||||
{
|
{
|
||||||
|
|
@ -25,15 +24,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
ModelBindingHelper.CastOrDefault<TKey>(keyResult.Model),
|
ModelBindingHelper.CastOrDefault<TKey>(keyResult.Model),
|
||||||
ModelBindingHelper.CastOrDefault<TValue>(valueResult.Model));
|
ModelBindingHelper.CastOrDefault<TValue>(valueResult.Model));
|
||||||
|
|
||||||
// Update the model for the top level validation node.
|
return ModelBindingResult.Success(bindingContext.ModelName, model);
|
||||||
var modelValidationNode =
|
|
||||||
new ModelValidationNode(
|
|
||||||
bindingContext.ModelName,
|
|
||||||
bindingContext.ModelMetadata,
|
|
||||||
model,
|
|
||||||
childNodes);
|
|
||||||
|
|
||||||
return ModelBindingResult.Success(bindingContext.ModelName, model, modelValidationNode);
|
|
||||||
}
|
}
|
||||||
else if (!keyResult.IsModelSet && valueResult.IsModelSet)
|
else if (!keyResult.IsModelSet && valueResult.IsModelSet)
|
||||||
{
|
{
|
||||||
|
|
@ -62,13 +53,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
if (bindingContext.IsTopLevelObject)
|
if (bindingContext.IsTopLevelObject)
|
||||||
{
|
{
|
||||||
var model = new KeyValuePair<TKey, TValue>();
|
var model = new KeyValuePair<TKey, TValue>();
|
||||||
|
return ModelBindingResult.Success(bindingContext.ModelName, model);
|
||||||
var validationNode = new ModelValidationNode(
|
|
||||||
bindingContext.ModelName,
|
|
||||||
bindingContext.ModelMetadata,
|
|
||||||
model);
|
|
||||||
|
|
||||||
return ModelBindingResult.Success(bindingContext.ModelName, model, validationNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ModelBindingResult.NoResult;
|
return ModelBindingResult.NoResult;
|
||||||
|
|
@ -77,8 +62,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
||||||
internal async Task<ModelBindingResult> TryBindStrongModel<TModel>(
|
internal async Task<ModelBindingResult> TryBindStrongModel<TModel>(
|
||||||
ModelBindingContext parentBindingContext,
|
ModelBindingContext parentBindingContext,
|
||||||
string propertyName,
|
string propertyName)
|
||||||
List<ModelValidationNode> childNodes)
|
|
||||||
{
|
{
|
||||||
var propertyModelMetadata =
|
var propertyModelMetadata =
|
||||||
parentBindingContext.OperationBindingContext.MetadataProvider.GetMetadataForType(typeof(TModel));
|
parentBindingContext.OperationBindingContext.MetadataProvider.GetMetadataForType(typeof(TModel));
|
||||||
|
|
@ -91,19 +75,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
propertyModelName,
|
propertyModelName,
|
||||||
model: null);
|
model: null);
|
||||||
|
|
||||||
var modelBindingResult = await propertyBindingContext.OperationBindingContext.ModelBinder.BindModelAsync(
|
var result = await propertyBindingContext.OperationBindingContext.ModelBinder.BindModelAsync(
|
||||||
propertyBindingContext);
|
propertyBindingContext);
|
||||||
if (modelBindingResult != ModelBindingResult.NoResult)
|
if (result.IsModelSet)
|
||||||
{
|
{
|
||||||
if (modelBindingResult.ValidationNode != null)
|
return result;
|
||||||
{
|
}
|
||||||
childNodes.Add(modelBindingResult.ValidationNode);
|
else
|
||||||
}
|
{
|
||||||
|
return ModelBindingResult.Failed(propertyModelName);
|
||||||
return modelBindingResult;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ModelBindingResult.Failed(propertyModelName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -309,22 +309,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var modelBindingResult = await modelBinder.BindModelAsync(modelBindingContext);
|
var modelBindingResult = await modelBinder.BindModelAsync(modelBindingContext);
|
||||||
if (modelBindingResult.IsModelSet)
|
if (modelBindingResult.IsModelSet)
|
||||||
{
|
{
|
||||||
var modelExplorer = new ModelExplorer(metadataProvider, modelMetadata, modelBindingResult.Model);
|
objectModelValidator.Validate(
|
||||||
var modelValidationContext = new ModelValidationContext(modelBindingContext, modelExplorer);
|
operationBindingContext.ValidatorProvider,
|
||||||
|
modelState,
|
||||||
var validationNode = modelBindingResult.ValidationNode;
|
modelBindingContext.ValidationState,
|
||||||
if (validationNode == null)
|
modelBindingResult.Key,
|
||||||
{
|
modelBindingResult.Model);
|
||||||
validationNode = new ModelValidationNode(
|
|
||||||
modelBindingResult.Key,
|
|
||||||
modelMetadata,
|
|
||||||
modelBindingResult.Model)
|
|
||||||
{
|
|
||||||
ValidateAllProperties = true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
objectModelValidator.Validate(modelValidationContext, validationNode);
|
|
||||||
|
|
||||||
return modelState.IsValid;
|
return modelState.IsValid;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -52,16 +52,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
||||||
var results = await BindPropertiesAsync(bindingContext, mutableObjectBinderContext.PropertyMetadata);
|
var results = await BindPropertiesAsync(bindingContext, mutableObjectBinderContext.PropertyMetadata);
|
||||||
|
|
||||||
var validationNode = new ModelValidationNode(
|
|
||||||
bindingContext.ModelName,
|
|
||||||
bindingContext.ModelMetadata,
|
|
||||||
model);
|
|
||||||
|
|
||||||
// Post-processing e.g. property setters and hooking up validation.
|
// Post-processing e.g. property setters and hooking up validation.
|
||||||
bindingContext.Model = model;
|
bindingContext.Model = model;
|
||||||
ProcessResults(bindingContext, results, validationNode);
|
ProcessResults(bindingContext, results);
|
||||||
|
|
||||||
return ModelBindingResult.Success(bindingContext.ModelName, model, validationNode);
|
return ModelBindingResult.Success(bindingContext.ModelName, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -398,10 +393,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal for testing.
|
// Internal for testing.
|
||||||
internal ModelValidationNode ProcessResults(
|
internal void ProcessResults(
|
||||||
ModelBindingContext bindingContext,
|
ModelBindingContext bindingContext,
|
||||||
IDictionary<ModelMetadata, ModelBindingResult> results,
|
IDictionary<ModelMetadata, ModelBindingResult> results)
|
||||||
ModelValidationNode validationNode)
|
|
||||||
{
|
{
|
||||||
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
|
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
|
||||||
var modelExplorer =
|
var modelExplorer =
|
||||||
|
|
@ -432,20 +426,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var result = entry.Value;
|
var result = entry.Value;
|
||||||
var propertyMetadata = entry.Key;
|
var propertyMetadata = entry.Key;
|
||||||
SetProperty(bindingContext, modelExplorer, propertyMetadata, result);
|
SetProperty(bindingContext, modelExplorer, propertyMetadata, result);
|
||||||
|
|
||||||
var propertyValidationNode = result.ValidationNode;
|
|
||||||
if (propertyValidationNode == null)
|
|
||||||
{
|
|
||||||
// Make sure that irrespective of whether the properties of the model were bound with a value,
|
|
||||||
// create a validation node so that these get validated.
|
|
||||||
propertyValidationNode = new ModelValidationNode(result.Key, entry.Key, result.Model);
|
|
||||||
}
|
|
||||||
|
|
||||||
validationNode.ChildNodes.Add(propertyValidationNode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return validationNode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -576,30 +558,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if validator execution adds a model error.
|
|
||||||
private static bool RunValidator(
|
|
||||||
IModelValidator validator,
|
|
||||||
ModelBindingContext bindingContext,
|
|
||||||
ModelExplorer propertyExplorer,
|
|
||||||
string modelStateKey)
|
|
||||||
{
|
|
||||||
var validationContext = new ModelValidationContext(bindingContext, propertyExplorer);
|
|
||||||
|
|
||||||
var addedError = false;
|
|
||||||
foreach (var validationResult in validator.Validate(validationContext))
|
|
||||||
{
|
|
||||||
bindingContext.ModelState.TryAddModelError(modelStateKey, validationResult.Message);
|
|
||||||
addedError = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!addedError)
|
|
||||||
{
|
|
||||||
bindingContext.ModelState.MarkFieldValid(modelStateKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
return addedError;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class PropertyValidationInfo
|
internal sealed class PropertyValidationInfo
|
||||||
{
|
{
|
||||||
public PropertyValidationInfo()
|
public PropertyValidationInfo()
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// 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.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
@ -30,13 +31,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
||||||
var requestServices = bindingContext.OperationBindingContext.HttpContext.RequestServices;
|
var requestServices = bindingContext.OperationBindingContext.HttpContext.RequestServices;
|
||||||
var model = requestServices.GetRequiredService(bindingContext.ModelType);
|
var model = requestServices.GetRequiredService(bindingContext.ModelType);
|
||||||
var validationNode =
|
|
||||||
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, model)
|
|
||||||
{
|
|
||||||
SuppressValidation = true
|
|
||||||
};
|
|
||||||
|
|
||||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model, validationNode);
|
bindingContext.ValidationState.Add(model, new ValidationStateEntry() { SuppressValidation = true });
|
||||||
|
|
||||||
|
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,12 +57,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var validationNode = new ModelValidationNode(
|
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model);
|
||||||
bindingContext.ModelName,
|
|
||||||
bindingContext.ModelMetadata,
|
|
||||||
model);
|
|
||||||
|
|
||||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model, validationNode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The default implementation of <see cref="IValidationStrategy"/> for a collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This implementation handles cases like:
|
||||||
|
/// <example>
|
||||||
|
/// Model: IList<Student>
|
||||||
|
/// Query String: ?students[0].Age=8&students[1].Age=9
|
||||||
|
///
|
||||||
|
/// In this case the elements of the collection are identified in the input data set by an incrementing
|
||||||
|
/// integer index.
|
||||||
|
/// </example>
|
||||||
|
///
|
||||||
|
/// or:
|
||||||
|
///
|
||||||
|
/// <example>
|
||||||
|
/// Model: IDictionary<string, int>
|
||||||
|
/// Query String: ?students[0].Key=Joey&students[0].Value=8
|
||||||
|
///
|
||||||
|
/// In this case the dictionary is treated as a collection of key-value pairs, and the elements of the
|
||||||
|
/// collection are identified in the input data set by an incrementing integer index.
|
||||||
|
/// </example>
|
||||||
|
///
|
||||||
|
/// Using this key format, the enumerator enumerates model objects of type matching
|
||||||
|
/// <see cref="ModelMetadata.ElementMetadata"/>. The indices of the elements in the collection are used to
|
||||||
|
/// compute the model prefix keys.
|
||||||
|
/// </remarks>
|
||||||
|
public class DefaultCollectionValidationStrategy : IValidationStrategy
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an instance of <see cref="DefaultCollectionValidationStrategy"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly IValidationStrategy Instance = new DefaultCollectionValidationStrategy();
|
||||||
|
|
||||||
|
private DefaultCollectionValidationStrategy()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IEnumerator<ValidationEntry> GetChildren(
|
||||||
|
ModelMetadata metadata,
|
||||||
|
string key,
|
||||||
|
object model)
|
||||||
|
{
|
||||||
|
return new Enumerator(metadata.ElementMetadata, key, (IEnumerable)model);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Enumerator : IEnumerator<ValidationEntry>
|
||||||
|
{
|
||||||
|
private readonly string _key;
|
||||||
|
private readonly ModelMetadata _metadata;
|
||||||
|
private readonly IEnumerable _model;
|
||||||
|
private readonly IEnumerator _enumerator;
|
||||||
|
|
||||||
|
private ValidationEntry _entry;
|
||||||
|
private int _index;
|
||||||
|
|
||||||
|
public Enumerator(
|
||||||
|
ModelMetadata metadata,
|
||||||
|
string key,
|
||||||
|
IEnumerable model)
|
||||||
|
{
|
||||||
|
_metadata = metadata;
|
||||||
|
_key = key;
|
||||||
|
_model = model;
|
||||||
|
|
||||||
|
_enumerator = _model.GetEnumerator();
|
||||||
|
|
||||||
|
_index = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValidationEntry Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object IEnumerator.Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
_index++;
|
||||||
|
if (!_enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var key = ModelNames.CreateIndexModelName(_key, _index);
|
||||||
|
var model = _enumerator.Current;
|
||||||
|
|
||||||
|
_entry = new ValidationEntry(_metadata, key, model);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The default implementation of <see cref="IValidationStrategy"/> for a complex object.
|
||||||
|
/// </summary>
|
||||||
|
public class DefaultComplexObjectValidationStrategy : IValidationStrategy
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an instance of <see cref="DefaultComplexObjectValidationStrategy"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly IValidationStrategy Instance = new DefaultComplexObjectValidationStrategy();
|
||||||
|
|
||||||
|
private DefaultComplexObjectValidationStrategy()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IEnumerator<ValidationEntry> GetChildren(
|
||||||
|
ModelMetadata metadata,
|
||||||
|
string key,
|
||||||
|
object model)
|
||||||
|
{
|
||||||
|
return new Enumerator(metadata.Properties, key, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Enumerator : IEnumerator<ValidationEntry>
|
||||||
|
{
|
||||||
|
private readonly string _key;
|
||||||
|
private readonly object _model;
|
||||||
|
private readonly ModelPropertyCollection _properties;
|
||||||
|
|
||||||
|
private ValidationEntry _entry;
|
||||||
|
private int _index;
|
||||||
|
|
||||||
|
public Enumerator(
|
||||||
|
ModelPropertyCollection properties,
|
||||||
|
string key,
|
||||||
|
object model)
|
||||||
|
{
|
||||||
|
_properties = properties;
|
||||||
|
_key = key;
|
||||||
|
_model = model;
|
||||||
|
|
||||||
|
_index = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValidationEntry Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object IEnumerator.Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
_index++;
|
||||||
|
if (_index >= _properties.Count)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var property = _properties[_index];
|
||||||
|
var propertyName = property.BinderModelName ?? property.PropertyName;
|
||||||
|
var key = ModelNames.CreatePropertyModelName(_key, propertyName);
|
||||||
|
var model = property.PropertyGetter(_model);
|
||||||
|
|
||||||
|
_entry = new ValidationEntry(property, key, model);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,18 +2,12 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Microsoft.Framework.Internal;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Recursively validate an object.
|
/// The default implementation of <see cref="IObjectModelValidator"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DefaultObjectValidator : IObjectModelValidator
|
public class DefaultObjectValidator : IObjectModelValidator
|
||||||
{
|
{
|
||||||
|
|
@ -27,324 +21,49 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
/// types to exclude from validation.</param>
|
/// types to exclude from validation.</param>
|
||||||
/// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
|
/// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
|
||||||
public DefaultObjectValidator(
|
public DefaultObjectValidator(
|
||||||
[NotNull] IList<IExcludeTypeValidationFilter> excludeFilters,
|
IList<IExcludeTypeValidationFilter> excludeFilters,
|
||||||
[NotNull] IModelMetadataProvider modelMetadataProvider)
|
IModelMetadataProvider modelMetadataProvider)
|
||||||
{
|
{
|
||||||
|
if (excludeFilters == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(excludeFilters));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modelMetadataProvider == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(modelMetadataProvider));
|
||||||
|
}
|
||||||
|
|
||||||
_modelMetadataProvider = modelMetadataProvider;
|
_modelMetadataProvider = modelMetadataProvider;
|
||||||
_excludeFilters = excludeFilters;
|
_excludeFilters = excludeFilters;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Validate(
|
public void Validate(
|
||||||
[NotNull] ModelValidationContext modelValidationContext,
|
IModelValidatorProvider validatorProvider,
|
||||||
[NotNull] ModelValidationNode validationNode)
|
ModelStateDictionary modelState,
|
||||||
|
ValidationStateDictionary validationState,
|
||||||
|
string prefix,
|
||||||
|
object model)
|
||||||
{
|
{
|
||||||
var validationContext = new ValidationContext()
|
if (validatorProvider == null)
|
||||||
{
|
{
|
||||||
ModelValidationContext = modelValidationContext,
|
throw new ArgumentNullException(nameof(validatorProvider));
|
||||||
Visited = new HashSet<object>(ReferenceEqualityComparer.Instance),
|
|
||||||
ValidationNode = validationNode
|
|
||||||
};
|
|
||||||
|
|
||||||
ValidateNonVisitedNodeAndChildren(
|
|
||||||
validationNode.Key,
|
|
||||||
validationContext,
|
|
||||||
validators: null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ValidateNonVisitedNodeAndChildren(
|
|
||||||
string modelKey,
|
|
||||||
ValidationContext validationContext,
|
|
||||||
IList<IModelValidator> validators)
|
|
||||||
{
|
|
||||||
// Recursion guard to avoid stack overflows
|
|
||||||
RuntimeHelpers.EnsureSufficientExecutionStack();
|
|
||||||
|
|
||||||
var modelValidationContext = validationContext.ModelValidationContext;
|
|
||||||
var modelExplorer = modelValidationContext.ModelExplorer;
|
|
||||||
var modelState = modelValidationContext.ModelState;
|
|
||||||
var currentValidationNode = validationContext.ValidationNode;
|
|
||||||
if (currentValidationNode.SuppressValidation)
|
|
||||||
{
|
|
||||||
// Short circuit if the node is marked to be suppressed.
|
|
||||||
// If there are any sub entries which were model bound, they need to be marked as skipped,
|
|
||||||
// Otherwise they will remain as unvalidated and the model state would be Invalid.
|
|
||||||
MarkChildNodesAsSkipped(modelKey, modelExplorer.Metadata, validationContext);
|
|
||||||
|
|
||||||
// For validation purposes this model is valid.
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (modelState.HasReachedMaxErrors)
|
if (modelState == null)
|
||||||
{
|
{
|
||||||
// Short circuit if max errors have been recorded. In which case we treat this as invalid.
|
throw new ArgumentNullException(nameof(modelState));
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var isValid = true;
|
var visitor = new ValidationVisitor(
|
||||||
if (validators == null)
|
validatorProvider,
|
||||||
{
|
_excludeFilters,
|
||||||
// The validators are not null in the case of validating an array. Since the validators are
|
modelState,
|
||||||
// the same for all the elements of the array, we do not do GetValidators for each element,
|
validationState);
|
||||||
// instead we just pass them over.
|
|
||||||
validators = GetValidators(modelValidationContext.ValidatorProvider, modelExplorer.Metadata);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't need to recursively traverse the graph if there are no child nodes.
|
var metadata = model == null ? null : _modelMetadataProvider.GetMetadataForType(model.GetType());
|
||||||
if (currentValidationNode.ChildNodes.Count == 0 && !currentValidationNode.ValidateAllProperties)
|
visitor.Validate(metadata, prefix, model);
|
||||||
{
|
|
||||||
return ShallowValidate(modelKey, modelExplorer, validationContext, validators);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't need to recursively traverse the graph for types that shouldn't be validated
|
|
||||||
var modelType = modelExplorer.ModelType;
|
|
||||||
if (IsTypeExcludedFromValidation(_excludeFilters, modelType))
|
|
||||||
{
|
|
||||||
var result = ShallowValidate(modelKey, modelExplorer, validationContext, validators);
|
|
||||||
|
|
||||||
// If there are any sub entries which were model bound, they need to be marked as skipped,
|
|
||||||
// Otherwise they will remain as unvalidated and the model state would be Invalid.
|
|
||||||
MarkChildNodesAsSkipped(modelKey, modelExplorer.Metadata, validationContext);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check to avoid infinite recursion. This can happen with cycles in an object graph.
|
|
||||||
// Note that this is only applicable in case the model is pre-existing (like in case of TryUpdateModel).
|
|
||||||
if (validationContext.Visited.Contains(modelExplorer.Model))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
validationContext.Visited.Add(modelExplorer.Model);
|
|
||||||
isValid = ValidateChildNodes(modelKey, modelExplorer, validationContext);
|
|
||||||
if (isValid)
|
|
||||||
{
|
|
||||||
// Don't bother to validate this node if children failed.
|
|
||||||
isValid = ShallowValidate(modelKey, modelExplorer, validationContext, validators);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pop the object so that it can be validated again in a different path
|
|
||||||
validationContext.Visited.Remove(modelExplorer.Model);
|
|
||||||
return isValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MarkChildNodesAsSkipped(string currentModelKey, ModelMetadata metadata, ValidationContext validationContext)
|
|
||||||
{
|
|
||||||
var modelState = validationContext.ModelValidationContext.ModelState;
|
|
||||||
var fieldValidationState = modelState.GetFieldValidationState(currentModelKey);
|
|
||||||
|
|
||||||
// Since shallow validation is done, if the modelvalidation state is still marked as unvalidated,
|
|
||||||
// it is because some properties in the subtree are marked as unvalidated. Mark all such properties
|
|
||||||
// as skipped. Models which have their subtrees as Valid or Invalid do not need to be marked as skipped.
|
|
||||||
if (fieldValidationState != ModelValidationState.Unvalidated)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// At this point we just want to mark all sub-entries present in the model state as skipped.
|
|
||||||
var entries = modelState.FindKeysWithPrefix(currentModelKey);
|
|
||||||
foreach (var entry in entries)
|
|
||||||
{
|
|
||||||
entry.Value.ValidationState = ModelValidationState.Skipped;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private IList<IModelValidator> GetValidators(IModelValidatorProvider provider, ModelMetadata metadata)
|
|
||||||
{
|
|
||||||
var validatorProviderContext = new ModelValidatorProviderContext(metadata);
|
|
||||||
provider.GetValidators(validatorProviderContext);
|
|
||||||
|
|
||||||
return validatorProviderContext
|
|
||||||
.Validators
|
|
||||||
.OrderBy(v => v, ValidatorOrderComparer.Instance)
|
|
||||||
.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ValidateChildNodes(
|
|
||||||
string currentModelKey,
|
|
||||||
ModelExplorer modelExplorer,
|
|
||||||
ValidationContext validationContext)
|
|
||||||
{
|
|
||||||
var isValid = true;
|
|
||||||
var childNodes = GetChildNodes(validationContext, modelExplorer);
|
|
||||||
|
|
||||||
IList<IModelValidator> validators = null;
|
|
||||||
var elementMetadata = modelExplorer.Metadata.ElementMetadata;
|
|
||||||
if (elementMetadata != null)
|
|
||||||
{
|
|
||||||
validators = GetValidators(validationContext.ModelValidationContext.ValidatorProvider, elementMetadata);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var childNode in childNodes)
|
|
||||||
{
|
|
||||||
var childModelExplorer = childNode.ModelMetadata.MetadataKind == Metadata.ModelMetadataKind.Type ?
|
|
||||||
_modelMetadataProvider.GetModelExplorerForType(childNode.ModelMetadata.ModelType, childNode.Model) :
|
|
||||||
modelExplorer.GetExplorerForProperty(childNode.ModelMetadata.PropertyName);
|
|
||||||
|
|
||||||
var propertyValidationContext = new ValidationContext()
|
|
||||||
{
|
|
||||||
ModelValidationContext = ModelValidationContext.GetChildValidationContext(
|
|
||||||
validationContext.ModelValidationContext,
|
|
||||||
childModelExplorer),
|
|
||||||
Visited = validationContext.Visited,
|
|
||||||
ValidationNode = childNode
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!ValidateNonVisitedNodeAndChildren(
|
|
||||||
childNode.Key,
|
|
||||||
propertyValidationContext,
|
|
||||||
validators))
|
|
||||||
{
|
|
||||||
isValid = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return isValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validates a single node (not including children)
|
|
||||||
// Returns true if validation passes successfully
|
|
||||||
private static bool ShallowValidate(
|
|
||||||
string modelKey,
|
|
||||||
ModelExplorer modelExplorer,
|
|
||||||
ValidationContext validationContext,
|
|
||||||
IList<IModelValidator> validators)
|
|
||||||
{
|
|
||||||
var isValid = true;
|
|
||||||
|
|
||||||
var modelState = validationContext.ModelValidationContext.ModelState;
|
|
||||||
var fieldValidationState = modelState.GetFieldValidationState(modelKey);
|
|
||||||
if (fieldValidationState == ModelValidationState.Invalid)
|
|
||||||
{
|
|
||||||
// Even if we have no validators it's possible that model binding may have added a
|
|
||||||
// validation error (conversion error, missing data). We want to still run
|
|
||||||
// validators even if that's the case.
|
|
||||||
isValid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// When the are no validators we bail quickly. This saves a GetEnumerator allocation.
|
|
||||||
// In a large array (tens of thousands or more) scenario it's very significant.
|
|
||||||
if (validators == null || validators.Count > 0)
|
|
||||||
{
|
|
||||||
var modelValidationContext = ModelValidationContext.GetChildValidationContext(
|
|
||||||
validationContext.ModelValidationContext,
|
|
||||||
modelExplorer);
|
|
||||||
|
|
||||||
var modelValidationState = modelState.GetValidationState(modelKey);
|
|
||||||
|
|
||||||
// If either the model or its properties are unvalidated, validate them now.
|
|
||||||
if (modelValidationState == ModelValidationState.Unvalidated ||
|
|
||||||
fieldValidationState == ModelValidationState.Unvalidated)
|
|
||||||
{
|
|
||||||
foreach (var validator in validators)
|
|
||||||
{
|
|
||||||
foreach (var error in validator.Validate(modelValidationContext))
|
|
||||||
{
|
|
||||||
var errorKey = ModelNames.CreatePropertyModelName(modelKey, error.MemberName);
|
|
||||||
if (!modelState.TryAddModelError(errorKey, error.Message) &&
|
|
||||||
modelState.GetFieldValidationState(errorKey) == ModelValidationState.Unvalidated)
|
|
||||||
{
|
|
||||||
|
|
||||||
// If we are not able to add a model error
|
|
||||||
// for instance when the max error count is reached, mark the model as skipped.
|
|
||||||
modelState.MarkFieldSkipped(errorKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
isValid = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add an entry only if there was an entry which was added by a model binder.
|
|
||||||
// This prevents adding spurious entries.
|
|
||||||
if (modelState.ContainsKey(modelKey) && isValid)
|
|
||||||
{
|
|
||||||
validationContext.ModelValidationContext.ModelState.MarkFieldValid(modelKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
return isValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsTypeExcludedFromValidation(IList<IExcludeTypeValidationFilter> filters, Type type)
|
|
||||||
{
|
|
||||||
return filters.Any(filter => filter.IsTypeExcluded(type));
|
|
||||||
}
|
|
||||||
|
|
||||||
private IList<ModelValidationNode> GetChildNodes(ValidationContext context, ModelExplorer modelExplorer)
|
|
||||||
{
|
|
||||||
var validationNode = context.ValidationNode;
|
|
||||||
|
|
||||||
// This is the trivial case where the node-tree that was built-up during binding already has
|
|
||||||
// all of the nodes we need.
|
|
||||||
if (validationNode.ChildNodes.Count != 0 ||
|
|
||||||
!validationNode.ValidateAllProperties ||
|
|
||||||
validationNode.Model == null)
|
|
||||||
{
|
|
||||||
return validationNode.ChildNodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
var childNodes = new List<ModelValidationNode>(validationNode.ChildNodes);
|
|
||||||
var elementMetadata = modelExplorer.Metadata.ElementMetadata;
|
|
||||||
if (elementMetadata == null)
|
|
||||||
{
|
|
||||||
foreach (var property in validationNode.ModelMetadata.Properties)
|
|
||||||
{
|
|
||||||
var propertyExplorer = modelExplorer.GetExplorerForProperty(property.PropertyName);
|
|
||||||
var propertyBindingName = property.BinderModelName ?? property.PropertyName;
|
|
||||||
var childKey = ModelNames.CreatePropertyModelName(validationNode.Key, propertyBindingName);
|
|
||||||
var childNode = new ModelValidationNode(childKey, property, propertyExplorer.Model)
|
|
||||||
{
|
|
||||||
ValidateAllProperties = true
|
|
||||||
};
|
|
||||||
childNodes.Add(childNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var enumerableModel = (IEnumerable)modelExplorer.Model;
|
|
||||||
|
|
||||||
// An integer index is incorrect in scenarios where there is a custom index provided by the user.
|
|
||||||
// However those scenarios are supported by createing a ModelValidationNode with the right keys.
|
|
||||||
var index = 0;
|
|
||||||
foreach (var element in enumerableModel)
|
|
||||||
{
|
|
||||||
var elementExplorer = new ModelExplorer(_modelMetadataProvider, elementMetadata, element);
|
|
||||||
var elementKey = ModelNames.CreateIndexModelName(validationNode.Key, index);
|
|
||||||
var childNode = new ModelValidationNode(elementKey, elementMetadata, elementExplorer.Model)
|
|
||||||
{
|
|
||||||
ValidateAllProperties = true
|
|
||||||
};
|
|
||||||
|
|
||||||
childNodes.Add(childNode);
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return childNodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ValidationContext
|
|
||||||
{
|
|
||||||
public ModelValidationContext ModelValidationContext { get; set; }
|
|
||||||
|
|
||||||
public HashSet<object> Visited { get; set; }
|
|
||||||
|
|
||||||
public ModelValidationNode ValidationNode { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sorts validators based on whether or not they are 'required'. We want to run
|
|
||||||
// 'required' validators first so that we get the best possible error message.
|
|
||||||
private class ValidatorOrderComparer : IComparer<IModelValidator>
|
|
||||||
{
|
|
||||||
public static readonly ValidatorOrderComparer Instance = new ValidatorOrderComparer();
|
|
||||||
|
|
||||||
public int Compare(IModelValidator x, IModelValidator y)
|
|
||||||
{
|
|
||||||
var xScore = x.IsRequired ? 0 : 1;
|
|
||||||
var yScore = y.IsRequired ? 0 : 1;
|
|
||||||
return xScore.CompareTo(yScore);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An implementation of <see cref="IValidationStrategy"/> for a collection bound using 'explict indexing'
|
||||||
|
/// style keys.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This implemenation handles cases like:
|
||||||
|
/// <example>
|
||||||
|
/// Model: IList<Student>
|
||||||
|
/// Query String: ?students.index=Joey,Katherine&students[Joey].Age=8&students[Katherine].Age=9
|
||||||
|
///
|
||||||
|
/// In this case, 'Joey' and 'Katherine' need to be used in the model prefix keys, but cannot be inferred
|
||||||
|
/// form inspecting the collection. These prefixes are captured during model binding, and mapped to
|
||||||
|
/// the corresponding ordinal index of a model object in the collection. The enumerator returned from this
|
||||||
|
/// class will yield two 'Student' objects with corresponding keys 'students[Joey]' and 'students[Katherine]'.
|
||||||
|
/// </example>
|
||||||
|
///
|
||||||
|
/// Using this key format, the enumerator enumerates model objects of type matching
|
||||||
|
/// <see cref="ModelMetadata.ElementMetadata"/>. The keys captured during model binding are mapped to the elements
|
||||||
|
/// in the collection to compute the model prefix keys.
|
||||||
|
/// </remarks>
|
||||||
|
public class ExplicitIndexCollectionValidationStrategy : IValidationStrategy
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="ExplicitIndexCollectionValidationStrategy"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="elementKeys">The keys of collection elements that were used during model binding.</param>
|
||||||
|
public ExplicitIndexCollectionValidationStrategy(IEnumerable<string> elementKeys)
|
||||||
|
{
|
||||||
|
if (elementKeys == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(elementKeys));
|
||||||
|
}
|
||||||
|
|
||||||
|
ElementKeys = elementKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the keys of collection elements that were used during model binding.
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<string> ElementKeys { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IEnumerator<ValidationEntry> GetChildren(
|
||||||
|
ModelMetadata metadata,
|
||||||
|
string key,
|
||||||
|
object model)
|
||||||
|
{
|
||||||
|
return new Enumerator(metadata.ElementMetadata, key, ElementKeys, (IEnumerable)model);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Enumerator : IEnumerator<ValidationEntry>
|
||||||
|
{
|
||||||
|
private readonly string _key;
|
||||||
|
private readonly ModelMetadata _metadata;
|
||||||
|
private readonly IEnumerator _enumerator;
|
||||||
|
private readonly IEnumerator<string> _keyEnumerator;
|
||||||
|
|
||||||
|
private ValidationEntry _entry;
|
||||||
|
|
||||||
|
public Enumerator(
|
||||||
|
ModelMetadata metadata,
|
||||||
|
string key,
|
||||||
|
IEnumerable<string> elementKeys,
|
||||||
|
IEnumerable model)
|
||||||
|
{
|
||||||
|
_metadata = metadata;
|
||||||
|
_key = key;
|
||||||
|
|
||||||
|
_keyEnumerator = elementKeys.GetEnumerator();
|
||||||
|
_enumerator = model.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValidationEntry Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object IEnumerator.Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
if (!_keyEnumerator.MoveNext())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var model = _enumerator.Current;
|
||||||
|
var key = ModelNames.CreateIndexModelName(_key, _keyEnumerator.Current);
|
||||||
|
|
||||||
|
_entry = new ValidationEntry(_metadata, key, model);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.Framework.Internal;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -9,12 +11,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
public interface IObjectModelValidator
|
public interface IObjectModelValidator
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Validates the given model in <see cref="ModelValidationContext.ModelExplorer"/>.
|
/// Validates the provided object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="validationContext">The <see cref="ModelValidationContext"/> associated with the current call.
|
/// <param name="validatorProvider">The <see cref="IModelValidatorProvider"/>.</param>
|
||||||
|
/// <param name="modelState">The <see cref="ModelStateDictionary"/>.</param>
|
||||||
|
/// <param name="validationState">The <see cref="ValidationStateDictionary"/>. May be null.</param>
|
||||||
|
/// <param name="prefix">
|
||||||
|
/// The model prefix. Used to map the model object to entries in <paramref name="modelState"/>.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="validationNode">The <see cref="ModelValidationNode"/> for the model which gets validated.
|
/// <param name="model">The model object.</param>
|
||||||
/// </param>
|
void Validate(
|
||||||
void Validate(ModelValidationContext validationContext, ModelValidationNode validationNode);
|
IModelValidatorProvider validatorProvider,
|
||||||
|
ModelStateDictionary modelState,
|
||||||
|
ValidationStateDictionary validationState,
|
||||||
|
string prefix,
|
||||||
|
object model);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,135 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An implementation of <see cref="IValidationStrategy"/> for a dictionary bound with 'short form' style keys.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TKey">The <see cref="Type"/> of the keys of the model dictionary.</typeparam>
|
||||||
|
/// <typeparam name="TValue">The <see cref="Type"/> of the values of the model dictionary.</typeparam>
|
||||||
|
/// <remarks>
|
||||||
|
/// This implemenation handles cases like:
|
||||||
|
/// <example>
|
||||||
|
/// Model: IDictionary<string, Student>
|
||||||
|
/// Query String: ?students[Joey].Age=8&students[Katherine].Age=9
|
||||||
|
///
|
||||||
|
/// In this case, 'Joey' and 'Katherine' are the keys of the dictionary, used to bind two 'Student'
|
||||||
|
/// objects. The enumerator returned from this class will yield two 'Student' objects with corresponding
|
||||||
|
/// keys 'students[Joey]' and 'students[Katherine]'
|
||||||
|
/// </example>
|
||||||
|
///
|
||||||
|
/// Using this key format, the enumerator enumerates model objects of type <typeparamref name="TValue"/>. The
|
||||||
|
/// keys of the dictionary are not validated as they must be simple types.
|
||||||
|
/// </remarks>
|
||||||
|
public class ShortFormDictionaryValidationStrategy<TKey, TValue> : IValidationStrategy
|
||||||
|
{
|
||||||
|
private readonly ModelMetadata _valueMetadata;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="ShortFormDictionaryValidationStrategy{TKey, TValue}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="keyMappings">The mapping from model prefix key to dictionary key.</param>
|
||||||
|
/// <param name="valueMetadata">
|
||||||
|
/// The <see cref="ModelMetadata"/> associated with <typeparamref name="TValue"/>.
|
||||||
|
/// </param>
|
||||||
|
public ShortFormDictionaryValidationStrategy(
|
||||||
|
IEnumerable<KeyValuePair<string, TKey>> keyMappings,
|
||||||
|
ModelMetadata valueMetadata)
|
||||||
|
{
|
||||||
|
KeyMappings = keyMappings;
|
||||||
|
_valueMetadata = valueMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the mapping from model prefix key to dictionary key.
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<KeyValuePair<string, TKey>> KeyMappings { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IEnumerator<ValidationEntry> GetChildren(
|
||||||
|
ModelMetadata metadata,
|
||||||
|
string key,
|
||||||
|
object model)
|
||||||
|
{
|
||||||
|
return new Enumerator(_valueMetadata, key, KeyMappings, (IDictionary<TKey, TValue>)model);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Enumerator : IEnumerator<ValidationEntry>
|
||||||
|
{
|
||||||
|
private readonly string _key;
|
||||||
|
private readonly ModelMetadata _metadata;
|
||||||
|
private readonly IDictionary<TKey, TValue> _model;
|
||||||
|
private readonly IEnumerator<KeyValuePair<string, TKey>> _keyMappingEnumerator;
|
||||||
|
|
||||||
|
private ValidationEntry _entry;
|
||||||
|
|
||||||
|
public Enumerator(
|
||||||
|
ModelMetadata metadata,
|
||||||
|
string key,
|
||||||
|
IEnumerable<KeyValuePair<string, TKey>> keyMappings,
|
||||||
|
IDictionary<TKey, TValue> model)
|
||||||
|
{
|
||||||
|
_metadata = metadata;
|
||||||
|
_key = key;
|
||||||
|
_model = model;
|
||||||
|
|
||||||
|
_keyMappingEnumerator = keyMappings.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValidationEntry Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object IEnumerator.Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return Current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
TValue value;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (!_keyMappingEnumerator.MoveNext())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_model.TryGetValue(_keyMappingEnumerator.Current.Value, out value))
|
||||||
|
{
|
||||||
|
// Skip over entries that we can't find in the dictionary, they will show up as unvalidated.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var key = ModelNames.CreateIndexModelName(_key, _keyMappingEnumerator.Current.Key);
|
||||||
|
var model = value;
|
||||||
|
|
||||||
|
_entry = new ValidationEntry(_metadata, key, model);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,366 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A visitor implementation that interprets <see cref="ValidationStateDictionary"/> to traverse
|
||||||
|
/// a model object graph and perform validation.
|
||||||
|
/// </summary>
|
||||||
|
public class ValidationVisitor
|
||||||
|
{
|
||||||
|
private readonly IModelValidatorProvider _validatorProvider;
|
||||||
|
private readonly IList<IExcludeTypeValidationFilter> _excludeFilters;
|
||||||
|
private readonly ModelStateDictionary _modelState;
|
||||||
|
private readonly ValidationStateDictionary _validationState;
|
||||||
|
|
||||||
|
private object _container;
|
||||||
|
private string _key;
|
||||||
|
private object _model;
|
||||||
|
private ModelMetadata _metadata;
|
||||||
|
private IValidationStrategy _strategy;
|
||||||
|
|
||||||
|
private HashSet<object> _currentPath;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new <see cref="ValidationVisitor"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="validatorProvider">The <see cref="IModelValidatorProvider"/>.</param>
|
||||||
|
/// <param name="excludeFilters">The list of <see cref="IExcludeTypeValidationFilter"/>.</param>
|
||||||
|
/// <param name="modelState">The <see cref="ModelStateDictionary"/>.</param>
|
||||||
|
/// <param name="validationState">The <see cref="ValidationStateDictionary"/>.</param>
|
||||||
|
public ValidationVisitor(
|
||||||
|
IModelValidatorProvider validatorProvider,
|
||||||
|
IList<IExcludeTypeValidationFilter> excludeFilters,
|
||||||
|
ModelStateDictionary modelState,
|
||||||
|
ValidationStateDictionary validationState)
|
||||||
|
{
|
||||||
|
if (validatorProvider == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(validatorProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (excludeFilters == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(excludeFilters));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modelState == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(modelState));
|
||||||
|
}
|
||||||
|
|
||||||
|
_validatorProvider = validatorProvider;
|
||||||
|
_excludeFilters = excludeFilters;
|
||||||
|
_modelState = modelState;
|
||||||
|
_validationState = validationState;
|
||||||
|
|
||||||
|
_currentPath = new HashSet<object>(ReferenceEqualityComparer.Instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validates a object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="metadata">The <see cref="ModelMetadata"/> associated with the model.</param>
|
||||||
|
/// <param name="key">The model prefix key.</param>
|
||||||
|
/// <param name="model">The model object.</param>
|
||||||
|
/// <returns><c>true</c> if the object is valid, otherwise <c>false</c>.</returns>
|
||||||
|
public bool Validate(ModelMetadata metadata, string key, object model)
|
||||||
|
{
|
||||||
|
if (model == null)
|
||||||
|
{
|
||||||
|
if (_modelState.GetValidationState(key) != ModelValidationState.Valid)
|
||||||
|
{
|
||||||
|
_modelState.MarkFieldValid(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Visit(metadata, key, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validates a single node in a model object graph.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns><c>true</c> if the node is valid, otherwise <c>false</c>.</returns>
|
||||||
|
protected virtual bool ValidateNode()
|
||||||
|
{
|
||||||
|
var state = _modelState.GetValidationState(_key);
|
||||||
|
if (state == ModelValidationState.Unvalidated)
|
||||||
|
{
|
||||||
|
var validators = GetValidators(_metadata);
|
||||||
|
|
||||||
|
var count = validators.Count;
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
var context = new ModelValidationContext()
|
||||||
|
{
|
||||||
|
Container = _container,
|
||||||
|
Model = _model,
|
||||||
|
Metadata = _metadata,
|
||||||
|
};
|
||||||
|
|
||||||
|
var results = new List<ModelValidationResult>();
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
results.AddRange(validators[i].Validate(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultsCount = results.Count;
|
||||||
|
for (var i = 0; i < resultsCount; i++)
|
||||||
|
{
|
||||||
|
var result = results[i];
|
||||||
|
var key = ModelNames.CreatePropertyModelName(_key, result.MemberName);
|
||||||
|
_modelState.TryAddModelError(key, result.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
state = _modelState.GetFieldValidationState(_key);
|
||||||
|
if (state == ModelValidationState.Invalid)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If the field has an entry in ModelState, then record it as valid. Don't create
|
||||||
|
// extra entries if they don't exist already.
|
||||||
|
var entry = _modelState[_key];
|
||||||
|
if (entry != null)
|
||||||
|
{
|
||||||
|
entry.ValidationState = ModelValidationState.Valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool Visit(ModelMetadata metadata, string key, object model)
|
||||||
|
{
|
||||||
|
RuntimeHelpers.EnsureSufficientExecutionStack();
|
||||||
|
|
||||||
|
if (model != null && !_currentPath.Add(model))
|
||||||
|
{
|
||||||
|
// This is a cycle, bail.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var entry = GetValidationEntry(model);
|
||||||
|
key = entry?.Key ?? key ?? string.Empty;
|
||||||
|
metadata = entry?.Metadata ?? metadata;
|
||||||
|
var strategy = entry?.Strategy;
|
||||||
|
|
||||||
|
if (_modelState.HasReachedMaxErrors)
|
||||||
|
{
|
||||||
|
SuppressValidation(key);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if ((entry != null && entry.SuppressValidation))
|
||||||
|
{
|
||||||
|
SuppressValidation(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (StateManager.Recurse(this, key, metadata, model, strategy))
|
||||||
|
{
|
||||||
|
if (_metadata.IsEnumerableType)
|
||||||
|
{
|
||||||
|
return VisitEnumerableType();
|
||||||
|
}
|
||||||
|
else if (_metadata.IsComplexType)
|
||||||
|
{
|
||||||
|
return VisitComplexType();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return VisitSimpleType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool VisitEnumerableType()
|
||||||
|
{
|
||||||
|
var isValid = true;
|
||||||
|
|
||||||
|
if (_model != null)
|
||||||
|
{
|
||||||
|
var strategy = _strategy ?? DefaultCollectionValidationStrategy.Instance;
|
||||||
|
isValid = VisitChildren(strategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Double-checking HasReachedMaxErrors just in case this model has no elements.
|
||||||
|
if (isValid && !_modelState.HasReachedMaxErrors)
|
||||||
|
{
|
||||||
|
isValid &= ValidateNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool VisitComplexType()
|
||||||
|
{
|
||||||
|
var isValid = true;
|
||||||
|
|
||||||
|
if (_model != null && ShouldValidateProperties(_metadata))
|
||||||
|
{
|
||||||
|
var strategy = _strategy ?? DefaultComplexObjectValidationStrategy.Instance;
|
||||||
|
isValid = VisitChildren(strategy);
|
||||||
|
}
|
||||||
|
else if (_model != null)
|
||||||
|
{
|
||||||
|
// Suppress validation for the prefix, but we still want to validate the object.
|
||||||
|
SuppressValidation(_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Double-checking HasReachedMaxErrors just in case this model has no properties.
|
||||||
|
if (isValid && !_modelState.HasReachedMaxErrors)
|
||||||
|
{
|
||||||
|
isValid &= ValidateNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool VisitSimpleType()
|
||||||
|
{
|
||||||
|
if (_modelState.HasReachedMaxErrors)
|
||||||
|
{
|
||||||
|
SuppressValidation(_key);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ValidateNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool VisitChildren(IValidationStrategy strategy)
|
||||||
|
{
|
||||||
|
var isValid = true;
|
||||||
|
var enumerator = strategy.GetChildren(_metadata, _key, _model);
|
||||||
|
|
||||||
|
while (enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
var metadata = enumerator.Current.Metadata;
|
||||||
|
var model = enumerator.Current.Model;
|
||||||
|
var key = enumerator.Current.Key;
|
||||||
|
|
||||||
|
isValid &= Visit(metadata, key, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IList<IModelValidator> GetValidators(ModelMetadata metadata)
|
||||||
|
{
|
||||||
|
var context = new ModelValidatorProviderContext(metadata);
|
||||||
|
_validatorProvider.GetValidators(context);
|
||||||
|
return context.Validators.OrderBy(v => v, ValidatorOrderComparer.Instance).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SuppressValidation(string key)
|
||||||
|
{
|
||||||
|
var entries = _modelState.FindKeysWithPrefix(key);
|
||||||
|
foreach (var entry in entries)
|
||||||
|
{
|
||||||
|
entry.Value.ValidationState = ModelValidationState.Skipped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ShouldValidateProperties(ModelMetadata metadata)
|
||||||
|
{
|
||||||
|
var count = _excludeFilters.Count;
|
||||||
|
for (var i = 0; i < _excludeFilters.Count; i++)
|
||||||
|
{
|
||||||
|
if (_excludeFilters[i].IsTypeExcluded(metadata.UnderlyingOrModelType))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ValidationStateEntry GetValidationEntry(object model)
|
||||||
|
{
|
||||||
|
if (model == null || _validationState == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ValidationStateEntry entry;
|
||||||
|
_validationState.TryGetValue(model, out entry);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct StateManager : IDisposable
|
||||||
|
{
|
||||||
|
private readonly ValidationVisitor _visitor;
|
||||||
|
private readonly object _container;
|
||||||
|
private readonly string _key;
|
||||||
|
private readonly ModelMetadata _metadata;
|
||||||
|
private readonly object _model;
|
||||||
|
private readonly object _newModel;
|
||||||
|
private readonly IValidationStrategy _strategy;
|
||||||
|
|
||||||
|
public static StateManager Recurse(
|
||||||
|
ValidationVisitor visitor,
|
||||||
|
string key,
|
||||||
|
ModelMetadata metadata,
|
||||||
|
object model,
|
||||||
|
IValidationStrategy strategy)
|
||||||
|
{
|
||||||
|
var recursifier = new StateManager(visitor, model);
|
||||||
|
|
||||||
|
visitor._container = visitor._model;
|
||||||
|
visitor._key = key;
|
||||||
|
visitor._metadata = metadata;
|
||||||
|
visitor._model = model;
|
||||||
|
visitor._strategy = strategy;
|
||||||
|
|
||||||
|
return recursifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StateManager(ValidationVisitor visitor, object newModel)
|
||||||
|
{
|
||||||
|
_visitor = visitor;
|
||||||
|
_newModel = newModel;
|
||||||
|
|
||||||
|
_container = _visitor._container;
|
||||||
|
_key = _visitor._key;
|
||||||
|
_metadata = _visitor._metadata;
|
||||||
|
_model = _visitor._model;
|
||||||
|
_strategy = _visitor._strategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_visitor._container = _container;
|
||||||
|
_visitor._key = _key;
|
||||||
|
_visitor._metadata = _metadata;
|
||||||
|
_visitor._model = _model;
|
||||||
|
_visitor._strategy = _strategy;
|
||||||
|
|
||||||
|
_visitor._currentPath.Remove(_newModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sorts validators based on whether or not they are 'required'. We want to run
|
||||||
|
// 'required' validators first so that we get the best possible error message.
|
||||||
|
private class ValidatorOrderComparer : IComparer<IModelValidator>
|
||||||
|
{
|
||||||
|
public static readonly ValidatorOrderComparer Instance = new ValidatorOrderComparer();
|
||||||
|
|
||||||
|
public int Compare(IModelValidator x, IModelValidator y)
|
||||||
|
{
|
||||||
|
var xScore = x.IsRequired ? 0 : 1;
|
||||||
|
var yScore = y.IsRequired ? 0 : 1;
|
||||||
|
return xScore.CompareTo(yScore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -33,20 +33,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
|
||||||
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext validationContext)
|
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext validationContext)
|
||||||
{
|
{
|
||||||
var modelExplorer = validationContext.ModelExplorer;
|
var metadata = validationContext.Metadata;
|
||||||
var metadata = modelExplorer.Metadata;
|
|
||||||
|
|
||||||
var memberName = metadata.PropertyName ?? metadata.ModelType.Name;
|
var memberName = metadata.PropertyName ?? metadata.ModelType.Name;
|
||||||
var containerExplorer = modelExplorer.Container;
|
var container = validationContext.Container;
|
||||||
|
|
||||||
var container = containerExplorer?.Model;
|
var context = new ValidationContext(container ?? validationContext.Model)
|
||||||
var context = new ValidationContext(container ?? modelExplorer.Model)
|
|
||||||
{
|
{
|
||||||
DisplayName = metadata.GetDisplayName(),
|
DisplayName = metadata.GetDisplayName(),
|
||||||
MemberName = memberName
|
MemberName = memberName
|
||||||
};
|
};
|
||||||
|
|
||||||
var result = Attribute.GetValidationResult(modelExplorer.Model, context);
|
var result = Attribute.GetValidationResult(validationContext.Model, context);
|
||||||
if (result != ValidationResult.Success)
|
if (result != ValidationResult.Success)
|
||||||
{
|
{
|
||||||
// ModelValidationResult.MemberName is used by invoking validators (such as ModelValidator) to
|
// ModelValidationResult.MemberName is used by invoking validators (such as ModelValidator) to
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
|
||||||
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
|
public IEnumerable<ModelValidationResult> Validate(ModelValidationContext context)
|
||||||
{
|
{
|
||||||
var model = context.ModelExplorer.Model;
|
var model = context.Model;
|
||||||
if (model == null)
|
if (model == null)
|
||||||
{
|
{
|
||||||
return Enumerable.Empty<ModelValidationResult>();
|
return Enumerable.Empty<ModelValidationResult>();
|
||||||
|
|
|
||||||
|
|
@ -1514,18 +1514,12 @@ namespace Microsoft.AspNet.Mvc
|
||||||
MetadataProvider,
|
MetadataProvider,
|
||||||
modelName);
|
modelName);
|
||||||
|
|
||||||
var validationContext = new ModelValidationContext(
|
|
||||||
bindingSource: null,
|
|
||||||
validatorProvider: BindingContext.ValidatorProvider,
|
|
||||||
modelState: ModelState,
|
|
||||||
modelExplorer: modelExplorer);
|
|
||||||
|
|
||||||
ObjectValidator.Validate(
|
ObjectValidator.Validate(
|
||||||
validationContext,
|
BindingContext.ValidatorProvider,
|
||||||
new ModelValidationNode(modelName, modelExplorer.Metadata, model)
|
ModelState,
|
||||||
{
|
validationState: null,
|
||||||
ValidateAllProperties = true
|
prefix: prefix,
|
||||||
});
|
model: model);
|
||||||
return ModelState.IsValid;
|
return ModelState.IsValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -417,18 +417,14 @@ namespace System.Web.Http
|
||||||
{
|
{
|
||||||
var modelExplorer = MetadataProvider.GetModelExplorerForType(typeof(TEntity), entity);
|
var modelExplorer = MetadataProvider.GetModelExplorerForType(typeof(TEntity), entity);
|
||||||
|
|
||||||
var modelValidationContext = new ModelValidationContext(
|
var validatidationState = new ValidationStateDictionary();
|
||||||
bindingSource: null,
|
|
||||||
validatorProvider: BindingContext.ValidatorProvider,
|
|
||||||
modelState: ModelState,
|
|
||||||
modelExplorer: modelExplorer);
|
|
||||||
|
|
||||||
ObjectValidator.Validate(
|
ObjectValidator.Validate(
|
||||||
modelValidationContext,
|
BindingContext.ValidatorProvider,
|
||||||
new ModelValidationNode(keyPrefix, modelExplorer.Metadata, entity)
|
ModelState,
|
||||||
{
|
validatidationState,
|
||||||
ValidateAllProperties = true
|
keyPrefix,
|
||||||
});
|
entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
{
|
{
|
||||||
|
|
@ -18,13 +19,8 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
if (bindingContext.ModelType == typeof(HttpRequestMessage))
|
if (bindingContext.ModelType == typeof(HttpRequestMessage))
|
||||||
{
|
{
|
||||||
var model = bindingContext.OperationBindingContext.HttpContext.GetHttpRequestMessage();
|
var model = bindingContext.OperationBindingContext.HttpContext.GetHttpRequestMessage();
|
||||||
var validationNode =
|
bindingContext.ValidationState.Add(model, new ValidationStateEntry() { SuppressValidation = true });
|
||||||
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, model)
|
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model);
|
||||||
{
|
|
||||||
SuppressValidation = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model, validationNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ModelBindingResult.NoResultAsync;
|
return ModelBindingResult.NoResultAsync;
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
{
|
{
|
||||||
context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(string));
|
context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(string));
|
||||||
})
|
})
|
||||||
.Returns(ModelBindingResult.SuccessAsync(string.Empty, value, validationNode: null));
|
.Returns(ModelBindingResult.SuccessAsync(string.Empty, value));
|
||||||
|
|
||||||
var actionContext = new ActionContext(
|
var actionContext = new ActionContext(
|
||||||
new DefaultHttpContext(),
|
new DefaultHttpContext(),
|
||||||
|
|
@ -153,19 +153,30 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
var actionContext = GetActionContext(actionDescriptor);
|
var actionContext = GetActionContext(actionDescriptor);
|
||||||
var actionBindingContext = GetActionBindingContext();
|
var actionBindingContext = GetActionBindingContext();
|
||||||
|
|
||||||
var mockValidatorProvider = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
var mockValidator = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
||||||
mockValidatorProvider
|
mockValidator
|
||||||
.Setup(o => o.Validate(It.IsAny<ModelValidationContext>(), It.IsAny<ModelValidationNode>()))
|
.Setup(o => o.Validate(
|
||||||
.Verifiable();
|
It.IsAny<IModelValidatorProvider>(),
|
||||||
var argumentBinder = GetArgumentBinder(mockValidatorProvider.Object);
|
It.IsAny<ModelStateDictionary>(),
|
||||||
|
It.IsAny<ValidationStateDictionary>(),
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<object>()));
|
||||||
|
|
||||||
|
var argumentBinder = GetArgumentBinder(mockValidator.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await argumentBinder
|
var result = await argumentBinder
|
||||||
.BindActionArgumentsAsync(actionContext, actionBindingContext, new TestController());
|
.BindActionArgumentsAsync(actionContext, actionBindingContext, new TestController());
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
mockValidatorProvider.Verify(
|
mockValidator
|
||||||
o => o.Validate(It.IsAny<ModelValidationContext>(), It.IsAny<ModelValidationNode>()), Times.Once());
|
.Verify(o => o.Validate(
|
||||||
|
It.IsAny<IModelValidatorProvider>(),
|
||||||
|
It.IsAny<ModelStateDictionary>(),
|
||||||
|
It.IsAny<ValidationStateDictionary>(),
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<object>()),
|
||||||
|
Times.Once());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -197,19 +208,29 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
ModelBinder = binder.Object,
|
ModelBinder = binder.Object,
|
||||||
};
|
};
|
||||||
|
|
||||||
var mockValidatorProvider = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
var mockValidator = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
||||||
mockValidatorProvider
|
mockValidator
|
||||||
.Setup(o => o.Validate(It.IsAny<ModelValidationContext>(), It.IsAny<ModelValidationNode>()))
|
.Setup(o => o.Validate(
|
||||||
.Verifiable();
|
It.IsAny<IModelValidatorProvider>(),
|
||||||
var argumentBinder = GetArgumentBinder(mockValidatorProvider.Object);
|
It.IsAny<ModelStateDictionary>(),
|
||||||
|
It.IsAny<ValidationStateDictionary>(),
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<object>()));
|
||||||
|
|
||||||
|
var argumentBinder = GetArgumentBinder(mockValidator.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await argumentBinder
|
var result = await argumentBinder
|
||||||
.BindActionArgumentsAsync(actionContext, actionBindingContext, new TestController());
|
.BindActionArgumentsAsync(actionContext, actionBindingContext, new TestController());
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
mockValidatorProvider.Verify(
|
mockValidator
|
||||||
o => o.Validate(It.IsAny<ModelValidationContext>(), It.IsAny<ModelValidationNode>()),
|
.Verify(o => o.Validate(
|
||||||
|
It.IsAny<IModelValidatorProvider>(),
|
||||||
|
It.IsAny<ModelStateDictionary>(),
|
||||||
|
It.IsAny<ValidationStateDictionary>(),
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<object>()),
|
||||||
Times.Never());
|
Times.Never());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -228,19 +249,30 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
var actionContext = GetActionContext(actionDescriptor);
|
var actionContext = GetActionContext(actionDescriptor);
|
||||||
var actionBindingContext = GetActionBindingContext();
|
var actionBindingContext = GetActionBindingContext();
|
||||||
|
|
||||||
var mockValidatorProvider = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
var mockValidator = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
||||||
mockValidatorProvider
|
mockValidator
|
||||||
.Setup(o => o.Validate(It.IsAny<ModelValidationContext>(), It.IsAny<ModelValidationNode>()))
|
.Setup(o => o.Validate(
|
||||||
.Verifiable();
|
It.IsAny<IModelValidatorProvider>(),
|
||||||
var argumentBinder = GetArgumentBinder(mockValidatorProvider.Object);
|
It.IsAny<ModelStateDictionary>(),
|
||||||
|
It.IsAny<ValidationStateDictionary>(),
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<object>()));
|
||||||
|
|
||||||
|
var argumentBinder = GetArgumentBinder(mockValidator.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await argumentBinder
|
var result = await argumentBinder
|
||||||
.BindActionArgumentsAsync(actionContext, actionBindingContext, new TestController());
|
.BindActionArgumentsAsync(actionContext, actionBindingContext, new TestController());
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
mockValidatorProvider.Verify(
|
mockValidator
|
||||||
o => o.Validate(It.IsAny<ModelValidationContext>(), It.IsAny<ModelValidationNode>()), Times.Once());
|
.Verify(o => o.Validate(
|
||||||
|
It.IsAny<IModelValidatorProvider>(),
|
||||||
|
It.IsAny<ModelStateDictionary>(),
|
||||||
|
It.IsAny<ValidationStateDictionary>(),
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<object>()),
|
||||||
|
Times.Once());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -271,20 +303,29 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
ModelBinder = binder.Object,
|
ModelBinder = binder.Object,
|
||||||
};
|
};
|
||||||
|
|
||||||
var mockValidatorProvider = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
var mockValidator = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
||||||
mockValidatorProvider
|
mockValidator
|
||||||
.Setup(o => o.Validate(It.IsAny<ModelValidationContext>(), It.IsAny<ModelValidationNode>()))
|
.Setup(o => o.Validate(
|
||||||
.Verifiable();
|
It.IsAny<IModelValidatorProvider>(),
|
||||||
|
It.IsAny<ModelStateDictionary>(),
|
||||||
|
It.IsAny<ValidationStateDictionary>(),
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<object>()));
|
||||||
|
|
||||||
var argumentBinder = GetArgumentBinder(mockValidatorProvider.Object);
|
var argumentBinder = GetArgumentBinder(mockValidator.Object);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await argumentBinder
|
var result = await argumentBinder
|
||||||
.BindActionArgumentsAsync(actionContext, actionBindingContext, new TestController());
|
.BindActionArgumentsAsync(actionContext, actionBindingContext, new TestController());
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
mockValidatorProvider.Verify(
|
mockValidator
|
||||||
o => o.Validate(It.IsAny<ModelValidationContext>(), It.IsAny<ModelValidationNode>()),
|
.Verify(o => o.Validate(
|
||||||
|
It.IsAny<IModelValidatorProvider>(),
|
||||||
|
It.IsAny<ModelStateDictionary>(),
|
||||||
|
It.IsAny<ValidationStateDictionary>(),
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<object>()),
|
||||||
Times.Never());
|
Times.Never());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -363,7 +404,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
var binder = new Mock<IModelBinder>();
|
var binder = new Mock<IModelBinder>();
|
||||||
binder
|
binder
|
||||||
.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||||
.Returns(ModelBindingResult.SuccessAsync(string.Empty, model: null, validationNode: null));
|
.Returns(ModelBindingResult.SuccessAsync(string.Empty, model: null));
|
||||||
|
|
||||||
var actionBindingContext = new ActionBindingContext()
|
var actionBindingContext = new ActionBindingContext()
|
||||||
{
|
{
|
||||||
|
|
@ -401,7 +442,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
var binder = new Mock<IModelBinder>();
|
var binder = new Mock<IModelBinder>();
|
||||||
binder
|
binder
|
||||||
.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||||
.Returns(ModelBindingResult.SuccessAsync(key: string.Empty, model: null, validationNode: null));
|
.Returns(ModelBindingResult.SuccessAsync(key: string.Empty, model: null));
|
||||||
|
|
||||||
var actionBindingContext = new ActionBindingContext()
|
var actionBindingContext = new ActionBindingContext()
|
||||||
{
|
{
|
||||||
|
|
@ -545,7 +586,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
object model;
|
object model;
|
||||||
if (inputPropertyValues.TryGetValue(bindingContext.ModelName, out model))
|
if (inputPropertyValues.TryGetValue(bindingContext.ModelName, out model))
|
||||||
{
|
{
|
||||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model, validationNode: null);
|
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -600,8 +641,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||||
.Returns<ModelBindingContext>(mbc =>
|
.Returns<ModelBindingContext>(mbc =>
|
||||||
{
|
{
|
||||||
var validationNode = new ModelValidationNode(string.Empty, mbc.ModelMetadata, model);
|
return ModelBindingResult.SuccessAsync(string.Empty, model);
|
||||||
return ModelBindingResult.SuccessAsync(string.Empty, model, validationNode: validationNode);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return new ActionBindingContext()
|
return new ActionBindingContext()
|
||||||
|
|
@ -614,10 +654,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
{
|
{
|
||||||
if (validator == null)
|
if (validator == null)
|
||||||
{
|
{
|
||||||
var mockValidator = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
validator = CreateMockValidator();
|
||||||
mockValidator.Setup(
|
|
||||||
o => o.Validate(It.IsAny<ModelValidationContext>(), It.IsAny<ModelValidationNode>()));
|
|
||||||
validator = mockValidator.Object;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DefaultControllerActionArgumentBinder(
|
return new DefaultControllerActionArgumentBinder(
|
||||||
|
|
@ -625,6 +662,19 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
||||||
validator);
|
validator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IObjectModelValidator CreateMockValidator()
|
||||||
|
{
|
||||||
|
var mockValidator = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
||||||
|
mockValidator
|
||||||
|
.Setup(o => o.Validate(
|
||||||
|
It.IsAny<IModelValidatorProvider>(),
|
||||||
|
It.IsAny<ModelStateDictionary>(),
|
||||||
|
It.IsAny<ValidationStateDictionary>(),
|
||||||
|
It.IsAny<string>(),
|
||||||
|
It.IsAny<object>()));
|
||||||
|
return mockValidator.Object;
|
||||||
|
}
|
||||||
|
|
||||||
// No need for bind-related attributes on properties in this controller class. Properties are added directly
|
// No need for bind-related attributes on properties in this controller class. Properties are added directly
|
||||||
// to the BoundProperties collection, bypassing usual requirements.
|
// to the BoundProperties collection, bypassing usual requirements.
|
||||||
private class TestController
|
private class TestController
|
||||||
|
|
|
||||||
|
|
@ -62,10 +62,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
Assert.Empty(Assert.IsType<string[]>(result.Model));
|
Assert.Empty(Assert.IsType<string[]>(result.Model));
|
||||||
Assert.Equal("modelName", result.Key);
|
Assert.Equal("modelName", result.Key);
|
||||||
Assert.True(result.IsModelSet);
|
Assert.True(result.IsModelSet);
|
||||||
|
|
||||||
Assert.Same(result.ValidationNode.Model, result.Model);
|
|
||||||
Assert.Same(result.ValidationNode.Key, result.Key);
|
|
||||||
Assert.Same(result.ValidationNode.ModelMetadata, context.ModelMetadata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -173,7 +169,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
if (value != ValueProviderResult.None)
|
if (value != ValueProviderResult.None)
|
||||||
{
|
{
|
||||||
var model = value.ConvertTo(mbc.ModelType);
|
var model = value.ConvertTo(mbc.ModelType);
|
||||||
return ModelBindingResult.SuccessAsync(mbc.ModelName, model, validationNode: null);
|
return ModelBindingResult.SuccessAsync(mbc.ModelName, model);
|
||||||
}
|
}
|
||||||
return ModelBindingResult.NoResultAsync;
|
return ModelBindingResult.NoResultAsync;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -67,9 +67,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
var p = (Person)binderResult.Model;
|
var p = (Person)binderResult.Model;
|
||||||
Assert.Equal(model.Age, p.Age);
|
Assert.Equal(model.Age, p.Age);
|
||||||
Assert.Equal(model.Name, p.Name);
|
Assert.Equal(model.Name, p.Name);
|
||||||
Assert.NotNull(binderResult.ValidationNode);
|
|
||||||
Assert.Equal(bindingContext.ModelName, binderResult.ValidationNode.Key);
|
|
||||||
Assert.Same(binderResult.Model, binderResult.ValidationNode.Model);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -141,9 +138,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
|
|
||||||
public Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext)
|
public Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext)
|
||||||
{
|
{
|
||||||
var validationNode =
|
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, _model);
|
||||||
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, _model);
|
|
||||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, _model, validationNode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,13 +50,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
mockInputFormatter.Verify(v => v.ReadAsync(It.IsAny<InputFormatterContext>()), Times.Once);
|
mockInputFormatter.Verify(v => v.ReadAsync(It.IsAny<InputFormatterContext>()), Times.Once);
|
||||||
Assert.NotNull(binderResult);
|
Assert.NotNull(binderResult);
|
||||||
Assert.True(binderResult.IsModelSet);
|
Assert.True(binderResult.IsModelSet);
|
||||||
Assert.NotNull(binderResult.ValidationNode);
|
|
||||||
Assert.True(binderResult.ValidationNode.ValidateAllProperties);
|
|
||||||
Assert.False(binderResult.ValidationNode.SuppressValidation);
|
|
||||||
Assert.Empty(binderResult.ValidationNode.ChildNodes);
|
|
||||||
Assert.Equal(binderResult.Key, binderResult.ValidationNode.Key);
|
|
||||||
Assert.Equal(bindingContext.ModelMetadata, binderResult.ValidationNode.ModelMetadata);
|
|
||||||
Assert.Same(binderResult.Model, binderResult.ValidationNode.Model);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -78,7 +71,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
// Returns non-null because it understands the metadata type.
|
// Returns non-null because it understands the metadata type.
|
||||||
Assert.NotNull(binderResult);
|
Assert.NotNull(binderResult);
|
||||||
Assert.False(binderResult.IsModelSet);
|
Assert.False(binderResult.IsModelSet);
|
||||||
Assert.Null(binderResult.ValidationNode);
|
|
||||||
Assert.Null(binderResult.Model);
|
Assert.Null(binderResult.Model);
|
||||||
|
|
||||||
// Key is empty because this was a top-level binding.
|
// Key is empty because this was a top-level binding.
|
||||||
|
|
@ -104,7 +96,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotNull(binderResult);
|
Assert.NotNull(binderResult);
|
||||||
Assert.False(binderResult.IsModelSet);
|
Assert.False(binderResult.IsModelSet);
|
||||||
Assert.Null(binderResult.ValidationNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -172,7 +163,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
// Returns non-null because it understands the metadata type.
|
// Returns non-null because it understands the metadata type.
|
||||||
Assert.NotNull(binderResult);
|
Assert.NotNull(binderResult);
|
||||||
Assert.False(binderResult.IsModelSet);
|
Assert.False(binderResult.IsModelSet);
|
||||||
Assert.Null(binderResult.ValidationNode);
|
|
||||||
Assert.Null(binderResult.Model);
|
Assert.Null(binderResult.Model);
|
||||||
|
|
||||||
// Key is empty because this was a top-level binding.
|
// Key is empty because this was a top-level binding.
|
||||||
|
|
@ -209,7 +199,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
Assert.NotNull(binderResult);
|
Assert.NotNull(binderResult);
|
||||||
Assert.False(binderResult.IsModelSet);
|
Assert.False(binderResult.IsModelSet);
|
||||||
Assert.Null(binderResult.Model);
|
Assert.Null(binderResult.Model);
|
||||||
Assert.Null(binderResult.ValidationNode);
|
|
||||||
|
|
||||||
// Key is empty because this was a top-level binding.
|
// Key is empty because this was a top-level binding.
|
||||||
var entry = Assert.Single(bindingContext.ModelState);
|
var entry = Assert.Single(bindingContext.ModelState);
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||||
Assert.True(result.IsModelSet);
|
Assert.True(result.IsModelSet);
|
||||||
Assert.Equal(bindingContext.OperationBindingContext.HttpContext.RequestAborted, result.Model);
|
Assert.Equal(bindingContext.OperationBindingContext.HttpContext.RequestAborted, result.Model);
|
||||||
Assert.NotNull(result.ValidationNode);
|
|
||||||
Assert.True(result.ValidationNode.SuppressValidation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ using System.Linq;
|
||||||
#endif
|
#endif
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http.Internal;
|
using Microsoft.AspNet.Http.Internal;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
#if DNX451
|
#if DNX451
|
||||||
using Moq;
|
using Moq;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -32,13 +33,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
var binder = new CollectionModelBinder<int>();
|
var binder = new CollectionModelBinder<int>();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var boundCollection = await binder.BindComplexCollectionFromIndexes(bindingContext, new[] { "foo", "bar", "baz" });
|
var collectionResult = await binder.BindComplexCollectionFromIndexes(
|
||||||
|
bindingContext,
|
||||||
|
new[] { "foo", "bar", "baz" });
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(new[] { 42, 0, 200 }, boundCollection.Model.ToArray());
|
Assert.Equal(new[] { 42, 0, 200 }, collectionResult.Model.ToArray());
|
||||||
Assert.Equal(
|
|
||||||
new[] { "someName[foo]", "someName[baz]" },
|
// This requires a non-default IValidationStrategy
|
||||||
boundCollection.ValidationNode.ChildNodes.Select(o => o.Key).ToArray());
|
var strategy = Assert.IsType<ExplicitIndexCollectionValidationStrategy>(collectionResult.ValidationStrategy);
|
||||||
|
Assert.Equal(new[] { "foo", "bar", "baz" }, strategy.ElementKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -59,9 +63,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(new[] { 42, 100 }, boundCollection.Model.ToArray());
|
Assert.Equal(new[] { 42, 100 }, boundCollection.Model.ToArray());
|
||||||
Assert.Equal(
|
|
||||||
new[] { "someName[0]", "someName[1]" },
|
// This uses the default IValidationStrategy
|
||||||
boundCollection.ValidationNode.ChildNodes.Select(o => o.Key).ToArray());
|
Assert.DoesNotContain(boundCollection, bindingContext.ValidationState.Keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -197,7 +201,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||||
Assert.True(result.IsModelSet);
|
Assert.True(result.IsModelSet);
|
||||||
Assert.NotNull(result.Model);
|
Assert.NotNull(result.Model);
|
||||||
Assert.NotNull(result.ValidationNode);
|
|
||||||
|
|
||||||
var model = Assert.IsType<List<int>>(result.Model);
|
var model = Assert.IsType<List<int>>(result.Model);
|
||||||
Assert.Empty(model);
|
Assert.Empty(model);
|
||||||
|
|
@ -252,10 +255,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
Assert.Empty(Assert.IsType<List<string>>(result.Model));
|
Assert.Empty(Assert.IsType<List<string>>(result.Model));
|
||||||
Assert.Equal("modelName", result.Key);
|
Assert.Equal("modelName", result.Key);
|
||||||
Assert.True(result.IsModelSet);
|
Assert.True(result.IsModelSet);
|
||||||
|
|
||||||
Assert.Same(result.ValidationNode.Model, result.Model);
|
|
||||||
Assert.Same(result.ValidationNode.Key, result.Key);
|
|
||||||
Assert.Same(result.ValidationNode.ModelMetadata, context.ModelMetadata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup like CollectionModelBinder_CreatesEmptyCollection_IfIsTopLevelObject except
|
// Setup like CollectionModelBinder_CreatesEmptyCollection_IfIsTopLevelObject except
|
||||||
|
|
@ -290,10 +289,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
Assert.Empty(list);
|
Assert.Empty(list);
|
||||||
Assert.Equal("modelName", result.Key);
|
Assert.Equal("modelName", result.Key);
|
||||||
Assert.True(result.IsModelSet);
|
Assert.True(result.IsModelSet);
|
||||||
|
|
||||||
Assert.Same(result.ValidationNode.Model, result.Model);
|
|
||||||
Assert.Same(result.ValidationNode.Key, result.Key);
|
|
||||||
Assert.Same(result.ValidationNode.ModelMetadata, context.ModelMetadata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -361,14 +356,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
// Arrange
|
// Arrange
|
||||||
var culture = new CultureInfo("fr-FR");
|
var culture = new CultureInfo("fr-FR");
|
||||||
var bindingContext = GetModelBindingContext(new SimpleValueProvider());
|
var bindingContext = GetModelBindingContext(new SimpleValueProvider());
|
||||||
ModelValidationNode childValidationNode = null;
|
|
||||||
Mock.Get<IModelBinder>(bindingContext.OperationBindingContext.ModelBinder)
|
Mock.Get<IModelBinder>(bindingContext.OperationBindingContext.ModelBinder)
|
||||||
.Setup(o => o.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
.Setup(o => o.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||||
.Returns((ModelBindingContext mbc) =>
|
.Returns((ModelBindingContext mbc) =>
|
||||||
{
|
{
|
||||||
Assert.Equal("someName", mbc.ModelName);
|
Assert.Equal("someName", mbc.ModelName);
|
||||||
childValidationNode = new ModelValidationNode("someName", mbc.ModelMetadata, mbc.Model);
|
return ModelBindingResult.SuccessAsync(mbc.ModelName, 42);
|
||||||
return ModelBindingResult.SuccessAsync(mbc.ModelName, 42, childValidationNode);
|
|
||||||
});
|
});
|
||||||
var modelBinder = new CollectionModelBinder<int>();
|
var modelBinder = new CollectionModelBinder<int>();
|
||||||
|
|
||||||
|
|
@ -379,7 +373,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(new[] { 42 }, boundCollection.Model.ToArray());
|
Assert.Equal(new[] { 42 }, boundCollection.Model.ToArray());
|
||||||
Assert.Equal(new[] { childValidationNode }, boundCollection.ValidationNode.ChildNodes.ToArray());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ModelBindingContext GetModelBindingContext(
|
private static ModelBindingContext GetModelBindingContext(
|
||||||
|
|
@ -399,7 +392,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
{
|
{
|
||||||
ModelBinder = CreateIntBinder(),
|
ModelBinder = CreateIntBinder(),
|
||||||
MetadataProvider = metadataProvider
|
MetadataProvider = metadataProvider
|
||||||
}
|
},
|
||||||
|
ValidationState = new ValidationStateDictionary(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return bindingContext;
|
return bindingContext;
|
||||||
|
|
@ -425,8 +419,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var validationNode = new ModelValidationNode(mbc.ModelName, mbc.ModelMetadata, model);
|
return ModelBindingResult.SuccessAsync(mbc.ModelName, model);
|
||||||
return ModelBindingResult.SuccessAsync(mbc.ModelName, model, validationNode);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return mockIntBinder.Object;
|
return mockIntBinder.Object;
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -28,6 +29,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
{
|
{
|
||||||
{ "someName", "dummyValue" }
|
{ "someName", "dummyValue" }
|
||||||
},
|
},
|
||||||
|
ValidationState = new ValidationStateDictionary(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var mockIntBinder = new Mock<IModelBinder>();
|
var mockIntBinder = new Mock<IModelBinder>();
|
||||||
|
|
@ -40,7 +42,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
Assert.Equal("someName", context.ModelName);
|
Assert.Equal("someName", context.ModelName);
|
||||||
Assert.Same(bindingContext.ValueProvider, context.ValueProvider);
|
Assert.Same(bindingContext.ValueProvider, context.ValueProvider);
|
||||||
|
|
||||||
return ModelBindingResult.SuccessAsync("someName", 42, validationNode: null);
|
return ModelBindingResult.SuccessAsync("someName", 42);
|
||||||
});
|
});
|
||||||
var shimBinder = CreateCompositeBinder(mockIntBinder.Object);
|
var shimBinder = CreateCompositeBinder(mockIntBinder.Object);
|
||||||
|
|
||||||
|
|
@ -53,6 +55,96 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
Assert.Equal(42, result.Model);
|
Assert.Equal(42, result.Model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task BindModel_SuccessfulBind_SetsValidationStateAtTopLevel()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var bindingContext = new ModelBindingContext
|
||||||
|
{
|
||||||
|
FallbackToEmptyPrefix = true,
|
||||||
|
IsTopLevelObject = true,
|
||||||
|
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(int)),
|
||||||
|
ModelName = "someName",
|
||||||
|
ModelState = new ModelStateDictionary(),
|
||||||
|
OperationBindingContext = new OperationBindingContext(),
|
||||||
|
ValueProvider = new SimpleValueProvider
|
||||||
|
{
|
||||||
|
{ "someName", "dummyValue" }
|
||||||
|
},
|
||||||
|
ValidationState = new ValidationStateDictionary(),
|
||||||
|
};
|
||||||
|
|
||||||
|
var mockIntBinder = new Mock<IModelBinder>();
|
||||||
|
mockIntBinder
|
||||||
|
.Setup(o => o.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||||
|
.Returns(
|
||||||
|
delegate (ModelBindingContext context)
|
||||||
|
{
|
||||||
|
Assert.Same(bindingContext.ModelMetadata, context.ModelMetadata);
|
||||||
|
Assert.Equal("someName", context.ModelName);
|
||||||
|
Assert.Same(bindingContext.ValueProvider, context.ValueProvider);
|
||||||
|
|
||||||
|
return ModelBindingResult.SuccessAsync("someName", 42);
|
||||||
|
});
|
||||||
|
var shimBinder = CreateCompositeBinder(mockIntBinder.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await shimBinder.BindModelAsync(bindingContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||||
|
Assert.True(result.IsModelSet);
|
||||||
|
Assert.Equal(42, result.Model);
|
||||||
|
|
||||||
|
Assert.Contains(result.Model, bindingContext.ValidationState.Keys);
|
||||||
|
var entry = bindingContext.ValidationState[result.Model];
|
||||||
|
Assert.Equal("someName", entry.Key);
|
||||||
|
Assert.Same(bindingContext.ModelMetadata, entry.Metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task BindModel_SuccessfulBind_DoesNotSetValidationState_WhenNotTopLevel()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var bindingContext = new ModelBindingContext
|
||||||
|
{
|
||||||
|
FallbackToEmptyPrefix = true,
|
||||||
|
ModelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(int)),
|
||||||
|
ModelName = "someName",
|
||||||
|
ModelState = new ModelStateDictionary(),
|
||||||
|
OperationBindingContext = new OperationBindingContext(),
|
||||||
|
ValueProvider = new SimpleValueProvider
|
||||||
|
{
|
||||||
|
{ "someName", "dummyValue" }
|
||||||
|
},
|
||||||
|
ValidationState = new ValidationStateDictionary(),
|
||||||
|
};
|
||||||
|
|
||||||
|
var mockIntBinder = new Mock<IModelBinder>();
|
||||||
|
mockIntBinder
|
||||||
|
.Setup(o => o.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||||
|
.Returns(
|
||||||
|
delegate (ModelBindingContext context)
|
||||||
|
{
|
||||||
|
Assert.Same(bindingContext.ModelMetadata, context.ModelMetadata);
|
||||||
|
Assert.Equal("someName", context.ModelName);
|
||||||
|
Assert.Same(bindingContext.ValueProvider, context.ValueProvider);
|
||||||
|
|
||||||
|
return ModelBindingResult.SuccessAsync("someName", 42);
|
||||||
|
});
|
||||||
|
var shimBinder = CreateCompositeBinder(mockIntBinder.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await shimBinder.BindModelAsync(bindingContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||||
|
Assert.True(result.IsModelSet);
|
||||||
|
Assert.Equal(42, result.Model);
|
||||||
|
|
||||||
|
Assert.Empty(bindingContext.ValidationState);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task BindModel_SuccessfulBind_ComplexTypeFallback_ReturnsModel()
|
public async Task BindModel_SuccessfulBind_ComplexTypeFallback_ReturnsModel()
|
||||||
{
|
{
|
||||||
|
|
@ -71,6 +163,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
{
|
{
|
||||||
{ "someOtherName", "dummyValue" }
|
{ "someOtherName", "dummyValue" }
|
||||||
},
|
},
|
||||||
|
ValidationState = new ValidationStateDictionary(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var mockIntBinder = new Mock<IModelBinder>();
|
var mockIntBinder = new Mock<IModelBinder>();
|
||||||
|
|
@ -88,7 +181,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
Assert.Equal("", mbc.ModelName);
|
Assert.Equal("", mbc.ModelName);
|
||||||
Assert.Same(bindingContext.ValueProvider, mbc.ValueProvider);
|
Assert.Same(bindingContext.ValueProvider, mbc.ValueProvider);
|
||||||
|
|
||||||
return ModelBindingResult.SuccessAsync(string.Empty, expectedModel, validationNode: null);
|
return ModelBindingResult.SuccessAsync(string.Empty, expectedModel);
|
||||||
});
|
});
|
||||||
|
|
||||||
var shimBinder = CreateCompositeBinder(mockIntBinder.Object);
|
var shimBinder = CreateCompositeBinder(mockIntBinder.Object);
|
||||||
|
|
@ -223,7 +316,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
var modelBinder = new Mock<IModelBinder>();
|
var modelBinder = new Mock<IModelBinder>();
|
||||||
modelBinder
|
modelBinder
|
||||||
.Setup(mb => mb.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
.Setup(mb => mb.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||||
.Returns(ModelBindingResult.SuccessAsync("someName", model: null, validationNode: null));
|
.Returns(ModelBindingResult.SuccessAsync("someName", model: null));
|
||||||
|
|
||||||
var composite = CreateCompositeBinder(modelBinder.Object);
|
var composite = CreateCompositeBinder(modelBinder.Object);
|
||||||
|
|
||||||
|
|
@ -308,26 +401,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
var model = Assert.IsType<SimplePropertiesModel>(result.Model);
|
var model = Assert.IsType<SimplePropertiesModel>(result.Model);
|
||||||
Assert.Equal("firstName-value", model.FirstName);
|
Assert.Equal("firstName-value", model.FirstName);
|
||||||
Assert.Equal("lastName-value", model.LastName);
|
Assert.Equal("lastName-value", model.LastName);
|
||||||
|
|
||||||
Assert.NotNull(result.ValidationNode);
|
|
||||||
Assert.Equal(2, result.ValidationNode.ChildNodes.Count);
|
|
||||||
Assert.Equal("", result.ValidationNode.Key);
|
|
||||||
Assert.Equal(bindingContext.ModelMetadata, result.ValidationNode.ModelMetadata);
|
|
||||||
model = Assert.IsType<SimplePropertiesModel>(result.ValidationNode.Model);
|
|
||||||
Assert.Equal("firstName-value", model.FirstName);
|
|
||||||
Assert.Equal("lastName-value", model.LastName);
|
|
||||||
|
|
||||||
Assert.Equal(2, result.ValidationNode.ChildNodes.Count);
|
|
||||||
|
|
||||||
var validationNode = result.ValidationNode.ChildNodes[0];
|
|
||||||
Assert.Equal("FirstName", validationNode.Key);
|
|
||||||
Assert.Equal("firstName-value", validationNode.Model);
|
|
||||||
Assert.Empty(validationNode.ChildNodes);
|
|
||||||
|
|
||||||
validationNode = result.ValidationNode.ChildNodes[1];
|
|
||||||
Assert.Equal("LastName", validationNode.Key);
|
|
||||||
Assert.Equal("lastName-value", validationNode.Model);
|
|
||||||
Assert.Empty(validationNode.ChildNodes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -415,15 +488,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var valueProvider = new SimpleValueProvider();
|
var valueProvider = new SimpleValueProvider();
|
||||||
ModelValidationNode validationNode = null;
|
|
||||||
|
|
||||||
var mockBinder = new Mock<IModelBinder>();
|
var mockBinder = new Mock<IModelBinder>();
|
||||||
mockBinder
|
mockBinder
|
||||||
.Setup(o => o.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
.Setup(o => o.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||||
.Returns((ModelBindingContext context) =>
|
.Returns((ModelBindingContext context) =>
|
||||||
{
|
{
|
||||||
validationNode = new ModelValidationNode("someName", context.ModelMetadata, 42);
|
return ModelBindingResult.SuccessAsync("someName", 42);
|
||||||
return ModelBindingResult.SuccessAsync("someName", 42, validationNode);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var binder = CreateCompositeBinder(mockBinder.Object);
|
var binder = CreateCompositeBinder(mockBinder.Object);
|
||||||
|
|
@ -435,7 +506,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||||
Assert.True(result.IsModelSet);
|
Assert.True(result.IsModelSet);
|
||||||
Assert.Same(validationNode, result.ValidationNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ModelBindingContext CreateBindingContext(
|
private static ModelBindingContext CreateBindingContext(
|
||||||
|
|
@ -456,7 +526,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
{
|
{
|
||||||
MetadataProvider = metadataProvider,
|
MetadataProvider = metadataProvider,
|
||||||
ModelBinder = binder,
|
ModelBinder = binder,
|
||||||
}
|
},
|
||||||
|
ValidationState = new ValidationStateDictionary(),
|
||||||
};
|
};
|
||||||
return bindingContext;
|
return bindingContext;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http.Internal;
|
using Microsoft.AspNet.Http.Internal;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Framework.Primitives;
|
using Microsoft.Framework.Primitives;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -45,6 +46,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
Assert.Equal(2, dictionary.Count);
|
Assert.Equal(2, dictionary.Count);
|
||||||
Assert.Equal("forty-two", dictionary[42]);
|
Assert.Equal("forty-two", dictionary[42]);
|
||||||
Assert.Equal("eighty-four", dictionary[84]);
|
Assert.Equal("eighty-four", dictionary[84]);
|
||||||
|
|
||||||
|
// This uses the default IValidationStrategy
|
||||||
|
Assert.DoesNotContain(result.Model, bindingContext.ValidationState.Keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -78,6 +82,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
Assert.Equal(2, dictionary.Count);
|
Assert.Equal(2, dictionary.Count);
|
||||||
Assert.Equal("forty-two", dictionary[42]);
|
Assert.Equal("forty-two", dictionary[42]);
|
||||||
Assert.Equal("eighty-four", dictionary[84]);
|
Assert.Equal("eighty-four", dictionary[84]);
|
||||||
|
|
||||||
|
// This uses the default IValidationStrategy
|
||||||
|
Assert.DoesNotContain(result.Model, bindingContext.ValidationState.Keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
// modelName, keyFormat, dictionary
|
// modelName, keyFormat, dictionary
|
||||||
|
|
@ -135,7 +142,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||||
Assert.True(result.IsModelSet);
|
Assert.True(result.IsModelSet);
|
||||||
Assert.Equal(modelName, result.Key);
|
Assert.Equal(modelName, result.Key);
|
||||||
Assert.NotNull(result.ValidationNode);
|
|
||||||
|
|
||||||
var resultDictionary = Assert.IsAssignableFrom<IDictionary<string, string>>(result.Model);
|
var resultDictionary = Assert.IsAssignableFrom<IDictionary<string, string>>(result.Model);
|
||||||
Assert.Equal(dictionary, resultDictionary);
|
Assert.Equal(dictionary, resultDictionary);
|
||||||
|
|
@ -172,7 +178,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||||
Assert.True(result.IsModelSet);
|
Assert.True(result.IsModelSet);
|
||||||
Assert.Equal("prefix", result.Key);
|
Assert.Equal("prefix", result.Key);
|
||||||
Assert.NotNull(result.ValidationNode);
|
|
||||||
|
|
||||||
var resultDictionary = Assert.IsAssignableFrom<IDictionary<string, string>>(result.Model);
|
var resultDictionary = Assert.IsAssignableFrom<IDictionary<string, string>>(result.Model);
|
||||||
Assert.Empty(resultDictionary);
|
Assert.Empty(resultDictionary);
|
||||||
|
|
@ -223,7 +228,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||||
Assert.True(result.IsModelSet);
|
Assert.True(result.IsModelSet);
|
||||||
Assert.Equal("prefix", result.Key);
|
Assert.Equal("prefix", result.Key);
|
||||||
Assert.NotNull(result.ValidationNode);
|
|
||||||
|
|
||||||
var resultDictionary = Assert.IsAssignableFrom<IDictionary<long, int>>(result.Model);
|
var resultDictionary = Assert.IsAssignableFrom<IDictionary<long, int>>(result.Model);
|
||||||
Assert.Equal(dictionary, resultDictionary);
|
Assert.Equal(dictionary, resultDictionary);
|
||||||
|
|
@ -264,10 +268,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||||
Assert.True(result.IsModelSet);
|
Assert.True(result.IsModelSet);
|
||||||
Assert.Equal("prefix", result.Key);
|
Assert.Equal("prefix", result.Key);
|
||||||
Assert.NotNull(result.ValidationNode);
|
|
||||||
|
|
||||||
var resultDictionary = Assert.IsAssignableFrom<IDictionary<int, ModelWithProperties>>(result.Model);
|
var resultDictionary = Assert.IsAssignableFrom<IDictionary<int, ModelWithProperties>>(result.Model);
|
||||||
Assert.Equal(dictionary, resultDictionary);
|
Assert.Equal(dictionary, resultDictionary);
|
||||||
|
|
||||||
|
// This requires a non-default IValidationStrategy
|
||||||
|
Assert.Contains(result.Model, context.ValidationState.Keys);
|
||||||
|
var entry = context.ValidationState[result.Model];
|
||||||
|
var strategy = Assert.IsType<ShortFormDictionaryValidationStrategy<int, ModelWithProperties>>(entry.Strategy);
|
||||||
|
Assert.Equal(
|
||||||
|
new KeyValuePair<string, int>[]
|
||||||
|
{
|
||||||
|
new KeyValuePair<string, int>("23", 23),
|
||||||
|
new KeyValuePair<string, int>("27", 27),
|
||||||
|
}.OrderBy(kvp => kvp.Key),
|
||||||
|
strategy.KeyMappings.OrderBy(kvp => kvp.Key));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -298,7 +313,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||||
Assert.True(result.IsModelSet);
|
Assert.True(result.IsModelSet);
|
||||||
Assert.Equal(modelName, result.Key);
|
Assert.Equal(modelName, result.Key);
|
||||||
Assert.NotNull(result.ValidationNode);
|
|
||||||
|
|
||||||
var resultDictionary = Assert.IsAssignableFrom<SortedDictionary<string, string>>(result.Model);
|
var resultDictionary = Assert.IsAssignableFrom<SortedDictionary<string, string>>(result.Model);
|
||||||
Assert.Equal(expectedDictionary, resultDictionary);
|
Assert.Equal(expectedDictionary, resultDictionary);
|
||||||
|
|
@ -330,10 +344,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
Assert.Empty(Assert.IsType<Dictionary<string, string>>(result.Model));
|
Assert.Empty(Assert.IsType<Dictionary<string, string>>(result.Model));
|
||||||
Assert.Equal("modelName", result.Key);
|
Assert.Equal("modelName", result.Key);
|
||||||
Assert.True(result.IsModelSet);
|
Assert.True(result.IsModelSet);
|
||||||
|
|
||||||
Assert.Same(result.ValidationNode.Model, result.Model);
|
|
||||||
Assert.Same(result.ValidationNode.Key, result.Key);
|
|
||||||
Assert.Same(result.ValidationNode.ModelMetadata, context.ModelMetadata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -403,7 +413,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
{
|
{
|
||||||
HttpContext = new DefaultHttpContext(),
|
HttpContext = new DefaultHttpContext(),
|
||||||
MetadataProvider = new TestModelMetadataProvider(),
|
MetadataProvider = new TestModelMetadataProvider(),
|
||||||
}
|
},
|
||||||
|
ValidationState = new ValidationStateDictionary(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return modelBindingContext;
|
return modelBindingContext;
|
||||||
|
|
@ -462,7 +473,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
KeyValuePair<int, string> value;
|
KeyValuePair<int, string> value;
|
||||||
if (values.TryGetValue(mbc.ModelName, out value))
|
if (values.TryGetValue(mbc.ModelName, out value))
|
||||||
{
|
{
|
||||||
return ModelBindingResult.SuccessAsync(mbc.ModelName, value, validationNode: null);
|
return ModelBindingResult.SuccessAsync(mbc.ModelName, value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -488,6 +499,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
ValueProvider = valueProvider,
|
ValueProvider = valueProvider,
|
||||||
},
|
},
|
||||||
ValueProvider = valueProvider,
|
ValueProvider = valueProvider,
|
||||||
|
ValidationState = new ValidationStateDictionary(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return bindingContext;
|
return bindingContext;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Http.Internal;
|
using Microsoft.AspNet.Http.Internal;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Framework.Primitives;
|
using Microsoft.Framework.Primitives;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -36,8 +37,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||||
Assert.True(result.IsModelSet);
|
Assert.True(result.IsModelSet);
|
||||||
Assert.NotNull(result.ValidationNode);
|
|
||||||
Assert.True(result.ValidationNode.SuppressValidation);
|
var entry = bindingContext.ValidationState[result.Model];
|
||||||
|
Assert.True(entry.SuppressValidation);
|
||||||
|
Assert.Null(entry.Key);
|
||||||
|
Assert.Null(entry.Metadata);
|
||||||
|
|
||||||
var form = Assert.IsAssignableFrom<IFormCollection>(result.Model);
|
var form = Assert.IsAssignableFrom<IFormCollection>(result.Model);
|
||||||
Assert.Equal(2, form.Count);
|
Assert.Equal(2, form.Count);
|
||||||
|
|
@ -124,7 +128,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
ModelBinder = new FormCollectionModelBinder(),
|
ModelBinder = new FormCollectionModelBinder(),
|
||||||
MetadataProvider = metadataProvider,
|
MetadataProvider = metadataProvider,
|
||||||
HttpContext = httpContext,
|
HttpContext = httpContext,
|
||||||
}
|
},
|
||||||
|
ValidationState = new ValidationStateDictionary(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return bindingContext;
|
return bindingContext;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Http.Internal;
|
using Microsoft.AspNet.Http.Internal;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -17,6 +18,29 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
public class FormFileModelBinderTest
|
public class FormFileModelBinderTest
|
||||||
{
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task FormFileModelBinder_SuppressesValidation()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var formFiles = new FormFileCollection();
|
||||||
|
formFiles.Add(GetMockFormFile("file", "file1.txt"));
|
||||||
|
var httpContext = GetMockHttpContext(GetMockFormCollection(formFiles));
|
||||||
|
var bindingContext = GetBindingContext(typeof(IEnumerable<IFormFile>), httpContext);
|
||||||
|
var binder = new FormFileModelBinder();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await binder.BindModelAsync(bindingContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||||
|
Assert.True(result.IsModelSet);
|
||||||
|
|
||||||
|
var entry = bindingContext.ValidationState[result.Model];
|
||||||
|
Assert.True(entry.SuppressValidation);
|
||||||
|
Assert.Null(entry.Key);
|
||||||
|
Assert.Null(entry.Metadata);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task FormFileModelBinder_ExpectMultipleFiles_BindSuccessful()
|
public async Task FormFileModelBinder_ExpectMultipleFiles_BindSuccessful()
|
||||||
{
|
{
|
||||||
|
|
@ -34,8 +58,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||||
Assert.True(result.IsModelSet);
|
Assert.True(result.IsModelSet);
|
||||||
Assert.NotNull(result.ValidationNode);
|
|
||||||
Assert.True(result.ValidationNode.SuppressValidation);
|
var entry = bindingContext.ValidationState[result.Model];
|
||||||
|
Assert.True(entry.SuppressValidation);
|
||||||
|
Assert.Null(entry.Key);
|
||||||
|
Assert.Null(entry.Metadata);
|
||||||
|
|
||||||
var files = Assert.IsAssignableFrom<IList<IFormFile>>(result.Model);
|
var files = Assert.IsAssignableFrom<IList<IFormFile>>(result.Model);
|
||||||
Assert.Equal(2, files.Count);
|
Assert.Equal(2, files.Count);
|
||||||
|
|
@ -197,7 +224,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
ModelBinder = new FormFileModelBinder(),
|
ModelBinder = new FormFileModelBinder(),
|
||||||
MetadataProvider = metadataProvider,
|
MetadataProvider = metadataProvider,
|
||||||
HttpContext = httpContext,
|
HttpContext = httpContext,
|
||||||
}
|
},
|
||||||
|
ValidationState = new ValidationStateDictionary(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return bindingContext;
|
return bindingContext;
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||||
Assert.Null(result.Model);
|
Assert.Null(result.Model);
|
||||||
Assert.False(bindingContext.ModelState.IsValid);
|
Assert.False(bindingContext.ModelState.IsValid);
|
||||||
Assert.Null(result.ValidationNode);
|
|
||||||
Assert.Equal("someName", bindingContext.ModelName);
|
Assert.Equal("someName", bindingContext.ModelName);
|
||||||
var error = Assert.Single(bindingContext.ModelState["someName.Key"].Errors);
|
var error = Assert.Single(bindingContext.ModelState["someName.Key"].Errors);
|
||||||
Assert.Equal("A value is required.", error.ErrorMessage);
|
Assert.Equal("A value is required.", error.ErrorMessage);
|
||||||
|
|
@ -54,7 +53,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Null(result.Model);
|
Assert.Null(result.Model);
|
||||||
Assert.False(bindingContext.ModelState.IsValid);
|
Assert.False(bindingContext.ModelState.IsValid);
|
||||||
Assert.Null(result.ValidationNode);
|
|
||||||
Assert.Equal("someName", bindingContext.ModelName);
|
Assert.Equal("someName", bindingContext.ModelName);
|
||||||
Assert.Equal(bindingContext.ModelState["someName.Value"].Errors.First().ErrorMessage, "A value is required.");
|
Assert.Equal(bindingContext.ModelState["someName.Value"].Errors.First().ErrorMessage, "A value is required.");
|
||||||
}
|
}
|
||||||
|
|
@ -99,19 +97,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
// Assert
|
// Assert
|
||||||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||||
Assert.Equal(new KeyValuePair<int, string>(42, "some-value"), result.Model);
|
Assert.Equal(new KeyValuePair<int, string>(42, "some-value"), result.Model);
|
||||||
Assert.NotNull(result.ValidationNode);
|
|
||||||
Assert.Equal(new KeyValuePair<int, string>(42, "some-value"), result.ValidationNode.Model);
|
|
||||||
Assert.Equal("someName", result.ValidationNode.Key);
|
|
||||||
|
|
||||||
var validationNode = result.ValidationNode.ChildNodes[0];
|
|
||||||
Assert.Equal("someName.Key", validationNode.Key);
|
|
||||||
Assert.Equal(42, validationNode.Model);
|
|
||||||
Assert.Empty(validationNode.ChildNodes);
|
|
||||||
|
|
||||||
validationNode = result.ValidationNode.ChildNodes[1];
|
|
||||||
Assert.Equal("someName.Value", validationNode.Key);
|
|
||||||
Assert.Equal("some-value", validationNode.Model);
|
|
||||||
Assert.Empty(validationNode.ChildNodes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -126,11 +111,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
ModelBindingResult innerResult;
|
ModelBindingResult innerResult;
|
||||||
if (isSuccess)
|
if (isSuccess)
|
||||||
{
|
{
|
||||||
innerResult = ModelBindingResult.Success(string.Empty, model, validationNode: null);
|
innerResult = ModelBindingResult.Success("somename.key", model);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
innerResult = ModelBindingResult.Failed(string.Empty);
|
innerResult = ModelBindingResult.Failed("somename.key");
|
||||||
}
|
}
|
||||||
|
|
||||||
var innerBinder = new Mock<IModelBinder>();
|
var innerBinder = new Mock<IModelBinder>();
|
||||||
|
|
@ -144,10 +129,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
var bindingContext = GetBindingContext(new SimpleValueProvider(), innerBinder.Object);
|
var bindingContext = GetBindingContext(new SimpleValueProvider(), innerBinder.Object);
|
||||||
|
|
||||||
var binder = new KeyValuePairModelBinder<int, string>();
|
var binder = new KeyValuePairModelBinder<int, string>();
|
||||||
var modelValidationNodeList = new List<ModelValidationNode>();
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await binder.TryBindStrongModel<int>(bindingContext, "key", modelValidationNodeList);
|
var result = await binder.TryBindStrongModel<int>(bindingContext, "key");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(innerResult, result);
|
Assert.Equal(innerResult, result);
|
||||||
|
|
@ -180,10 +164,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
Assert.Equal(default(KeyValuePair<string, string>), Assert.IsType<KeyValuePair<string, string>>(result.Model));
|
Assert.Equal(default(KeyValuePair<string, string>), Assert.IsType<KeyValuePair<string, string>>(result.Model));
|
||||||
Assert.Equal("modelName", result.Key);
|
Assert.Equal("modelName", result.Key);
|
||||||
Assert.True(result.IsModelSet);
|
Assert.True(result.IsModelSet);
|
||||||
|
|
||||||
Assert.Equal(result.ValidationNode.Model, result.Model);
|
|
||||||
Assert.Same(result.ValidationNode.Key, result.Key);
|
|
||||||
Assert.Same(result.ValidationNode.ModelMetadata, context.ModelMetadata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -260,8 +240,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
if (mbc.ModelType == typeof(int))
|
if (mbc.ModelType == typeof(int))
|
||||||
{
|
{
|
||||||
var model = 42;
|
var model = 42;
|
||||||
var validationNode = new ModelValidationNode(mbc.ModelName, mbc.ModelMetadata, model);
|
return ModelBindingResult.SuccessAsync(mbc.ModelName, model);
|
||||||
return ModelBindingResult.SuccessAsync(mbc.ModelName, model, validationNode);
|
|
||||||
}
|
}
|
||||||
return ModelBindingResult.NoResultAsync;
|
return ModelBindingResult.NoResultAsync;
|
||||||
});
|
});
|
||||||
|
|
@ -278,8 +257,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
if (mbc.ModelType == typeof(string))
|
if (mbc.ModelType == typeof(string))
|
||||||
{
|
{
|
||||||
var model = "some-value";
|
var model = "some-value";
|
||||||
var validationNode = new ModelValidationNode(mbc.ModelName, mbc.ModelMetadata, model);
|
return ModelBindingResult.SuccessAsync(mbc.ModelName, model);
|
||||||
return ModelBindingResult.SuccessAsync(mbc.ModelName, model, validationNode);
|
|
||||||
}
|
}
|
||||||
return ModelBindingResult.NoResultAsync;
|
return ModelBindingResult.NoResultAsync;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -15,16 +15,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
// Arrange
|
// Arrange
|
||||||
var key = "someName";
|
var key = "someName";
|
||||||
var model = "some model";
|
var model = "some model";
|
||||||
var validationNode = new ModelValidationNode(key, null, model);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = ModelBindingResult.Success(key, model, validationNode);
|
var result = ModelBindingResult.Success(key, model);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Same(key, result.Key);
|
Assert.Same(key, result.Key);
|
||||||
Assert.True(result.IsModelSet);
|
Assert.True(result.IsModelSet);
|
||||||
Assert.Same(model, result.Model);
|
Assert.Same(model, result.Model);
|
||||||
Assert.Same(validationNode, result.ValidationNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -33,16 +31,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
// Arrange
|
// Arrange
|
||||||
var key = "someName";
|
var key = "someName";
|
||||||
var model = "some model";
|
var model = "some model";
|
||||||
var validationNode = new ModelValidationNode(key, null, model);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await ModelBindingResult.SuccessAsync(key, model, validationNode);
|
var result = await ModelBindingResult.SuccessAsync(key, model);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Same(key, result.Key);
|
Assert.Same(key, result.Key);
|
||||||
Assert.True(result.IsModelSet);
|
Assert.True(result.IsModelSet);
|
||||||
Assert.Same(model, result.Model);
|
Assert.Same(model, result.Model);
|
||||||
Assert.Same(validationNode, result.ValidationNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -58,7 +54,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
Assert.Same(key, result.Key);
|
Assert.Same(key, result.Key);
|
||||||
Assert.False(result.IsModelSet);
|
Assert.False(result.IsModelSet);
|
||||||
Assert.Null(result.Model);
|
Assert.Null(result.Model);
|
||||||
Assert.Null(result.ValidationNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -74,7 +69,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
Assert.Same(key, result.Key);
|
Assert.Same(key, result.Key);
|
||||||
Assert.False(result.IsModelSet);
|
Assert.False(result.IsModelSet);
|
||||||
Assert.Null(result.Model);
|
Assert.Null(result.Model);
|
||||||
Assert.Null(result.ValidationNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -87,7 +81,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
Assert.Null(result.Key);
|
Assert.Null(result.Key);
|
||||||
Assert.False(result.IsModelSet);
|
Assert.False(result.IsModelSet);
|
||||||
Assert.Null(result.Model);
|
Assert.Null(result.Model);
|
||||||
Assert.Null(result.ValidationNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -100,7 +93,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
Assert.Null(result.Key);
|
Assert.Null(result.Key);
|
||||||
Assert.False(result.IsModelSet);
|
Assert.False(result.IsModelSet);
|
||||||
Assert.Null(result.Model);
|
Assert.Null(result.Model);
|
||||||
Assert.Null(result.ValidationNode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -786,13 +786,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
property => property,
|
property => property,
|
||||||
property => ModelBindingResult.Failed(property.PropertyName));
|
property => ModelBindingResult.Failed(property.PropertyName));
|
||||||
var nameProperty = containerMetadata.Properties[nameof(model.Name)];
|
var nameProperty = containerMetadata.Properties[nameof(model.Name)];
|
||||||
results[nameProperty] = ModelBindingResult.Success(string.Empty, "John Doe", validationNode: null);
|
results[nameProperty] = ModelBindingResult.Success(string.Empty, "John Doe");
|
||||||
|
|
||||||
var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
|
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
testableBinder.ProcessResults(bindingContext, results, modelValidationNode);
|
testableBinder.ProcessResults(bindingContext, results);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var modelStateDictionary = bindingContext.ModelState;
|
var modelStateDictionary = bindingContext.ModelState;
|
||||||
|
|
@ -837,13 +836,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
property => property,
|
property => property,
|
||||||
property => ModelBindingResult.Failed(property.PropertyName));
|
property => ModelBindingResult.Failed(property.PropertyName));
|
||||||
var nameProperty = containerMetadata.Properties[nameof(model.Name)];
|
var nameProperty = containerMetadata.Properties[nameof(model.Name)];
|
||||||
results[nameProperty] = ModelBindingResult.Success(string.Empty, "John Doe", validationNode: null);
|
results[nameProperty] = ModelBindingResult.Success(string.Empty, "John Doe");
|
||||||
|
|
||||||
var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
|
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
testableBinder.ProcessResults(bindingContext, results, modelValidationNode);
|
testableBinder.ProcessResults(bindingContext, results);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var modelStateDictionary = bindingContext.ModelState;
|
var modelStateDictionary = bindingContext.ModelState;
|
||||||
|
|
@ -888,18 +886,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
property => property,
|
property => property,
|
||||||
property => ModelBindingResult.Failed(property.PropertyName));
|
property => ModelBindingResult.Failed(property.PropertyName));
|
||||||
var propertyMetadata = containerMetadata.Properties[nameof(model.Name)];
|
var propertyMetadata = containerMetadata.Properties[nameof(model.Name)];
|
||||||
results[propertyMetadata] = ModelBindingResult.Success("theModel.Name", "John Doe", validationNode: null);
|
results[propertyMetadata] = ModelBindingResult.Success("theModel.Name", "John Doe");
|
||||||
|
|
||||||
// Attempt to set non-Nullable property to null. BindRequiredAttribute should not be relevant in this
|
// Attempt to set non-Nullable property to null. BindRequiredAttribute should not be relevant in this
|
||||||
// case because the binding exists.
|
// case because the binding exists.
|
||||||
propertyMetadata = containerMetadata.Properties[nameof(model.Age)];
|
propertyMetadata = containerMetadata.Properties[nameof(model.Age)];
|
||||||
results[propertyMetadata] = ModelBindingResult.Success("theModel.Age", model: null, validationNode: null);
|
results[propertyMetadata] = ModelBindingResult.Success("theModel.Age", model: null);
|
||||||
|
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
testableBinder.ProcessResults(bindingContext, results, modelValidationNode);
|
testableBinder.ProcessResults(bindingContext, results);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var modelStateDictionary = bindingContext.ModelState;
|
var modelStateDictionary = bindingContext.ModelState;
|
||||||
|
|
@ -929,10 +926,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
property => ModelBindingResult.Failed(property.PropertyName));
|
property => ModelBindingResult.Failed(property.PropertyName));
|
||||||
|
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
testableBinder.ProcessResults(bindingContext, results, modelValidationNode);
|
testableBinder.ProcessResults(bindingContext, results);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var modelStateDictionary = bindingContext.ModelState;
|
var modelStateDictionary = bindingContext.ModelState;
|
||||||
|
|
@ -952,10 +948,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
property => ModelBindingResult.Failed(property.PropertyName));
|
property => ModelBindingResult.Failed(property.PropertyName));
|
||||||
|
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
testableBinder.ProcessResults(bindingContext, results, modelValidationNode);
|
testableBinder.ProcessResults(bindingContext, results);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var modelStateDictionary = bindingContext.ModelState;
|
var modelStateDictionary = bindingContext.ModelState;
|
||||||
|
|
@ -984,20 +979,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequired)];
|
var propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequired)];
|
||||||
results[propertyMetadata] = ModelBindingResult.Success(
|
results[propertyMetadata] = ModelBindingResult.Success(
|
||||||
key: "theModel." + nameof(Person.ValueTypeRequired),
|
key: "theModel." + nameof(Person.ValueTypeRequired),
|
||||||
model: null,
|
model: null);
|
||||||
validationNode: null);
|
|
||||||
|
|
||||||
// Make ValueTypeRequiredWithDefaultValue invalid
|
// Make ValueTypeRequiredWithDefaultValue invalid
|
||||||
propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequiredWithDefaultValue)];
|
propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequiredWithDefaultValue)];
|
||||||
results[propertyMetadata] = ModelBindingResult.Success(
|
results[propertyMetadata] = ModelBindingResult.Success(
|
||||||
key: "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue),
|
key: "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue),
|
||||||
model: null,
|
model: null);
|
||||||
validationNode: null);
|
|
||||||
|
|
||||||
var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
testableBinder.ProcessResults(bindingContext, results, modelValidationNode);
|
testableBinder.ProcessResults(bindingContext, results);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(modelStateDictionary.IsValid);
|
Assert.False(modelStateDictionary.IsValid);
|
||||||
|
|
@ -1056,10 +1047,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
results[propertyMetadata] = ModelBindingResult.Failed(
|
results[propertyMetadata] = ModelBindingResult.Failed(
|
||||||
key: "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue));
|
key: "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue));
|
||||||
|
|
||||||
var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
testableBinder.ProcessResults(bindingContext, results, modelValidationNode);
|
testableBinder.ProcessResults(bindingContext, results);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(modelState.IsValid);
|
Assert.True(modelState.IsValid);
|
||||||
|
|
@ -1084,24 +1073,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequired)];
|
var propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequired)];
|
||||||
results[propertyMetadata] = ModelBindingResult.Success(
|
results[propertyMetadata] = ModelBindingResult.Success(
|
||||||
key: "theModel." + nameof(Person.ValueTypeRequired),
|
key: "theModel." + nameof(Person.ValueTypeRequired),
|
||||||
model: 41,
|
model: 41);
|
||||||
validationNode: null);
|
|
||||||
|
|
||||||
// Make ValueTypeRequiredWithDefaultValue valid.
|
// Make ValueTypeRequiredWithDefaultValue valid.
|
||||||
propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequiredWithDefaultValue)];
|
propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequiredWithDefaultValue)];
|
||||||
results[propertyMetadata] = ModelBindingResult.Success(
|
results[propertyMetadata] = ModelBindingResult.Success(
|
||||||
key: "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue),
|
key: "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue),
|
||||||
model: 57,
|
model: 57);
|
||||||
validationNode: null);
|
|
||||||
|
|
||||||
// Also remind ProcessResults about PropertyWithDefaultValue -- as BindPropertiesAsync() would.
|
// Also remind ProcessResults about PropertyWithDefaultValue -- as BindPropertiesAsync() would.
|
||||||
propertyMetadata = containerMetadata.Properties[nameof(Person.PropertyWithDefaultValue)];
|
propertyMetadata = containerMetadata.Properties[nameof(Person.PropertyWithDefaultValue)];
|
||||||
results[propertyMetadata] = ModelBindingResult.Failed(
|
results[propertyMetadata] = ModelBindingResult.Failed(
|
||||||
key: "theModel." + nameof(Person.PropertyWithDefaultValue));
|
key: "theModel." + nameof(Person.PropertyWithDefaultValue));
|
||||||
var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
testableBinder.ProcessResults(bindingContext, results, modelValidationNode);
|
testableBinder.ProcessResults(bindingContext, results);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(modelStateDictionary.IsValid);
|
Assert.True(modelStateDictionary.IsValid);
|
||||||
|
|
@ -1140,13 +1126,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
.Properties[nameof(ModelWithBindRequiredAndRequiredAttribute.ReferenceTypeProperty)];
|
.Properties[nameof(ModelWithBindRequiredAndRequiredAttribute.ReferenceTypeProperty)];
|
||||||
results[propertyMetadata] = ModelBindingResult.Success(
|
results[propertyMetadata] = ModelBindingResult.Success(
|
||||||
key: "theModel." + nameof(ModelWithBindRequiredAndRequiredAttribute.ReferenceTypeProperty),
|
key: "theModel." + nameof(ModelWithBindRequiredAndRequiredAttribute.ReferenceTypeProperty),
|
||||||
model: "value",
|
model: "value");
|
||||||
validationNode: null);
|
|
||||||
|
|
||||||
var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
testableBinder.ProcessResults(bindingContext, results, modelValidationNode);
|
testableBinder.ProcessResults(bindingContext, results);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(modelStateDictionary.IsValid);
|
Assert.False(modelStateDictionary.IsValid);
|
||||||
|
|
@ -1185,19 +1167,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
.Properties[nameof(ModelWithBindRequiredAndRequiredAttribute.ValueTypeProperty)];
|
.Properties[nameof(ModelWithBindRequiredAndRequiredAttribute.ValueTypeProperty)];
|
||||||
results[propertyMetadata] = ModelBindingResult.Success(
|
results[propertyMetadata] = ModelBindingResult.Success(
|
||||||
key: "theModel." + nameof(ModelWithBindRequiredAndRequiredAttribute.ValueTypeProperty),
|
key: "theModel." + nameof(ModelWithBindRequiredAndRequiredAttribute.ValueTypeProperty),
|
||||||
model: 17,
|
model: 17);
|
||||||
validationNode: null);
|
|
||||||
|
|
||||||
// Make ReferenceTypeProperty not have a value.
|
// Make ReferenceTypeProperty not have a value.
|
||||||
propertyMetadata = containerMetadata
|
propertyMetadata = containerMetadata
|
||||||
.Properties[nameof(ModelWithBindRequiredAndRequiredAttribute.ReferenceTypeProperty)];
|
.Properties[nameof(ModelWithBindRequiredAndRequiredAttribute.ReferenceTypeProperty)];
|
||||||
results[propertyMetadata] = ModelBindingResult.Failed(
|
results[propertyMetadata] = ModelBindingResult.Failed(
|
||||||
key: "theModel." + nameof(ModelWithBindRequiredAndRequiredAttribute.ReferenceTypeProperty));
|
key: "theModel." + nameof(ModelWithBindRequiredAndRequiredAttribute.ReferenceTypeProperty));
|
||||||
|
|
||||||
var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
testableBinder.ProcessResults(bindingContext, results, modelValidationNode);
|
testableBinder.ProcessResults(bindingContext, results);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(modelStateDictionary.IsValid);
|
Assert.False(modelStateDictionary.IsValid);
|
||||||
|
|
@ -1235,57 +1213,23 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var firstNameProperty = containerMetadata.Properties[nameof(model.FirstName)];
|
var firstNameProperty = containerMetadata.Properties[nameof(model.FirstName)];
|
||||||
results[firstNameProperty] = ModelBindingResult.Success(
|
results[firstNameProperty] = ModelBindingResult.Success(
|
||||||
nameof(model.FirstName),
|
nameof(model.FirstName),
|
||||||
"John",
|
"John");
|
||||||
validationNode: null);
|
|
||||||
|
|
||||||
var lastNameProperty = containerMetadata.Properties[nameof(model.LastName)];
|
var lastNameProperty = containerMetadata.Properties[nameof(model.LastName)];
|
||||||
results[lastNameProperty] = ModelBindingResult.Success(
|
results[lastNameProperty] = ModelBindingResult.Success(
|
||||||
nameof(model.LastName),
|
nameof(model.LastName),
|
||||||
"Doe",
|
"Doe");
|
||||||
validationNode: null);
|
|
||||||
|
|
||||||
var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
|
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
testableBinder.ProcessResults(bindingContext, results, modelValidationNode);
|
testableBinder.ProcessResults(bindingContext, results);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal("John", model.FirstName);
|
Assert.Equal("John", model.FirstName);
|
||||||
Assert.Equal("Doe", model.LastName);
|
Assert.Equal("Doe", model.LastName);
|
||||||
Assert.Equal(dob, model.DateOfBirth);
|
Assert.Equal(dob, model.DateOfBirth);
|
||||||
Assert.True(bindingContext.ModelState.IsValid);
|
Assert.True(bindingContext.ModelState.IsValid);
|
||||||
|
|
||||||
// Ensure that we add child nodes for all the nodes which have a result (irrespective of if they
|
|
||||||
// are bound or not).
|
|
||||||
Assert.Equal(5, modelValidationNode.ChildNodes.Count);
|
|
||||||
|
|
||||||
Assert.Collection(modelValidationNode.ChildNodes,
|
|
||||||
child =>
|
|
||||||
{
|
|
||||||
Assert.Equal(nameof(model.DateOfBirth), child.Key);
|
|
||||||
Assert.Equal(null, child.Model);
|
|
||||||
},
|
|
||||||
child =>
|
|
||||||
{
|
|
||||||
Assert.Equal(nameof(model.DateOfDeath), child.Key);
|
|
||||||
Assert.Equal(null, child.Model);
|
|
||||||
},
|
|
||||||
child =>
|
|
||||||
{
|
|
||||||
Assert.Equal(nameof(model.FirstName), child.Key);
|
|
||||||
Assert.Equal("John", child.Model);
|
|
||||||
},
|
|
||||||
child =>
|
|
||||||
{
|
|
||||||
Assert.Equal(nameof(model.LastName), child.Key);
|
|
||||||
Assert.Equal("Doe", child.Model);
|
|
||||||
},
|
|
||||||
child =>
|
|
||||||
{
|
|
||||||
Assert.Equal(nameof(model.NonUpdateableProperty), child.Key);
|
|
||||||
Assert.Equal(null, child.Model);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -1420,8 +1364,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var propertyMetadata = bindingContext.ModelMetadata.Properties[propertyName];
|
var propertyMetadata = bindingContext.ModelMetadata.Properties[propertyName];
|
||||||
var result = ModelBindingResult.Success(
|
var result = ModelBindingResult.Success(
|
||||||
propertyName,
|
propertyName,
|
||||||
new Simple { Name = "Hanna" },
|
new Simple { Name = "Hanna" });
|
||||||
validationNode: null);
|
|
||||||
|
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
|
|
||||||
|
|
@ -1496,7 +1439,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var modelExplorer = metadataProvider.GetModelExplorerForType(type, model);
|
var modelExplorer = metadataProvider.GetModelExplorerForType(type, model);
|
||||||
|
|
||||||
var propertyMetadata = bindingContext.ModelMetadata.Properties[propertyName];
|
var propertyMetadata = bindingContext.ModelMetadata.Properties[propertyName];
|
||||||
var result = ModelBindingResult.Success(propertyName, collection, validationNode: null);
|
var result = ModelBindingResult.Success(propertyName, collection);
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -1519,7 +1462,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
|
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
|
||||||
var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.DateOfBirth)];
|
var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.DateOfBirth)];
|
||||||
|
|
||||||
var result = ModelBindingResult.Success("foo", new DateTime(2001, 1, 1), validationNode: null);
|
var result = ModelBindingResult.Success("foo", new DateTime(2001, 1, 1));
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -1546,7 +1489,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
|
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
|
||||||
var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.DateOfDeath)];
|
var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.DateOfDeath)];
|
||||||
|
|
||||||
var result = ModelBindingResult.Success("foo", new DateTime(1800, 1, 1), validationNode: null);
|
var result = ModelBindingResult.Success("foo", new DateTime(1800, 1, 1));
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -1571,7 +1514,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
|
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
|
||||||
var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.DateOfBirth)];
|
var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.DateOfBirth)];
|
||||||
|
|
||||||
var result = ModelBindingResult.Success("foo.DateOfBirth", model: null, validationNode: null);
|
var result = ModelBindingResult.Success("foo.DateOfBirth", model: null);
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -1599,7 +1542,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(ModelWhosePropertySetterThrows), model);
|
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(ModelWhosePropertySetterThrows), model);
|
||||||
var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.NameNoAttribute)];
|
var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.NameNoAttribute)];
|
||||||
|
|
||||||
var result = ModelBindingResult.Success("foo.NameNoAttribute", model: null, validationNode: null);
|
var result = ModelBindingResult.Success("foo.NameNoAttribute", model: null);
|
||||||
var testableBinder = new TestableMutableObjectModelBinder();
|
var testableBinder = new TestableMutableObjectModelBinder();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http.Internal;
|
using Microsoft.AspNet.Http.Internal;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -29,9 +30,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
Assert.NotNull(result.Model);
|
Assert.NotNull(result.Model);
|
||||||
Assert.Equal("modelName", result.Key);
|
Assert.Equal("modelName", result.Key);
|
||||||
|
|
||||||
Assert.NotNull(result.ValidationNode);
|
var entry = modelBindingContext.ValidationState[result.Model];
|
||||||
Assert.Equal("modelName", result.ValidationNode.Key);
|
Assert.True(entry.SuppressValidation);
|
||||||
Assert.True(result.ValidationNode.SuppressValidation);
|
Assert.Null(entry.Key);
|
||||||
|
Assert.Null(entry.Metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -95,6 +97,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
},
|
},
|
||||||
BinderModelName = modelMetadata.BinderModelName,
|
BinderModelName = modelMetadata.BinderModelName,
|
||||||
BindingSource = modelMetadata.BindingSource,
|
BindingSource = modelMetadata.BindingSource,
|
||||||
|
ValidationState = new ValidationStateDictionary(),
|
||||||
};
|
};
|
||||||
|
|
||||||
return bindingContext;
|
return bindingContext;
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.IsModelSet);
|
Assert.False(result.IsModelSet);
|
||||||
Assert.Null(result.Model);
|
Assert.Null(result.Model);
|
||||||
Assert.Null(result.ValidationNode);
|
|
||||||
|
|
||||||
var error = Assert.Single(bindingContext.ModelState["theModelName"].Errors);
|
var error = Assert.Single(bindingContext.ModelState["theModelName"].Errors);
|
||||||
Assert.Equal(error.ErrorMessage, "The value '' is invalid.", StringComparer.Ordinal);
|
Assert.Equal(error.ErrorMessage, "The value '' is invalid.", StringComparer.Ordinal);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
{
|
||||||
|
public class DefaultCollectionValidationStrategyTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void EnumerateElements()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var model = new List<int>() { 2, 3, 5 };
|
||||||
|
|
||||||
|
var metadata = TestModelMetadataProvider.CreateDefaultProvider().GetMetadataForType(typeof(List<int>));
|
||||||
|
var strategy = DefaultCollectionValidationStrategy.Instance;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var enumerator = strategy.GetChildren(metadata, "prefix", model);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
BufferEntries(enumerator).OrderBy(e => e.Key),
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[0]", e.Key);
|
||||||
|
Assert.Equal(2, e.Model);
|
||||||
|
Assert.Same(metadata.ElementMetadata, e.Metadata);
|
||||||
|
},
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[1]", e.Key);
|
||||||
|
Assert.Equal(3, e.Model);
|
||||||
|
Assert.Same(metadata.ElementMetadata, e.Metadata);
|
||||||
|
},
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[2]", e.Key);
|
||||||
|
Assert.Equal(5, e.Model);
|
||||||
|
Assert.Same(metadata.ElementMetadata, e.Metadata);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EnumerateElements_Dictionary()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var model = new Dictionary<int, string>()
|
||||||
|
{
|
||||||
|
{ 2, "two" },
|
||||||
|
{ 3, "three" },
|
||||||
|
{ 5, "five" },
|
||||||
|
};
|
||||||
|
|
||||||
|
var metadata = TestModelMetadataProvider.CreateDefaultProvider().GetMetadataForType(typeof(List<int>));
|
||||||
|
var strategy = DefaultCollectionValidationStrategy.Instance;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var enumerator = strategy.GetChildren(metadata, "prefix", model);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
BufferEntries(enumerator).OrderBy(e => e.Key),
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[0]", e.Key);
|
||||||
|
Assert.Equal(new KeyValuePair<int, string>(2, "two"), e.Model);
|
||||||
|
Assert.Same(metadata.ElementMetadata, e.Metadata);
|
||||||
|
},
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[1]", e.Key);
|
||||||
|
Assert.Equal(new KeyValuePair<int, string>(3, "three"), e.Model);
|
||||||
|
Assert.Same(metadata.ElementMetadata, e.Metadata);
|
||||||
|
},
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[2]", e.Key);
|
||||||
|
Assert.Equal(new KeyValuePair<int, string>(5, "five"), e.Model);
|
||||||
|
Assert.Same(metadata.ElementMetadata, e.Metadata);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ValidationEntry> BufferEntries(IEnumerator<ValidationEntry> enumerator)
|
||||||
|
{
|
||||||
|
var entries = new List<ValidationEntry>();
|
||||||
|
while (enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
entries.Add(enumerator.Current);
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
{
|
||||||
|
public class DefaultComplexObjectValidationStrategyTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void EnumerateElements()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var model = new Person()
|
||||||
|
{
|
||||||
|
Age = 23,
|
||||||
|
Id = 1,
|
||||||
|
Name = "Joey",
|
||||||
|
};
|
||||||
|
|
||||||
|
var metadata = TestModelMetadataProvider.CreateDefaultProvider().GetMetadataForType(typeof(Person));
|
||||||
|
var strategy = DefaultComplexObjectValidationStrategy.Instance;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var enumerator = strategy.GetChildren(metadata, "prefix", model);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
BufferEntries(enumerator).OrderBy(e => e.Key),
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix.Age", e.Key);
|
||||||
|
Assert.Equal(23, e.Model);
|
||||||
|
Assert.Same(metadata.Properties["Age"], e.Metadata);
|
||||||
|
},
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix.Id", e.Key);
|
||||||
|
Assert.Equal(1, e.Model);
|
||||||
|
Assert.Same(metadata.Properties["Id"], e.Metadata);
|
||||||
|
},
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix.Name", e.Key);
|
||||||
|
Assert.Equal("Joey", e.Model);
|
||||||
|
Assert.Same(metadata.Properties["Name"], e.Metadata);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ValidationEntry> BufferEntries(IEnumerator<ValidationEntry> enumerator)
|
||||||
|
{
|
||||||
|
var entries = new List<ValidationEntry>();
|
||||||
|
while (enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
entries.Add(enumerator.Current);
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Person
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public int Age { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,159 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
{
|
||||||
|
public class ExplicitIndexCollectionValidationStrategyTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void EnumerateElements_List()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var model = new List<int>() { 2, 3, 5 };
|
||||||
|
|
||||||
|
var metadata = TestModelMetadataProvider.CreateDefaultProvider().GetMetadataForType(typeof(List<int>));
|
||||||
|
var strategy = new ExplicitIndexCollectionValidationStrategy(new string[] { "zero", "one", "two" });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var enumerator = strategy.GetChildren(metadata, "prefix", model);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
BufferEntries(enumerator).OrderBy(e => e.Key),
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[one]", e.Key);
|
||||||
|
Assert.Equal(3, e.Model);
|
||||||
|
Assert.Same(metadata.ElementMetadata, e.Metadata);
|
||||||
|
},
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[two]", e.Key);
|
||||||
|
Assert.Equal(5, e.Model);
|
||||||
|
Assert.Same(metadata.ElementMetadata, e.Metadata);
|
||||||
|
},
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[zero]", e.Key);
|
||||||
|
Assert.Equal(2, e.Model);
|
||||||
|
Assert.Same(metadata.ElementMetadata, e.Metadata);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EnumerateElements_Dictionary()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var model = new Dictionary<int, string>()
|
||||||
|
{
|
||||||
|
{ 2, "two" },
|
||||||
|
{ 3, "three" },
|
||||||
|
{ 5, "five" },
|
||||||
|
};
|
||||||
|
|
||||||
|
var metadata = TestModelMetadataProvider.CreateDefaultProvider().GetMetadataForType(typeof(List<int>));
|
||||||
|
var strategy = new ExplicitIndexCollectionValidationStrategy(new string[] { "zero", "one", "two" });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var enumerator = strategy.GetChildren(metadata, "prefix", model);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
BufferEntries(enumerator).OrderBy(e => e.Key),
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[one]", e.Key);
|
||||||
|
Assert.Equal(new KeyValuePair<int, string>(3, "three"), e.Model);
|
||||||
|
Assert.Same(metadata.ElementMetadata, e.Metadata);
|
||||||
|
},
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[two]", e.Key);
|
||||||
|
Assert.Equal(new KeyValuePair<int, string>(5, "five"), e.Model);
|
||||||
|
Assert.Same(metadata.ElementMetadata, e.Metadata);
|
||||||
|
},
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[zero]", e.Key);
|
||||||
|
Assert.Equal(new KeyValuePair<int, string>(2, "two"), e.Model);
|
||||||
|
Assert.Same(metadata.ElementMetadata, e.Metadata);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EnumerateElements_RunOutOfIndices()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var model = new List<int>() { 2, 3, 5 };
|
||||||
|
|
||||||
|
var metadata = TestModelMetadataProvider.CreateDefaultProvider().GetMetadataForType(typeof(List<int>));
|
||||||
|
|
||||||
|
var strategy = new ExplicitIndexCollectionValidationStrategy(new string[] { "zero", "one", });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var enumerator = strategy.GetChildren(metadata, "prefix", model);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
BufferEntries(enumerator).OrderBy(e => e.Key),
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[one]", e.Key);
|
||||||
|
Assert.Equal(3, e.Model);
|
||||||
|
Assert.Same(metadata.ElementMetadata, e.Metadata);
|
||||||
|
},
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[zero]", e.Key);
|
||||||
|
Assert.Equal(2, e.Model);
|
||||||
|
Assert.Same(metadata.ElementMetadata, e.Metadata);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EnumerateElements_RunOutOfElements()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var model = new List<int>() { 2, 3, };
|
||||||
|
|
||||||
|
var metadata = TestModelMetadataProvider.CreateDefaultProvider().GetMetadataForType(typeof(List<int>));
|
||||||
|
|
||||||
|
var strategy = new ExplicitIndexCollectionValidationStrategy(new string[] { "zero", "one", "two" });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var enumerator = strategy.GetChildren(metadata, "prefix", model);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
BufferEntries(enumerator).OrderBy(e => e.Key),
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[one]", e.Key);
|
||||||
|
Assert.Equal(3, e.Model);
|
||||||
|
Assert.Same(metadata.ElementMetadata, e.Metadata);
|
||||||
|
},
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[zero]", e.Key);
|
||||||
|
Assert.Equal(2, e.Model);
|
||||||
|
Assert.Same(metadata.ElementMetadata, e.Metadata);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ValidationEntry> BufferEntries(IEnumerator<ValidationEntry> enumerator)
|
||||||
|
{
|
||||||
|
var entries = new List<ValidationEntry>();
|
||||||
|
while (enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
entries.Add(enumerator.Current);
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
{
|
||||||
|
public class ShortFormDictionaryValidationStrategyTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void EnumerateElements()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var model = new Dictionary<int, string>()
|
||||||
|
{
|
||||||
|
{ 2, "two" },
|
||||||
|
{ 3, "three" },
|
||||||
|
{ 5, "five" },
|
||||||
|
};
|
||||||
|
|
||||||
|
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
|
var metadata = metadataProvider.GetMetadataForType(typeof(List<int>));
|
||||||
|
var valueMetadata = metadataProvider.GetMetadataForType(typeof(string));
|
||||||
|
var strategy = new ShortFormDictionaryValidationStrategy<int, string>(new Dictionary<string, int>()
|
||||||
|
{
|
||||||
|
{ "2", 2 },
|
||||||
|
{ "3", 3 },
|
||||||
|
{ "5", 5 },
|
||||||
|
},
|
||||||
|
valueMetadata);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var enumerator = strategy.GetChildren(metadata, "prefix", model);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
BufferEntries(enumerator).OrderBy(e => e.Key),
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[2]", e.Key);
|
||||||
|
Assert.Equal("two", e.Model);
|
||||||
|
Assert.Same(valueMetadata, e.Metadata);
|
||||||
|
},
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[3]", e.Key);
|
||||||
|
Assert.Equal("three", e.Model);
|
||||||
|
Assert.Same(valueMetadata, e.Metadata);
|
||||||
|
},
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[5]", e.Key);
|
||||||
|
Assert.Equal("five", e.Model);
|
||||||
|
Assert.Same(valueMetadata, e.Metadata);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EnumerateElements_RunOutOfIndices()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var model = new Dictionary<int, string>()
|
||||||
|
{
|
||||||
|
{ 2, "two" },
|
||||||
|
{ 3, "three" },
|
||||||
|
{ 5, "five" },
|
||||||
|
};
|
||||||
|
|
||||||
|
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
|
var metadata = metadataProvider.GetMetadataForType(typeof(List<int>));
|
||||||
|
var valueMetadata = metadataProvider.GetMetadataForType(typeof(string));
|
||||||
|
var strategy = new ShortFormDictionaryValidationStrategy<int, string>(new Dictionary<string, int>()
|
||||||
|
{
|
||||||
|
{ "2", 2 },
|
||||||
|
{ "3", 3 },
|
||||||
|
},
|
||||||
|
valueMetadata);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var enumerator = strategy.GetChildren(metadata, "prefix", model);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
BufferEntries(enumerator).OrderBy(e => e.Key),
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[2]", e.Key);
|
||||||
|
Assert.Equal("two", e.Model);
|
||||||
|
Assert.Same(valueMetadata, e.Metadata);
|
||||||
|
},
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[3]", e.Key);
|
||||||
|
Assert.Equal("three", e.Model);
|
||||||
|
Assert.Same(valueMetadata, e.Metadata);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void EnumerateElements_RunOutOfElements()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var model = new Dictionary<int, string>()
|
||||||
|
{
|
||||||
|
{ 2, "two" },
|
||||||
|
{ 3, "three" },
|
||||||
|
};
|
||||||
|
|
||||||
|
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
|
var metadata = metadataProvider.GetMetadataForType(typeof(List<int>));
|
||||||
|
var valueMetadata = metadataProvider.GetMetadataForType(typeof(string));
|
||||||
|
var strategy = new ShortFormDictionaryValidationStrategy<int, string>(new Dictionary<string, int>()
|
||||||
|
{
|
||||||
|
{ "2", 2 },
|
||||||
|
{ "3", 3 },
|
||||||
|
{ "5", 5 },
|
||||||
|
},
|
||||||
|
valueMetadata);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var enumerator = strategy.GetChildren(metadata, "prefix", model);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Collection(
|
||||||
|
BufferEntries(enumerator).OrderBy(e => e.Key),
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[2]", e.Key);
|
||||||
|
Assert.Equal("two", e.Model);
|
||||||
|
Assert.Same(valueMetadata, e.Metadata);
|
||||||
|
},
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
Assert.Equal("prefix[3]", e.Key);
|
||||||
|
Assert.Equal("three", e.Model);
|
||||||
|
Assert.Same(valueMetadata, e.Metadata);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ValidationEntry> BufferEntries(IEnumerator<ValidationEntry> enumerator)
|
||||||
|
{
|
||||||
|
var entries = new List<ValidationEntry>();
|
||||||
|
while (enumerator.MoveNext())
|
||||||
|
{
|
||||||
|
entries.Add(enumerator.Current);
|
||||||
|
}
|
||||||
|
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -262,11 +262,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
||||||
|
|
||||||
private static ModelValidationContext CreateValidationContext(ModelExplorer modelExplorer)
|
private static ModelValidationContext CreateValidationContext(ModelExplorer modelExplorer)
|
||||||
{
|
{
|
||||||
return new ModelValidationContext(
|
return new ModelValidationContext()
|
||||||
bindingSource: null,
|
{
|
||||||
modelState: null,
|
Container = modelExplorer.Container,
|
||||||
validatorProvider: null,
|
Metadata = modelExplorer.Metadata,
|
||||||
modelExplorer: modelExplorer);
|
Model = modelExplorer.Model,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DerivedRequiredAttribute : RequiredAttribute
|
private class DerivedRequiredAttribute : RequiredAttribute
|
||||||
|
|
|
||||||
|
|
@ -107,13 +107,13 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
// Read-only collection should not be updated.
|
// Read-only collection should not be updated.
|
||||||
Assert.Empty(boundModel.Address);
|
Assert.Empty(boundModel.Address);
|
||||||
|
|
||||||
// ModelState (data is valid but is not copied into Address).
|
// ModelState (data is can't be validated).
|
||||||
Assert.True(modelState.IsValid);
|
Assert.False(modelState.IsValid);
|
||||||
var entry = Assert.Single(modelState);
|
var entry = Assert.Single(modelState);
|
||||||
Assert.Equal("Address[0].Street", entry.Key);
|
Assert.Equal("Address[0].Street", entry.Key);
|
||||||
var state = entry.Value;
|
var state = entry.Value;
|
||||||
Assert.NotNull(state);
|
Assert.NotNull(state);
|
||||||
Assert.Equal(ModelValidationState.Valid, state.ValidationState);
|
Assert.Equal(ModelValidationState.Unvalidated, state.ValidationState);
|
||||||
Assert.Equal("SomeStreet", state.RawValue);
|
Assert.Equal("SomeStreet", state.RawValue);
|
||||||
Assert.Equal("SomeStreet", state.AttemptedValue);
|
Assert.Equal("SomeStreet", state.AttemptedValue);
|
||||||
}
|
}
|
||||||
|
|
@ -286,13 +286,13 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
// Read-only collection should not be updated.
|
// Read-only collection should not be updated.
|
||||||
Assert.Empty(boundModel.Address);
|
Assert.Empty(boundModel.Address);
|
||||||
|
|
||||||
// ModelState (data is valid but is not copied into Address).
|
// ModelState (data cannot be validated).
|
||||||
Assert.True(modelState.IsValid);
|
Assert.False(modelState.IsValid);
|
||||||
var entry = Assert.Single(modelState);
|
var entry = Assert.Single(modelState);
|
||||||
Assert.Equal("prefix.Address[0].Street", entry.Key);
|
Assert.Equal("prefix.Address[0].Street", entry.Key);
|
||||||
var state = entry.Value;
|
var state = entry.Value;
|
||||||
Assert.NotNull(state);
|
Assert.NotNull(state);
|
||||||
Assert.Equal(ModelValidationState.Valid, state.ValidationState);
|
Assert.Equal(ModelValidationState.Unvalidated, state.ValidationState);
|
||||||
Assert.Equal("SomeStreet", state.AttemptedValue);
|
Assert.Equal("SomeStreet", state.AttemptedValue);
|
||||||
Assert.Equal("SomeStreet", state.RawValue);
|
Assert.Equal("SomeStreet", state.RawValue);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,11 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
|
|
||||||
// ModelState (not set unless inner binder sets it)
|
// ModelState (not set unless inner binder sets it)
|
||||||
Assert.True(modelState.IsValid);
|
Assert.True(modelState.IsValid);
|
||||||
Assert.Empty(modelState);
|
var entry = modelState[string.Empty];
|
||||||
|
Assert.Null(entry.AttemptedValue);
|
||||||
|
Assert.Null(entry.RawValue);
|
||||||
|
Assert.Empty(entry.Errors);
|
||||||
|
Assert.Equal(ModelValidationState.Valid, entry.ValidationState);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -294,15 +298,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
new string[] { address.Street },
|
new string[] { address.Street },
|
||||||
address.Street);
|
address.Street);
|
||||||
|
|
||||||
var validationNode = new ModelValidationNode(
|
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, address);
|
||||||
bindingContext.ModelName,
|
|
||||||
bindingContext.ModelMetadata,
|
|
||||||
address)
|
|
||||||
{
|
|
||||||
ValidateAllProperties = true
|
|
||||||
};
|
|
||||||
|
|
||||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, address, validationNode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -316,11 +312,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
new string[] { model },
|
new string[] { model },
|
||||||
model);
|
model);
|
||||||
|
|
||||||
var modelValidationNode = new ModelValidationNode(
|
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model);
|
||||||
bindingContext.ModelName,
|
|
||||||
bindingContext.ModelMetadata,
|
|
||||||
model);
|
|
||||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model, modelValidationNode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -328,7 +320,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
{
|
{
|
||||||
public Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext)
|
public Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext)
|
||||||
{
|
{
|
||||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model: null, validationNode: null);
|
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model: null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -630,12 +630,12 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return _data.GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerator<KeyValuePair<string, string>> IEnumerable<KeyValuePair<string, string>>.GetEnumerator()
|
IEnumerator<KeyValuePair<string, string>> IEnumerable<KeyValuePair<string, string>>.GetEnumerator()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return _data.GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ICollection<KeyValuePair<string, string>>.Remove(KeyValuePair<string, string> item)
|
bool ICollection<KeyValuePair<string, string>>.Remove(KeyValuePair<string, string> item)
|
||||||
|
|
@ -650,7 +650,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
|
|
||||||
bool IDictionary<string, string>.TryGetValue(string key, out string value)
|
bool IDictionary<string, string>.TryGetValue(string key, out string value)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return _data.TryGetValue(key, out value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -735,12 +735,12 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator()
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return _data.GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
|
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return _data.GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
|
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
|
||||||
|
|
@ -755,7 +755,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
|
|
||||||
bool IDictionary<TKey, TValue>.TryGetValue(TKey key, out TValue value)
|
bool IDictionary<TKey, TValue>.TryGetValue(TKey key, out TValue value)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
return _data.TryGetValue(key, out value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
||||||
return ModelBindingResult.NoResultAsync;
|
return ModelBindingResult.NoResultAsync;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, new Address(), validationNode: null);
|
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, new Address());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http.Internal;
|
using Microsoft.AspNet.Http.Internal;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
|
|
@ -27,8 +28,11 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||||
Assert.True(result.IsModelSet);
|
Assert.True(result.IsModelSet);
|
||||||
Assert.Same(expectedModel, result.Model);
|
Assert.Same(expectedModel, result.Model);
|
||||||
Assert.NotNull(result.ValidationNode);
|
|
||||||
Assert.True(result.ValidationNode.SuppressValidation);
|
var entry = bindingContext.ValidationState[result.Model];
|
||||||
|
Assert.True(entry.SuppressValidation);
|
||||||
|
Assert.Null(entry.Key);
|
||||||
|
Assert.Null(entry.Metadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -59,7 +63,8 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||||
{
|
{
|
||||||
HttpContext = new DefaultHttpContext(),
|
HttpContext = new DefaultHttpContext(),
|
||||||
MetadataProvider = metadataProvider,
|
MetadataProvider = metadataProvider,
|
||||||
}
|
},
|
||||||
|
ValidationState = new ValidationStateDictionary(),
|
||||||
};
|
};
|
||||||
|
|
||||||
bindingContext.OperationBindingContext.HttpContext.Request.Method = "GET";
|
bindingContext.OperationBindingContext.HttpContext.Request.Method = "GET";
|
||||||
|
|
|
||||||
|
|
@ -75,9 +75,7 @@ namespace ModelBindingWebSite.Controllers
|
||||||
OrderStatus model;
|
OrderStatus model;
|
||||||
if (Enum.TryParse<OrderStatus>("Status" + request.Query["status"], out model))
|
if (Enum.TryParse<OrderStatus>("Status" + request.Query["status"], out model))
|
||||||
{
|
{
|
||||||
var validationNode =
|
return ModelBindingResult.SuccessAsync("status", model);
|
||||||
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, model);
|
|
||||||
return ModelBindingResult.SuccessAsync("status", model, validationNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ModelBindingResult.FailedAsync("status");
|
return ModelBindingResult.FailedAsync("status");
|
||||||
|
|
@ -105,9 +103,7 @@ namespace ModelBindingWebSite.Controllers
|
||||||
var value = bindingContext.ValueProvider.GetValue(key);
|
var value = bindingContext.ValueProvider.GetValue(key);
|
||||||
model.ProductId = value.ConvertTo<int>();
|
model.ProductId = value.ConvertTo<int>();
|
||||||
|
|
||||||
var validationNode =
|
return ModelBindingResult.SuccessAsync(key, model);
|
||||||
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, value);
|
|
||||||
return ModelBindingResult.SuccessAsync(key, model, validationNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ModelBindingResult.NoResultAsync;
|
return ModelBindingResult.NoResultAsync;
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ namespace ModelBindingWebSite
|
||||||
if (!IsSimpleType(bindingContext.ModelType))
|
if (!IsSimpleType(bindingContext.ModelType))
|
||||||
{
|
{
|
||||||
model = Activator.CreateInstance(bindingContext.ModelType);
|
model = Activator.CreateInstance(bindingContext.ModelType);
|
||||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model, validationNode: null);
|
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ModelBindingResult.FailedAsync(bindingContext.ModelName);
|
return ModelBindingResult.FailedAsync(bindingContext.ModelName);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue