Rewrite of validation
This commit is contained in:
parent
0889b18f95
commit
8a502dbe5d
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using Microsoft.Framework.Internal;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
|
|
@ -59,6 +60,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
ModelState = modelState,
|
||||
OperationBindingContext = operationBindingContext,
|
||||
ValueProvider = operationBindingContext.ValueProvider,
|
||||
|
||||
ValidationState = new ValidationStateDictionary(),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -74,6 +77,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
ModelState = parent.ModelState,
|
||||
OperationBindingContext = parent.OperationBindingContext,
|
||||
ValueProvider = parent.ValueProvider,
|
||||
ValidationState = parent.ValidationState,
|
||||
|
||||
Model = model,
|
||||
ModelMetadata = modelMetadata,
|
||||
|
|
@ -174,5 +178,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
/// is eligible for model binding.
|
||||
/// </summary>
|
||||
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>
|
||||
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>
|
||||
|
|
@ -49,14 +49,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
/// </summary>
|
||||
/// <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="validationNode">The <see cref="ModelValidationNode"/>. May be <c>null</c>.</param>
|
||||
/// <returns>A <see cref="ModelBindingResult"/> representing a successful model bind.</returns>
|
||||
public static ModelBindingResult Success(
|
||||
[NotNull] string key,
|
||||
object model,
|
||||
ModelValidationNode validationNode)
|
||||
object model)
|
||||
{
|
||||
return new ModelBindingResult(key, model, isModelSet: true, validationNode: validationNode);
|
||||
return new ModelBindingResult(key, model, isModelSet: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -65,22 +63,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
/// </summary>
|
||||
/// <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="validationNode">The <see cref="ModelValidationNode"/>. May be <c>null</c>.</param>
|
||||
/// <returns>A completed <see cref="Task{ModelBindingResult}"/> representing a successful model bind.</returns>
|
||||
public static Task<ModelBindingResult> SuccessAsync(
|
||||
[NotNull] string key,
|
||||
object model,
|
||||
ModelValidationNode validationNode)
|
||||
object model)
|
||||
{
|
||||
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;
|
||||
Model = model;
|
||||
IsModelSet = isModelSet;
|
||||
ValidationNode = validationNode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -109,11 +104,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
/// </summary>
|
||||
public bool IsModelSet { get; }
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="ModelValidationNode"/> associated with the current <see cref="ModelBindingResult"/>.
|
||||
/// </summary>
|
||||
public ModelValidationNode ValidationNode { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Metadata;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
|
@ -14,6 +15,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
/// <summary>
|
||||
/// A metadata representation of a model type, property or parameter.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("{DebuggerToString(),nq}")]
|
||||
public abstract class ModelMetadata
|
||||
{
|
||||
private bool? _isComplexType;
|
||||
|
|
@ -407,5 +409,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
{
|
||||
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.
|
||||
// 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
|
||||
{
|
||||
/// <summary>
|
||||
/// A context object for <see cref="IModelValidator"/>.
|
||||
/// </summary>
|
||||
public class ModelValidationContext
|
||||
{
|
||||
public ModelValidationContext(
|
||||
[NotNull] ModelBindingContext bindingContext,
|
||||
[NotNull] ModelExplorer modelExplorer)
|
||||
: this(
|
||||
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>
|
||||
/// Gets or sets the model object.
|
||||
/// </summary>
|
||||
public object Model { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of the <see cref="ModelValidationContext"/> class using the
|
||||
/// <paramref name="parentContext" /> and <paramref name="modelExplorer"/>.
|
||||
/// Gets or sets the model container object.
|
||||
/// </summary>
|
||||
/// <param name="parentContext">Existing <see cref="ModelValidationContext"/>.</param>
|
||||
/// <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 object Container { get; set; }
|
||||
|
||||
public ModelExplorer ModelExplorer { get; }
|
||||
|
||||
public ModelStateDictionary ModelState { get; }
|
||||
|
||||
public BindingSource BindingSource { get; set; }
|
||||
|
||||
public IModelValidatorProvider ValidatorProvider { get; }
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ModelMetadata"/> associated with <see cref="Model"/>.
|
||||
/// </summary>
|
||||
public ModelMetadata Metadata { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
var modelBindingResult = await operationContext.ModelBinder.BindModelAsync(modelBindingContext);
|
||||
if (modelBindingResult.IsModelSet &&
|
||||
modelBindingResult.ValidationNode != null)
|
||||
if (modelBindingResult.IsModelSet)
|
||||
{
|
||||
var modelExplorer = new ModelExplorer(
|
||||
_modelMetadataProvider,
|
||||
metadata,
|
||||
modelBindingResult.Model);
|
||||
var validationContext = new ModelValidationContext(
|
||||
modelBindingContext.BindingSource,
|
||||
_validator.Validate(
|
||||
operationContext.ValidatorProvider,
|
||||
modelState,
|
||||
modelExplorer);
|
||||
|
||||
_validator.Validate(validationContext, modelBindingResult.ValidationNode);
|
||||
modelBindingContext.ValidationState,
|
||||
modelBindingResult.Key,
|
||||
modelBindingResult.Model);
|
||||
}
|
||||
|
||||
return modelBindingResult;
|
||||
|
|
|
|||
|
|
@ -87,12 +87,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
return ModelBindingResult.Failed(modelBindingKey);
|
||||
}
|
||||
|
||||
var validationNode = new ModelValidationNode(modelBindingKey, bindingContext.ModelMetadata, model)
|
||||
{
|
||||
ValidateAllProperties = true
|
||||
};
|
||||
|
||||
return ModelBindingResult.Success(modelBindingKey, model, validationNode);
|
||||
return ModelBindingResult.Success(modelBindingKey, model);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -44,12 +44,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
try
|
||||
{
|
||||
var model = Convert.FromBase64String(value);
|
||||
var validationNode = new ModelValidationNode(
|
||||
bindingContext.ModelName,
|
||||
bindingContext.ModelMetadata,
|
||||
model);
|
||||
|
||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model, validationNode);
|
||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,13 +17,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
if (bindingContext.ModelType == typeof(CancellationToken))
|
||||
{
|
||||
var model = bindingContext.OperationBindingContext.HttpContext.RequestAborted;
|
||||
var validationNode =
|
||||
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, model)
|
||||
{
|
||||
SuppressValidation = true,
|
||||
};
|
||||
|
||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model, validationNode);
|
||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model);
|
||||
}
|
||||
|
||||
return ModelBindingResult.NoResultAsync;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using System.Linq;
|
|||
using System.Reflection;
|
||||
#endif
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
|
|
@ -38,12 +39,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
model = CreateEmptyCollection(bindingContext.ModelType);
|
||||
}
|
||||
|
||||
var validationNode = new ModelValidationNode(
|
||||
bindingContext.ModelName,
|
||||
bindingContext.ModelMetadata,
|
||||
model);
|
||||
|
||||
return ModelBindingResult.Success(bindingContext.ModelName, model, validationNode);
|
||||
return ModelBindingResult.Success(bindingContext.ModelName, model);
|
||||
}
|
||||
|
||||
return ModelBindingResult.NoResult;
|
||||
|
|
@ -72,6 +68,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
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 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);
|
||||
}
|
||||
|
||||
return ModelBindingResult.Success(bindingContext.ModelName, model, result.ValidationNode);
|
||||
return ModelBindingResult.Success(bindingContext.ModelName, model);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
@ -137,11 +142,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
|
||||
var elementMetadata = metadataProvider.GetMetadataForType(typeof(TElement));
|
||||
|
||||
var validationNode = new ModelValidationNode(
|
||||
bindingContext.ModelName,
|
||||
bindingContext.ModelMetadata,
|
||||
boundCollection);
|
||||
|
||||
var innerBindingContext = ModelBindingContext.CreateChildBindingContext(
|
||||
bindingContext,
|
||||
elementMetadata,
|
||||
|
|
@ -164,18 +164,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
if (result != null && result.IsModelSet)
|
||||
{
|
||||
boundValue = result.Model;
|
||||
if (result.ValidationNode != null)
|
||||
{
|
||||
validationNode.ChildNodes.Add(result.ValidationNode);
|
||||
}
|
||||
|
||||
boundCollection.Add(ModelBindingHelper.CastOrDefault<TElement>(boundValue));
|
||||
}
|
||||
}
|
||||
|
||||
return new CollectionResult
|
||||
{
|
||||
ValidationNode = validationNode,
|
||||
Model = boundCollection
|
||||
};
|
||||
}
|
||||
|
|
@ -211,10 +205,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var elementMetadata = metadataProvider.GetMetadataForType(typeof(TElement));
|
||||
|
||||
var boundCollection = new List<TElement>();
|
||||
var validationNode = new ModelValidationNode(
|
||||
bindingContext.ModelName,
|
||||
bindingContext.ModelMetadata,
|
||||
boundCollection);
|
||||
|
||||
foreach (var indexName in indexNames)
|
||||
{
|
||||
var fullChildName = ModelNames.CreateIndexModelName(bindingContext.ModelName, indexName);
|
||||
|
|
@ -235,10 +226,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
{
|
||||
didBind = true;
|
||||
boundValue = result.Model;
|
||||
if (result.ValidationNode != null)
|
||||
{
|
||||
validationNode.ChildNodes.Add(result.ValidationNode);
|
||||
}
|
||||
}
|
||||
|
||||
// infinite size collection stops on first bind failure
|
||||
|
|
@ -252,17 +239,26 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
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 class CollectionResult
|
||||
{
|
||||
public ModelValidationNode ValidationNode { get; set; }
|
||||
|
||||
public IEnumerable<TElement> Model { get; set; }
|
||||
|
||||
public IValidationStrategy ValidationStrategy { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc.Core;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
|
|
@ -60,6 +61,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
if (result.IsModelSet ||
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -123,6 +137,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
BindingSource = oldBindingContext.BindingSource,
|
||||
BinderType = oldBindingContext.BinderType,
|
||||
IsTopLevelObject = oldBindingContext.IsTopLevelObject,
|
||||
ValidationState = oldBindingContext.ValidationState,
|
||||
};
|
||||
|
||||
if (bindingSource != null && bindingSource.IsGreedy)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using System.Linq;
|
|||
using System.Reflection;
|
||||
#endif
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
|
|
@ -24,7 +25,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
public override async Task<ModelBindingResult> BindModelAsync([NotNull] ModelBindingContext bindingContext)
|
||||
{
|
||||
var result = await base.BindModelAsync(bindingContext);
|
||||
if (result == null || !result.IsModelSet)
|
||||
if (!result.IsModelSet)
|
||||
{
|
||||
// No match for the prefix at all.
|
||||
return result;
|
||||
|
|
@ -65,8 +66,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
model: null);
|
||||
|
||||
var modelBinder = bindingContext.OperationBindingContext.ModelBinder;
|
||||
var validationNode = result.ValidationNode;
|
||||
|
||||
var keyMappings = new Dictionary<string, TKey>(StringComparer.Ordinal);
|
||||
foreach (var kvp in keys)
|
||||
{
|
||||
// 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.
|
||||
model[convertedKey] = ModelBindingHelper.CastOrDefault<TValue>(valueResult.Model);
|
||||
if (valueResult.IsModelSet)
|
||||
{
|
||||
validationNode.ChildNodes.Add(valueResult.ValidationNode);
|
||||
}
|
||||
keyMappings.Add(kvp.Key, convertedKey);
|
||||
}
|
||||
|
||||
bindingContext.ValidationState.Add(model, new ValidationStateEntry()
|
||||
{
|
||||
Strategy = new ShortFormDictionaryValidationStrategy<TKey, TValue>(keyMappings, valueMetadata),
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Framework.Internal;
|
||||
using Microsoft.Framework.Primitives;
|
||||
|
||||
|
|
@ -46,13 +47,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
model = new EmptyFormCollection();
|
||||
}
|
||||
|
||||
var validationNode =
|
||||
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, model)
|
||||
{
|
||||
SuppressValidation = true,
|
||||
};
|
||||
|
||||
return ModelBindingResult.Success(bindingContext.ModelName, model, validationNode);
|
||||
bindingContext.ValidationState.Add(model, new ValidationStateEntry() { SuppressValidation = true });
|
||||
return ModelBindingResult.Success(bindingContext.ModelName, model);
|
||||
}
|
||||
|
||||
private class EmptyFormCollection : IFormCollection
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ using System.Reflection;
|
|||
#endif
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Framework.Internal;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
|
||||
|
|
@ -62,18 +63,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
else
|
||||
{
|
||||
var validationNode =
|
||||
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, value)
|
||||
{
|
||||
SuppressValidation = true,
|
||||
};
|
||||
|
||||
bindingContext.ValidationState.Add(value, new ValidationStateEntry() { SuppressValidation = true });
|
||||
bindingContext.ModelState.SetModelValue(
|
||||
bindingContext.ModelName,
|
||||
rawValue: 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
|
||||
{
|
||||
var validationNode = new ModelValidationNode(
|
||||
bindingContext.ModelName,
|
||||
bindingContext.ModelMetadata,
|
||||
model);
|
||||
|
||||
bindingContext.ModelState.SetModelValue(
|
||||
bindingContext.ModelName,
|
||||
request.Headers.GetCommaSeparatedValues(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>),
|
||||
allowNullModel: true);
|
||||
|
||||
var childNodes = new List<ModelValidationNode>();
|
||||
var keyResult = await TryBindStrongModel<TKey>(bindingContext, "Key", childNodes);
|
||||
var valueResult = await TryBindStrongModel<TValue>(bindingContext, "Value", childNodes);
|
||||
var keyResult = await TryBindStrongModel<TKey>(bindingContext, "Key");
|
||||
var valueResult = await TryBindStrongModel<TValue>(bindingContext, "Value");
|
||||
|
||||
if (keyResult.IsModelSet && valueResult.IsModelSet)
|
||||
{
|
||||
|
|
@ -25,15 +24,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
ModelBindingHelper.CastOrDefault<TKey>(keyResult.Model),
|
||||
ModelBindingHelper.CastOrDefault<TValue>(valueResult.Model));
|
||||
|
||||
// Update the model for the top level validation node.
|
||||
var modelValidationNode =
|
||||
new ModelValidationNode(
|
||||
bindingContext.ModelName,
|
||||
bindingContext.ModelMetadata,
|
||||
model,
|
||||
childNodes);
|
||||
|
||||
return ModelBindingResult.Success(bindingContext.ModelName, model, modelValidationNode);
|
||||
return ModelBindingResult.Success(bindingContext.ModelName, model);
|
||||
}
|
||||
else if (!keyResult.IsModelSet && valueResult.IsModelSet)
|
||||
{
|
||||
|
|
@ -62,13 +53,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
if (bindingContext.IsTopLevelObject)
|
||||
{
|
||||
var model = new KeyValuePair<TKey, TValue>();
|
||||
|
||||
var validationNode = new ModelValidationNode(
|
||||
bindingContext.ModelName,
|
||||
bindingContext.ModelMetadata,
|
||||
model);
|
||||
|
||||
return ModelBindingResult.Success(bindingContext.ModelName, model, validationNode);
|
||||
return ModelBindingResult.Success(bindingContext.ModelName, model);
|
||||
}
|
||||
|
||||
return ModelBindingResult.NoResult;
|
||||
|
|
@ -77,8 +62,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
internal async Task<ModelBindingResult> TryBindStrongModel<TModel>(
|
||||
ModelBindingContext parentBindingContext,
|
||||
string propertyName,
|
||||
List<ModelValidationNode> childNodes)
|
||||
string propertyName)
|
||||
{
|
||||
var propertyModelMetadata =
|
||||
parentBindingContext.OperationBindingContext.MetadataProvider.GetMetadataForType(typeof(TModel));
|
||||
|
|
@ -91,19 +75,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
propertyModelName,
|
||||
model: null);
|
||||
|
||||
var modelBindingResult = await propertyBindingContext.OperationBindingContext.ModelBinder.BindModelAsync(
|
||||
var result = await propertyBindingContext.OperationBindingContext.ModelBinder.BindModelAsync(
|
||||
propertyBindingContext);
|
||||
if (modelBindingResult != ModelBindingResult.NoResult)
|
||||
if (result.IsModelSet)
|
||||
{
|
||||
if (modelBindingResult.ValidationNode != null)
|
||||
{
|
||||
childNodes.Add(modelBindingResult.ValidationNode);
|
||||
}
|
||||
|
||||
return modelBindingResult;
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ModelBindingResult.Failed(propertyModelName);
|
||||
}
|
||||
|
||||
return ModelBindingResult.Failed(propertyModelName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -309,22 +309,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var modelBindingResult = await modelBinder.BindModelAsync(modelBindingContext);
|
||||
if (modelBindingResult.IsModelSet)
|
||||
{
|
||||
var modelExplorer = new ModelExplorer(metadataProvider, modelMetadata, modelBindingResult.Model);
|
||||
var modelValidationContext = new ModelValidationContext(modelBindingContext, modelExplorer);
|
||||
|
||||
var validationNode = modelBindingResult.ValidationNode;
|
||||
if (validationNode == null)
|
||||
{
|
||||
validationNode = new ModelValidationNode(
|
||||
modelBindingResult.Key,
|
||||
modelMetadata,
|
||||
modelBindingResult.Model)
|
||||
{
|
||||
ValidateAllProperties = true,
|
||||
};
|
||||
}
|
||||
|
||||
objectModelValidator.Validate(modelValidationContext, validationNode);
|
||||
objectModelValidator.Validate(
|
||||
operationBindingContext.ValidatorProvider,
|
||||
modelState,
|
||||
modelBindingContext.ValidationState,
|
||||
modelBindingResult.Key,
|
||||
modelBindingResult.Model);
|
||||
|
||||
return modelState.IsValid;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,16 +52,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
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.
|
||||
bindingContext.Model = model;
|
||||
ProcessResults(bindingContext, results, validationNode);
|
||||
ProcessResults(bindingContext, results);
|
||||
|
||||
return ModelBindingResult.Success(bindingContext.ModelName, model, validationNode);
|
||||
return ModelBindingResult.Success(bindingContext.ModelName, model);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -398,10 +393,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
|
||||
// Internal for testing.
|
||||
internal ModelValidationNode ProcessResults(
|
||||
internal void ProcessResults(
|
||||
ModelBindingContext bindingContext,
|
||||
IDictionary<ModelMetadata, ModelBindingResult> results,
|
||||
ModelValidationNode validationNode)
|
||||
IDictionary<ModelMetadata, ModelBindingResult> results)
|
||||
{
|
||||
var metadataProvider = bindingContext.OperationBindingContext.MetadataProvider;
|
||||
var modelExplorer =
|
||||
|
|
@ -432,20 +426,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var result = entry.Value;
|
||||
var propertyMetadata = entry.Key;
|
||||
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>
|
||||
|
|
@ -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
|
||||
{
|
||||
public PropertyValidationInfo()
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
|
|
@ -30,13 +31,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
var requestServices = bindingContext.OperationBindingContext.HttpContext.RequestServices;
|
||||
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
|
||||
{
|
||||
var validationNode = new ModelValidationNode(
|
||||
bindingContext.ModelName,
|
||||
bindingContext.ModelMetadata,
|
||||
model);
|
||||
|
||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model, validationNode);
|
||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model);
|
||||
}
|
||||
}
|
||||
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.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
/// Recursively validate an object.
|
||||
/// The default implementation of <see cref="IObjectModelValidator"/>.
|
||||
/// </summary>
|
||||
public class DefaultObjectValidator : IObjectModelValidator
|
||||
{
|
||||
|
|
@ -27,324 +21,49 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
/// types to exclude from validation.</param>
|
||||
/// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
|
||||
public DefaultObjectValidator(
|
||||
[NotNull] IList<IExcludeTypeValidationFilter> excludeFilters,
|
||||
[NotNull] IModelMetadataProvider modelMetadataProvider)
|
||||
IList<IExcludeTypeValidationFilter> excludeFilters,
|
||||
IModelMetadataProvider modelMetadataProvider)
|
||||
{
|
||||
if (excludeFilters == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(excludeFilters));
|
||||
}
|
||||
|
||||
if (modelMetadataProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(modelMetadataProvider));
|
||||
}
|
||||
|
||||
_modelMetadataProvider = modelMetadataProvider;
|
||||
_excludeFilters = excludeFilters;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Validate(
|
||||
[NotNull] ModelValidationContext modelValidationContext,
|
||||
[NotNull] ModelValidationNode validationNode)
|
||||
IModelValidatorProvider validatorProvider,
|
||||
ModelStateDictionary modelState,
|
||||
ValidationStateDictionary validationState,
|
||||
string prefix,
|
||||
object model)
|
||||
{
|
||||
var validationContext = new ValidationContext()
|
||||
if (validatorProvider == null)
|
||||
{
|
||||
ModelValidationContext = modelValidationContext,
|
||||
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;
|
||||
throw new ArgumentNullException(nameof(validatorProvider));
|
||||
}
|
||||
|
||||
if (modelState.HasReachedMaxErrors)
|
||||
if (modelState == null)
|
||||
{
|
||||
// Short circuit if max errors have been recorded. In which case we treat this as invalid.
|
||||
return false;
|
||||
throw new ArgumentNullException(nameof(modelState));
|
||||
}
|
||||
|
||||
var isValid = true;
|
||||
if (validators == null)
|
||||
{
|
||||
// The validators are not null in the case of validating an array. Since the validators are
|
||||
// the same for all the elements of the array, we do not do GetValidators for each element,
|
||||
// instead we just pass them over.
|
||||
validators = GetValidators(modelValidationContext.ValidatorProvider, modelExplorer.Metadata);
|
||||
}
|
||||
var visitor = new ValidationVisitor(
|
||||
validatorProvider,
|
||||
_excludeFilters,
|
||||
modelState,
|
||||
validationState);
|
||||
|
||||
// We don't need to recursively traverse the graph if there are no child nodes.
|
||||
if (currentValidationNode.ChildNodes.Count == 0 && !currentValidationNode.ValidateAllProperties)
|
||||
{
|
||||
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);
|
||||
}
|
||||
var metadata = model == null ? null : _modelMetadataProvider.GetMetadataForType(model.GetType());
|
||||
visitor.Validate(metadata, prefix, model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
// 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
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -9,12 +11,20 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Validation
|
|||
public interface IObjectModelValidator
|
||||
{
|
||||
/// <summary>
|
||||
/// Validates the given model in <see cref="ModelValidationContext.ModelExplorer"/>.
|
||||
/// Validates the provided object.
|
||||
/// </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 name="validationNode">The <see cref="ModelValidationNode"/> for the model which gets validated.
|
||||
/// </param>
|
||||
void Validate(ModelValidationContext validationContext, ModelValidationNode validationNode);
|
||||
/// <param name="model">The model object.</param>
|
||||
void Validate(
|
||||
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)
|
||||
{
|
||||
var modelExplorer = validationContext.ModelExplorer;
|
||||
var metadata = modelExplorer.Metadata;
|
||||
|
||||
var metadata = validationContext.Metadata;
|
||||
var memberName = metadata.PropertyName ?? metadata.ModelType.Name;
|
||||
var containerExplorer = modelExplorer.Container;
|
||||
var container = validationContext.Container;
|
||||
|
||||
var container = containerExplorer?.Model;
|
||||
var context = new ValidationContext(container ?? modelExplorer.Model)
|
||||
var context = new ValidationContext(container ?? validationContext.Model)
|
||||
{
|
||||
DisplayName = metadata.GetDisplayName(),
|
||||
MemberName = memberName
|
||||
};
|
||||
|
||||
var result = Attribute.GetValidationResult(modelExplorer.Model, context);
|
||||
var result = Attribute.GetValidationResult(validationContext.Model, context);
|
||||
if (result != ValidationResult.Success)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
var model = context.ModelExplorer.Model;
|
||||
var model = context.Model;
|
||||
if (model == null)
|
||||
{
|
||||
return Enumerable.Empty<ModelValidationResult>();
|
||||
|
|
|
|||
|
|
@ -1514,18 +1514,12 @@ namespace Microsoft.AspNet.Mvc
|
|||
MetadataProvider,
|
||||
modelName);
|
||||
|
||||
var validationContext = new ModelValidationContext(
|
||||
bindingSource: null,
|
||||
validatorProvider: BindingContext.ValidatorProvider,
|
||||
modelState: ModelState,
|
||||
modelExplorer: modelExplorer);
|
||||
|
||||
ObjectValidator.Validate(
|
||||
validationContext,
|
||||
new ModelValidationNode(modelName, modelExplorer.Metadata, model)
|
||||
{
|
||||
ValidateAllProperties = true
|
||||
});
|
||||
BindingContext.ValidatorProvider,
|
||||
ModelState,
|
||||
validationState: null,
|
||||
prefix: prefix,
|
||||
model: model);
|
||||
return ModelState.IsValid;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -417,18 +417,14 @@ namespace System.Web.Http
|
|||
{
|
||||
var modelExplorer = MetadataProvider.GetModelExplorerForType(typeof(TEntity), entity);
|
||||
|
||||
var modelValidationContext = new ModelValidationContext(
|
||||
bindingSource: null,
|
||||
validatorProvider: BindingContext.ValidatorProvider,
|
||||
modelState: ModelState,
|
||||
modelExplorer: modelExplorer);
|
||||
var validatidationState = new ValidationStateDictionary();
|
||||
|
||||
ObjectValidator.Validate(
|
||||
modelValidationContext,
|
||||
new ModelValidationNode(keyPrefix, modelExplorer.Metadata, entity)
|
||||
{
|
||||
ValidateAllProperties = true
|
||||
});
|
||||
BindingContext.ValidatorProvider,
|
||||
ModelState,
|
||||
validatidationState,
|
||||
keyPrefix,
|
||||
entity);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||
{
|
||||
|
|
@ -18,13 +19,8 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
|||
if (bindingContext.ModelType == typeof(HttpRequestMessage))
|
||||
{
|
||||
var model = bindingContext.OperationBindingContext.HttpContext.GetHttpRequestMessage();
|
||||
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);
|
||||
}
|
||||
|
||||
return ModelBindingResult.NoResultAsync;
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
|||
{
|
||||
context.ModelMetadata = metadataProvider.GetMetadataForType(typeof(string));
|
||||
})
|
||||
.Returns(ModelBindingResult.SuccessAsync(string.Empty, value, validationNode: null));
|
||||
.Returns(ModelBindingResult.SuccessAsync(string.Empty, value));
|
||||
|
||||
var actionContext = new ActionContext(
|
||||
new DefaultHttpContext(),
|
||||
|
|
@ -153,19 +153,30 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
|||
var actionContext = GetActionContext(actionDescriptor);
|
||||
var actionBindingContext = GetActionBindingContext();
|
||||
|
||||
var mockValidatorProvider = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
||||
mockValidatorProvider
|
||||
.Setup(o => o.Validate(It.IsAny<ModelValidationContext>(), It.IsAny<ModelValidationNode>()))
|
||||
.Verifiable();
|
||||
var argumentBinder = GetArgumentBinder(mockValidatorProvider.Object);
|
||||
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>()));
|
||||
|
||||
var argumentBinder = GetArgumentBinder(mockValidator.Object);
|
||||
|
||||
// Act
|
||||
var result = await argumentBinder
|
||||
.BindActionArgumentsAsync(actionContext, actionBindingContext, new TestController());
|
||||
|
||||
// Assert
|
||||
mockValidatorProvider.Verify(
|
||||
o => o.Validate(It.IsAny<ModelValidationContext>(), It.IsAny<ModelValidationNode>()), Times.Once());
|
||||
mockValidator
|
||||
.Verify(o => o.Validate(
|
||||
It.IsAny<IModelValidatorProvider>(),
|
||||
It.IsAny<ModelStateDictionary>(),
|
||||
It.IsAny<ValidationStateDictionary>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<object>()),
|
||||
Times.Once());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -197,19 +208,29 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
|||
ModelBinder = binder.Object,
|
||||
};
|
||||
|
||||
var mockValidatorProvider = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
||||
mockValidatorProvider
|
||||
.Setup(o => o.Validate(It.IsAny<ModelValidationContext>(), It.IsAny<ModelValidationNode>()))
|
||||
.Verifiable();
|
||||
var argumentBinder = GetArgumentBinder(mockValidatorProvider.Object);
|
||||
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>()));
|
||||
|
||||
var argumentBinder = GetArgumentBinder(mockValidator.Object);
|
||||
|
||||
// Act
|
||||
var result = await argumentBinder
|
||||
.BindActionArgumentsAsync(actionContext, actionBindingContext, new TestController());
|
||||
|
||||
// Assert
|
||||
mockValidatorProvider.Verify(
|
||||
o => o.Validate(It.IsAny<ModelValidationContext>(), It.IsAny<ModelValidationNode>()),
|
||||
mockValidator
|
||||
.Verify(o => o.Validate(
|
||||
It.IsAny<IModelValidatorProvider>(),
|
||||
It.IsAny<ModelStateDictionary>(),
|
||||
It.IsAny<ValidationStateDictionary>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<object>()),
|
||||
Times.Never());
|
||||
}
|
||||
|
||||
|
|
@ -228,19 +249,30 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
|||
var actionContext = GetActionContext(actionDescriptor);
|
||||
var actionBindingContext = GetActionBindingContext();
|
||||
|
||||
var mockValidatorProvider = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
||||
mockValidatorProvider
|
||||
.Setup(o => o.Validate(It.IsAny<ModelValidationContext>(), It.IsAny<ModelValidationNode>()))
|
||||
.Verifiable();
|
||||
var argumentBinder = GetArgumentBinder(mockValidatorProvider.Object);
|
||||
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>()));
|
||||
|
||||
var argumentBinder = GetArgumentBinder(mockValidator.Object);
|
||||
|
||||
// Act
|
||||
var result = await argumentBinder
|
||||
.BindActionArgumentsAsync(actionContext, actionBindingContext, new TestController());
|
||||
|
||||
// Assert
|
||||
mockValidatorProvider.Verify(
|
||||
o => o.Validate(It.IsAny<ModelValidationContext>(), It.IsAny<ModelValidationNode>()), Times.Once());
|
||||
mockValidator
|
||||
.Verify(o => o.Validate(
|
||||
It.IsAny<IModelValidatorProvider>(),
|
||||
It.IsAny<ModelStateDictionary>(),
|
||||
It.IsAny<ValidationStateDictionary>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<object>()),
|
||||
Times.Once());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -271,20 +303,29 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
|||
ModelBinder = binder.Object,
|
||||
};
|
||||
|
||||
var mockValidatorProvider = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
||||
mockValidatorProvider
|
||||
.Setup(o => o.Validate(It.IsAny<ModelValidationContext>(), It.IsAny<ModelValidationNode>()))
|
||||
.Verifiable();
|
||||
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>()));
|
||||
|
||||
var argumentBinder = GetArgumentBinder(mockValidatorProvider.Object);
|
||||
var argumentBinder = GetArgumentBinder(mockValidator.Object);
|
||||
|
||||
// Act
|
||||
var result = await argumentBinder
|
||||
.BindActionArgumentsAsync(actionContext, actionBindingContext, new TestController());
|
||||
|
||||
// Assert
|
||||
mockValidatorProvider.Verify(
|
||||
o => o.Validate(It.IsAny<ModelValidationContext>(), It.IsAny<ModelValidationNode>()),
|
||||
mockValidator
|
||||
.Verify(o => o.Validate(
|
||||
It.IsAny<IModelValidatorProvider>(),
|
||||
It.IsAny<ModelStateDictionary>(),
|
||||
It.IsAny<ValidationStateDictionary>(),
|
||||
It.IsAny<string>(),
|
||||
It.IsAny<object>()),
|
||||
Times.Never());
|
||||
}
|
||||
|
||||
|
|
@ -363,7 +404,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
|||
var binder = new Mock<IModelBinder>();
|
||||
binder
|
||||
.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()
|
||||
{
|
||||
|
|
@ -401,7 +442,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
|||
var binder = new Mock<IModelBinder>();
|
||||
binder
|
||||
.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()
|
||||
{
|
||||
|
|
@ -545,7 +586,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
|||
object model;
|
||||
if (inputPropertyValues.TryGetValue(bindingContext.ModelName, out model))
|
||||
{
|
||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model, validationNode: null);
|
||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -600,8 +641,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
|||
binder.Setup(b => b.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||
.Returns<ModelBindingContext>(mbc =>
|
||||
{
|
||||
var validationNode = new ModelValidationNode(string.Empty, mbc.ModelMetadata, model);
|
||||
return ModelBindingResult.SuccessAsync(string.Empty, model, validationNode: validationNode);
|
||||
return ModelBindingResult.SuccessAsync(string.Empty, model);
|
||||
});
|
||||
|
||||
return new ActionBindingContext()
|
||||
|
|
@ -614,10 +654,7 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
|||
{
|
||||
if (validator == null)
|
||||
{
|
||||
var mockValidator = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
||||
mockValidator.Setup(
|
||||
o => o.Validate(It.IsAny<ModelValidationContext>(), It.IsAny<ModelValidationNode>()));
|
||||
validator = mockValidator.Object;
|
||||
validator = CreateMockValidator();
|
||||
}
|
||||
|
||||
return new DefaultControllerActionArgumentBinder(
|
||||
|
|
@ -625,6 +662,19 @@ namespace Microsoft.AspNet.Mvc.Controllers
|
|||
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
|
||||
// to the BoundProperties collection, bypassing usual requirements.
|
||||
private class TestController
|
||||
|
|
|
|||
|
|
@ -62,10 +62,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.Empty(Assert.IsType<string[]>(result.Model));
|
||||
Assert.Equal("modelName", result.Key);
|
||||
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]
|
||||
|
|
@ -173,7 +169,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
if (value != ValueProviderResult.None)
|
||||
{
|
||||
var model = value.ConvertTo(mbc.ModelType);
|
||||
return ModelBindingResult.SuccessAsync(mbc.ModelName, model, validationNode: null);
|
||||
return ModelBindingResult.SuccessAsync(mbc.ModelName, model);
|
||||
}
|
||||
return ModelBindingResult.NoResultAsync;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -67,9 +67,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
var p = (Person)binderResult.Model;
|
||||
Assert.Equal(model.Age, p.Age);
|
||||
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]
|
||||
|
|
@ -141,9 +138,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
|
||||
public Task<ModelBindingResult> BindModelAsync(ModelBindingContext bindingContext)
|
||||
{
|
||||
var validationNode =
|
||||
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, _model);
|
||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, _model, validationNode);
|
||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, _model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,13 +50,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
mockInputFormatter.Verify(v => v.ReadAsync(It.IsAny<InputFormatterContext>()), Times.Once);
|
||||
Assert.NotNull(binderResult);
|
||||
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]
|
||||
|
|
@ -78,7 +71,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
// Returns non-null because it understands the metadata type.
|
||||
Assert.NotNull(binderResult);
|
||||
Assert.False(binderResult.IsModelSet);
|
||||
Assert.Null(binderResult.ValidationNode);
|
||||
Assert.Null(binderResult.Model);
|
||||
|
||||
// Key is empty because this was a top-level binding.
|
||||
|
|
@ -104,7 +96,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
// Assert
|
||||
Assert.NotNull(binderResult);
|
||||
Assert.False(binderResult.IsModelSet);
|
||||
Assert.Null(binderResult.ValidationNode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -172,7 +163,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
// Returns non-null because it understands the metadata type.
|
||||
Assert.NotNull(binderResult);
|
||||
Assert.False(binderResult.IsModelSet);
|
||||
Assert.Null(binderResult.ValidationNode);
|
||||
Assert.Null(binderResult.Model);
|
||||
|
||||
// Key is empty because this was a top-level binding.
|
||||
|
|
@ -209,7 +199,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
Assert.NotNull(binderResult);
|
||||
Assert.False(binderResult.IsModelSet);
|
||||
Assert.Null(binderResult.Model);
|
||||
Assert.Null(binderResult.ValidationNode);
|
||||
|
||||
// Key is empty because this was a top-level binding.
|
||||
var entry = Assert.Single(bindingContext.ModelState);
|
||||
|
|
|
|||
|
|
@ -25,8 +25,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||
Assert.True(result.IsModelSet);
|
||||
Assert.Equal(bindingContext.OperationBindingContext.HttpContext.RequestAborted, result.Model);
|
||||
Assert.NotNull(result.ValidationNode);
|
||||
Assert.True(result.ValidationNode.SuppressValidation);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using System.Linq;
|
|||
#endif
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http.Internal;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
#if DNX451
|
||||
using Moq;
|
||||
#endif
|
||||
|
|
@ -32,13 +33,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
var binder = new CollectionModelBinder<int>();
|
||||
|
||||
// Act
|
||||
var boundCollection = await binder.BindComplexCollectionFromIndexes(bindingContext, new[] { "foo", "bar", "baz" });
|
||||
var collectionResult = await binder.BindComplexCollectionFromIndexes(
|
||||
bindingContext,
|
||||
new[] { "foo", "bar", "baz" });
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new[] { 42, 0, 200 }, boundCollection.Model.ToArray());
|
||||
Assert.Equal(
|
||||
new[] { "someName[foo]", "someName[baz]" },
|
||||
boundCollection.ValidationNode.ChildNodes.Select(o => o.Key).ToArray());
|
||||
Assert.Equal(new[] { 42, 0, 200 }, collectionResult.Model.ToArray());
|
||||
|
||||
// This requires a non-default IValidationStrategy
|
||||
var strategy = Assert.IsType<ExplicitIndexCollectionValidationStrategy>(collectionResult.ValidationStrategy);
|
||||
Assert.Equal(new[] { "foo", "bar", "baz" }, strategy.ElementKeys);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -59,9 +63,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
|
||||
// Assert
|
||||
Assert.Equal(new[] { 42, 100 }, boundCollection.Model.ToArray());
|
||||
Assert.Equal(
|
||||
new[] { "someName[0]", "someName[1]" },
|
||||
boundCollection.ValidationNode.ChildNodes.Select(o => o.Key).ToArray());
|
||||
|
||||
// This uses the default IValidationStrategy
|
||||
Assert.DoesNotContain(boundCollection, bindingContext.ValidationState.Keys);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -197,7 +201,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||
Assert.True(result.IsModelSet);
|
||||
Assert.NotNull(result.Model);
|
||||
Assert.NotNull(result.ValidationNode);
|
||||
|
||||
var model = Assert.IsType<List<int>>(result.Model);
|
||||
Assert.Empty(model);
|
||||
|
|
@ -252,10 +255,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.Empty(Assert.IsType<List<string>>(result.Model));
|
||||
Assert.Equal("modelName", result.Key);
|
||||
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
|
||||
|
|
@ -290,10 +289,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.Empty(list);
|
||||
Assert.Equal("modelName", result.Key);
|
||||
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]
|
||||
|
|
@ -361,14 +356,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
// Arrange
|
||||
var culture = new CultureInfo("fr-FR");
|
||||
var bindingContext = GetModelBindingContext(new SimpleValueProvider());
|
||||
ModelValidationNode childValidationNode = null;
|
||||
|
||||
Mock.Get<IModelBinder>(bindingContext.OperationBindingContext.ModelBinder)
|
||||
.Setup(o => o.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||
.Returns((ModelBindingContext mbc) =>
|
||||
{
|
||||
Assert.Equal("someName", mbc.ModelName);
|
||||
childValidationNode = new ModelValidationNode("someName", mbc.ModelMetadata, mbc.Model);
|
||||
return ModelBindingResult.SuccessAsync(mbc.ModelName, 42, childValidationNode);
|
||||
return ModelBindingResult.SuccessAsync(mbc.ModelName, 42);
|
||||
});
|
||||
var modelBinder = new CollectionModelBinder<int>();
|
||||
|
||||
|
|
@ -379,7 +373,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
|
||||
// Assert
|
||||
Assert.Equal(new[] { 42 }, boundCollection.Model.ToArray());
|
||||
Assert.Equal(new[] { childValidationNode }, boundCollection.ValidationNode.ChildNodes.ToArray());
|
||||
}
|
||||
|
||||
private static ModelBindingContext GetModelBindingContext(
|
||||
|
|
@ -399,7 +392,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
{
|
||||
ModelBinder = CreateIntBinder(),
|
||||
MetadataProvider = metadataProvider
|
||||
}
|
||||
},
|
||||
ValidationState = new ValidationStateDictionary(),
|
||||
};
|
||||
|
||||
return bindingContext;
|
||||
|
|
@ -425,8 +419,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
}
|
||||
else
|
||||
{
|
||||
var validationNode = new ModelValidationNode(mbc.ModelName, mbc.ModelMetadata, model);
|
||||
return ModelBindingResult.SuccessAsync(mbc.ModelName, model, validationNode);
|
||||
return ModelBindingResult.SuccessAsync(mbc.ModelName, model);
|
||||
}
|
||||
});
|
||||
return mockIntBinder.Object;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -28,6 +29,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
{
|
||||
{ "someName", "dummyValue" }
|
||||
},
|
||||
ValidationState = new ValidationStateDictionary(),
|
||||
};
|
||||
|
||||
var mockIntBinder = new Mock<IModelBinder>();
|
||||
|
|
@ -40,7 +42,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.Equal("someName", context.ModelName);
|
||||
Assert.Same(bindingContext.ValueProvider, context.ValueProvider);
|
||||
|
||||
return ModelBindingResult.SuccessAsync("someName", 42, validationNode: null);
|
||||
return ModelBindingResult.SuccessAsync("someName", 42);
|
||||
});
|
||||
var shimBinder = CreateCompositeBinder(mockIntBinder.Object);
|
||||
|
||||
|
|
@ -53,6 +55,96 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
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]
|
||||
public async Task BindModel_SuccessfulBind_ComplexTypeFallback_ReturnsModel()
|
||||
{
|
||||
|
|
@ -71,6 +163,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
{
|
||||
{ "someOtherName", "dummyValue" }
|
||||
},
|
||||
ValidationState = new ValidationStateDictionary(),
|
||||
};
|
||||
|
||||
var mockIntBinder = new Mock<IModelBinder>();
|
||||
|
|
@ -88,7 +181,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.Equal("", mbc.ModelName);
|
||||
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);
|
||||
|
|
@ -223,7 +316,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
var modelBinder = new Mock<IModelBinder>();
|
||||
modelBinder
|
||||
.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);
|
||||
|
||||
|
|
@ -308,26 +401,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
var model = Assert.IsType<SimplePropertiesModel>(result.Model);
|
||||
Assert.Equal("firstName-value", model.FirstName);
|
||||
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]
|
||||
|
|
@ -415,15 +488,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
{
|
||||
// Arrange
|
||||
var valueProvider = new SimpleValueProvider();
|
||||
ModelValidationNode validationNode = null;
|
||||
|
||||
var mockBinder = new Mock<IModelBinder>();
|
||||
mockBinder
|
||||
.Setup(o => o.BindModelAsync(It.IsAny<ModelBindingContext>()))
|
||||
.Returns((ModelBindingContext context) =>
|
||||
{
|
||||
validationNode = new ModelValidationNode("someName", context.ModelMetadata, 42);
|
||||
return ModelBindingResult.SuccessAsync("someName", 42, validationNode);
|
||||
return ModelBindingResult.SuccessAsync("someName", 42);
|
||||
});
|
||||
|
||||
var binder = CreateCompositeBinder(mockBinder.Object);
|
||||
|
|
@ -435,7 +506,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
// Assert
|
||||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||
Assert.True(result.IsModelSet);
|
||||
Assert.Same(validationNode, result.ValidationNode);
|
||||
}
|
||||
|
||||
private static ModelBindingContext CreateBindingContext(
|
||||
|
|
@ -456,7 +526,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
{
|
||||
MetadataProvider = metadataProvider,
|
||||
ModelBinder = binder,
|
||||
}
|
||||
},
|
||||
ValidationState = new ValidationStateDictionary(),
|
||||
};
|
||||
return bindingContext;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using System.Globalization;
|
|||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http.Internal;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Framework.Primitives;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
|
@ -45,6 +46,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.Equal(2, dictionary.Count);
|
||||
Assert.Equal("forty-two", dictionary[42]);
|
||||
Assert.Equal("eighty-four", dictionary[84]);
|
||||
|
||||
// This uses the default IValidationStrategy
|
||||
Assert.DoesNotContain(result.Model, bindingContext.ValidationState.Keys);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -78,6 +82,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.Equal(2, dictionary.Count);
|
||||
Assert.Equal("forty-two", dictionary[42]);
|
||||
Assert.Equal("eighty-four", dictionary[84]);
|
||||
|
||||
// This uses the default IValidationStrategy
|
||||
Assert.DoesNotContain(result.Model, bindingContext.ValidationState.Keys);
|
||||
}
|
||||
|
||||
// modelName, keyFormat, dictionary
|
||||
|
|
@ -135,7 +142,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||
Assert.True(result.IsModelSet);
|
||||
Assert.Equal(modelName, result.Key);
|
||||
Assert.NotNull(result.ValidationNode);
|
||||
|
||||
var resultDictionary = Assert.IsAssignableFrom<IDictionary<string, string>>(result.Model);
|
||||
Assert.Equal(dictionary, resultDictionary);
|
||||
|
|
@ -172,7 +178,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||
Assert.True(result.IsModelSet);
|
||||
Assert.Equal("prefix", result.Key);
|
||||
Assert.NotNull(result.ValidationNode);
|
||||
|
||||
var resultDictionary = Assert.IsAssignableFrom<IDictionary<string, string>>(result.Model);
|
||||
Assert.Empty(resultDictionary);
|
||||
|
|
@ -223,7 +228,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||
Assert.True(result.IsModelSet);
|
||||
Assert.Equal("prefix", result.Key);
|
||||
Assert.NotNull(result.ValidationNode);
|
||||
|
||||
var resultDictionary = Assert.IsAssignableFrom<IDictionary<long, int>>(result.Model);
|
||||
Assert.Equal(dictionary, resultDictionary);
|
||||
|
|
@ -264,10 +268,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||
Assert.True(result.IsModelSet);
|
||||
Assert.Equal("prefix", result.Key);
|
||||
Assert.NotNull(result.ValidationNode);
|
||||
|
||||
var resultDictionary = Assert.IsAssignableFrom<IDictionary<int, ModelWithProperties>>(result.Model);
|
||||
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]
|
||||
|
|
@ -298,7 +313,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||
Assert.True(result.IsModelSet);
|
||||
Assert.Equal(modelName, result.Key);
|
||||
Assert.NotNull(result.ValidationNode);
|
||||
|
||||
var resultDictionary = Assert.IsAssignableFrom<SortedDictionary<string, string>>(result.Model);
|
||||
Assert.Equal(expectedDictionary, resultDictionary);
|
||||
|
|
@ -330,10 +344,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.Empty(Assert.IsType<Dictionary<string, string>>(result.Model));
|
||||
Assert.Equal("modelName", result.Key);
|
||||
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]
|
||||
|
|
@ -403,7 +413,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
{
|
||||
HttpContext = new DefaultHttpContext(),
|
||||
MetadataProvider = new TestModelMetadataProvider(),
|
||||
}
|
||||
},
|
||||
ValidationState = new ValidationStateDictionary(),
|
||||
};
|
||||
|
||||
return modelBindingContext;
|
||||
|
|
@ -462,7 +473,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
KeyValuePair<int, string> value;
|
||||
if (values.TryGetValue(mbc.ModelName, out value))
|
||||
{
|
||||
return ModelBindingResult.SuccessAsync(mbc.ModelName, value, validationNode: null);
|
||||
return ModelBindingResult.SuccessAsync(mbc.ModelName, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -488,6 +499,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
ValueProvider = valueProvider,
|
||||
},
|
||||
ValueProvider = valueProvider,
|
||||
ValidationState = new ValidationStateDictionary(),
|
||||
};
|
||||
|
||||
return bindingContext;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Internal;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Framework.Primitives;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
|
@ -36,8 +37,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
// Assert
|
||||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||
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);
|
||||
Assert.Equal(2, form.Count);
|
||||
|
|
@ -124,7 +128,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
ModelBinder = new FormCollectionModelBinder(),
|
||||
MetadataProvider = metadataProvider,
|
||||
HttpContext = httpContext,
|
||||
}
|
||||
},
|
||||
ValidationState = new ValidationStateDictionary(),
|
||||
};
|
||||
|
||||
return bindingContext;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using System.Threading;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Internal;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
|
@ -17,6 +18,29 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
{
|
||||
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]
|
||||
public async Task FormFileModelBinder_ExpectMultipleFiles_BindSuccessful()
|
||||
{
|
||||
|
|
@ -34,8 +58,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
// Assert
|
||||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||
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);
|
||||
Assert.Equal(2, files.Count);
|
||||
|
|
@ -197,7 +224,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
ModelBinder = new FormFileModelBinder(),
|
||||
MetadataProvider = metadataProvider,
|
||||
HttpContext = httpContext,
|
||||
}
|
||||
},
|
||||
ValidationState = new ValidationStateDictionary(),
|
||||
};
|
||||
|
||||
return bindingContext;
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||
Assert.Null(result.Model);
|
||||
Assert.False(bindingContext.ModelState.IsValid);
|
||||
Assert.Null(result.ValidationNode);
|
||||
Assert.Equal("someName", bindingContext.ModelName);
|
||||
var error = Assert.Single(bindingContext.ModelState["someName.Key"].Errors);
|
||||
Assert.Equal("A value is required.", error.ErrorMessage);
|
||||
|
|
@ -54,7 +53,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
// Assert
|
||||
Assert.Null(result.Model);
|
||||
Assert.False(bindingContext.ModelState.IsValid);
|
||||
Assert.Null(result.ValidationNode);
|
||||
Assert.Equal("someName", bindingContext.ModelName);
|
||||
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.NotEqual(ModelBindingResult.NoResult, result);
|
||||
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]
|
||||
|
|
@ -126,11 +111,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
ModelBindingResult innerResult;
|
||||
if (isSuccess)
|
||||
{
|
||||
innerResult = ModelBindingResult.Success(string.Empty, model, validationNode: null);
|
||||
innerResult = ModelBindingResult.Success("somename.key", model);
|
||||
}
|
||||
else
|
||||
{
|
||||
innerResult = ModelBindingResult.Failed(string.Empty);
|
||||
innerResult = ModelBindingResult.Failed("somename.key");
|
||||
}
|
||||
|
||||
var innerBinder = new Mock<IModelBinder>();
|
||||
|
|
@ -144,10 +129,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
var bindingContext = GetBindingContext(new SimpleValueProvider(), innerBinder.Object);
|
||||
|
||||
var binder = new KeyValuePairModelBinder<int, string>();
|
||||
var modelValidationNodeList = new List<ModelValidationNode>();
|
||||
|
||||
// Act
|
||||
var result = await binder.TryBindStrongModel<int>(bindingContext, "key", modelValidationNodeList);
|
||||
var result = await binder.TryBindStrongModel<int>(bindingContext, "key");
|
||||
|
||||
// Assert
|
||||
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("modelName", result.Key);
|
||||
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]
|
||||
|
|
@ -260,8 +240,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
if (mbc.ModelType == typeof(int))
|
||||
{
|
||||
var model = 42;
|
||||
var validationNode = new ModelValidationNode(mbc.ModelName, mbc.ModelMetadata, model);
|
||||
return ModelBindingResult.SuccessAsync(mbc.ModelName, model, validationNode);
|
||||
return ModelBindingResult.SuccessAsync(mbc.ModelName, model);
|
||||
}
|
||||
return ModelBindingResult.NoResultAsync;
|
||||
});
|
||||
|
|
@ -278,8 +257,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
if (mbc.ModelType == typeof(string))
|
||||
{
|
||||
var model = "some-value";
|
||||
var validationNode = new ModelValidationNode(mbc.ModelName, mbc.ModelMetadata, model);
|
||||
return ModelBindingResult.SuccessAsync(mbc.ModelName, model, validationNode);
|
||||
return ModelBindingResult.SuccessAsync(mbc.ModelName, model);
|
||||
}
|
||||
return ModelBindingResult.NoResultAsync;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -15,16 +15,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
// Arrange
|
||||
var key = "someName";
|
||||
var model = "some model";
|
||||
var validationNode = new ModelValidationNode(key, null, model);
|
||||
|
||||
// Act
|
||||
var result = ModelBindingResult.Success(key, model, validationNode);
|
||||
var result = ModelBindingResult.Success(key, model);
|
||||
|
||||
// Assert
|
||||
Assert.Same(key, result.Key);
|
||||
Assert.True(result.IsModelSet);
|
||||
Assert.Same(model, result.Model);
|
||||
Assert.Same(validationNode, result.ValidationNode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -33,16 +31,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
// Arrange
|
||||
var key = "someName";
|
||||
var model = "some model";
|
||||
var validationNode = new ModelValidationNode(key, null, model);
|
||||
|
||||
// Act
|
||||
var result = await ModelBindingResult.SuccessAsync(key, model, validationNode);
|
||||
var result = await ModelBindingResult.SuccessAsync(key, model);
|
||||
|
||||
// Assert
|
||||
Assert.Same(key, result.Key);
|
||||
Assert.True(result.IsModelSet);
|
||||
Assert.Same(model, result.Model);
|
||||
Assert.Same(validationNode, result.ValidationNode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -58,7 +54,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.Same(key, result.Key);
|
||||
Assert.False(result.IsModelSet);
|
||||
Assert.Null(result.Model);
|
||||
Assert.Null(result.ValidationNode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -74,7 +69,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.Same(key, result.Key);
|
||||
Assert.False(result.IsModelSet);
|
||||
Assert.Null(result.Model);
|
||||
Assert.Null(result.ValidationNode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -87,7 +81,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.Null(result.Key);
|
||||
Assert.False(result.IsModelSet);
|
||||
Assert.Null(result.Model);
|
||||
Assert.Null(result.ValidationNode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -100,7 +93,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
Assert.Null(result.Key);
|
||||
Assert.False(result.IsModelSet);
|
||||
Assert.Null(result.Model);
|
||||
Assert.Null(result.ValidationNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -786,13 +786,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
property => property,
|
||||
property => ModelBindingResult.Failed(property.PropertyName));
|
||||
var nameProperty = containerMetadata.Properties[nameof(model.Name)];
|
||||
results[nameProperty] = ModelBindingResult.Success(string.Empty, "John Doe", validationNode: null);
|
||||
|
||||
var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
|
||||
results[nameProperty] = ModelBindingResult.Success(string.Empty, "John Doe");
|
||||
|
||||
var testableBinder = new TestableMutableObjectModelBinder();
|
||||
|
||||
// Act
|
||||
testableBinder.ProcessResults(bindingContext, results, modelValidationNode);
|
||||
testableBinder.ProcessResults(bindingContext, results);
|
||||
|
||||
// Assert
|
||||
var modelStateDictionary = bindingContext.ModelState;
|
||||
|
|
@ -837,13 +836,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
property => property,
|
||||
property => ModelBindingResult.Failed(property.PropertyName));
|
||||
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();
|
||||
|
||||
// Act
|
||||
testableBinder.ProcessResults(bindingContext, results, modelValidationNode);
|
||||
testableBinder.ProcessResults(bindingContext, results);
|
||||
|
||||
// Assert
|
||||
var modelStateDictionary = bindingContext.ModelState;
|
||||
|
|
@ -888,18 +886,17 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
property => property,
|
||||
property => ModelBindingResult.Failed(property.PropertyName));
|
||||
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
|
||||
// case because the binding exists.
|
||||
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 modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
|
||||
|
||||
// Act
|
||||
testableBinder.ProcessResults(bindingContext, results, modelValidationNode);
|
||||
testableBinder.ProcessResults(bindingContext, results);
|
||||
|
||||
// Assert
|
||||
var modelStateDictionary = bindingContext.ModelState;
|
||||
|
|
@ -929,10 +926,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
property => ModelBindingResult.Failed(property.PropertyName));
|
||||
|
||||
var testableBinder = new TestableMutableObjectModelBinder();
|
||||
var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
|
||||
|
||||
// Act
|
||||
testableBinder.ProcessResults(bindingContext, results, modelValidationNode);
|
||||
testableBinder.ProcessResults(bindingContext, results);
|
||||
|
||||
// Assert
|
||||
var modelStateDictionary = bindingContext.ModelState;
|
||||
|
|
@ -952,10 +948,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
property => ModelBindingResult.Failed(property.PropertyName));
|
||||
|
||||
var testableBinder = new TestableMutableObjectModelBinder();
|
||||
var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
|
||||
|
||||
// Act
|
||||
testableBinder.ProcessResults(bindingContext, results, modelValidationNode);
|
||||
testableBinder.ProcessResults(bindingContext, results);
|
||||
|
||||
// Assert
|
||||
var modelStateDictionary = bindingContext.ModelState;
|
||||
|
|
@ -984,20 +979,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequired)];
|
||||
results[propertyMetadata] = ModelBindingResult.Success(
|
||||
key: "theModel." + nameof(Person.ValueTypeRequired),
|
||||
model: null,
|
||||
validationNode: null);
|
||||
model: null);
|
||||
|
||||
// Make ValueTypeRequiredWithDefaultValue invalid
|
||||
propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequiredWithDefaultValue)];
|
||||
results[propertyMetadata] = ModelBindingResult.Success(
|
||||
key: "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue),
|
||||
model: null,
|
||||
validationNode: null);
|
||||
|
||||
var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
|
||||
model: null);
|
||||
|
||||
// Act
|
||||
testableBinder.ProcessResults(bindingContext, results, modelValidationNode);
|
||||
testableBinder.ProcessResults(bindingContext, results);
|
||||
|
||||
// Assert
|
||||
Assert.False(modelStateDictionary.IsValid);
|
||||
|
|
@ -1056,10 +1047,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
results[propertyMetadata] = ModelBindingResult.Failed(
|
||||
key: "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue));
|
||||
|
||||
var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
|
||||
|
||||
// Act
|
||||
testableBinder.ProcessResults(bindingContext, results, modelValidationNode);
|
||||
testableBinder.ProcessResults(bindingContext, results);
|
||||
|
||||
// Assert
|
||||
Assert.True(modelState.IsValid);
|
||||
|
|
@ -1084,24 +1073,21 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequired)];
|
||||
results[propertyMetadata] = ModelBindingResult.Success(
|
||||
key: "theModel." + nameof(Person.ValueTypeRequired),
|
||||
model: 41,
|
||||
validationNode: null);
|
||||
model: 41);
|
||||
|
||||
// Make ValueTypeRequiredWithDefaultValue valid.
|
||||
propertyMetadata = containerMetadata.Properties[nameof(Person.ValueTypeRequiredWithDefaultValue)];
|
||||
results[propertyMetadata] = ModelBindingResult.Success(
|
||||
key: "theModel." + nameof(Person.ValueTypeRequiredWithDefaultValue),
|
||||
model: 57,
|
||||
validationNode: null);
|
||||
model: 57);
|
||||
|
||||
// Also remind ProcessResults about PropertyWithDefaultValue -- as BindPropertiesAsync() would.
|
||||
propertyMetadata = containerMetadata.Properties[nameof(Person.PropertyWithDefaultValue)];
|
||||
results[propertyMetadata] = ModelBindingResult.Failed(
|
||||
key: "theModel." + nameof(Person.PropertyWithDefaultValue));
|
||||
var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
|
||||
|
||||
// Act
|
||||
testableBinder.ProcessResults(bindingContext, results, modelValidationNode);
|
||||
testableBinder.ProcessResults(bindingContext, results);
|
||||
|
||||
// Assert
|
||||
Assert.True(modelStateDictionary.IsValid);
|
||||
|
|
@ -1140,13 +1126,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
.Properties[nameof(ModelWithBindRequiredAndRequiredAttribute.ReferenceTypeProperty)];
|
||||
results[propertyMetadata] = ModelBindingResult.Success(
|
||||
key: "theModel." + nameof(ModelWithBindRequiredAndRequiredAttribute.ReferenceTypeProperty),
|
||||
model: "value",
|
||||
validationNode: null);
|
||||
|
||||
var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
|
||||
|
||||
model: "value");
|
||||
// Act
|
||||
testableBinder.ProcessResults(bindingContext, results, modelValidationNode);
|
||||
testableBinder.ProcessResults(bindingContext, results);
|
||||
|
||||
// Assert
|
||||
Assert.False(modelStateDictionary.IsValid);
|
||||
|
|
@ -1185,19 +1167,15 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
.Properties[nameof(ModelWithBindRequiredAndRequiredAttribute.ValueTypeProperty)];
|
||||
results[propertyMetadata] = ModelBindingResult.Success(
|
||||
key: "theModel." + nameof(ModelWithBindRequiredAndRequiredAttribute.ValueTypeProperty),
|
||||
model: 17,
|
||||
validationNode: null);
|
||||
model: 17);
|
||||
|
||||
// Make ReferenceTypeProperty not have a value.
|
||||
propertyMetadata = containerMetadata
|
||||
.Properties[nameof(ModelWithBindRequiredAndRequiredAttribute.ReferenceTypeProperty)];
|
||||
results[propertyMetadata] = ModelBindingResult.Failed(
|
||||
key: "theModel." + nameof(ModelWithBindRequiredAndRequiredAttribute.ReferenceTypeProperty));
|
||||
|
||||
var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
|
||||
|
||||
// Act
|
||||
testableBinder.ProcessResults(bindingContext, results, modelValidationNode);
|
||||
testableBinder.ProcessResults(bindingContext, results);
|
||||
|
||||
// Assert
|
||||
Assert.False(modelStateDictionary.IsValid);
|
||||
|
|
@ -1235,57 +1213,23 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var firstNameProperty = containerMetadata.Properties[nameof(model.FirstName)];
|
||||
results[firstNameProperty] = ModelBindingResult.Success(
|
||||
nameof(model.FirstName),
|
||||
"John",
|
||||
validationNode: null);
|
||||
"John");
|
||||
|
||||
var lastNameProperty = containerMetadata.Properties[nameof(model.LastName)];
|
||||
results[lastNameProperty] = ModelBindingResult.Success(
|
||||
nameof(model.LastName),
|
||||
"Doe",
|
||||
validationNode: null);
|
||||
|
||||
var modelValidationNode = new ModelValidationNode(string.Empty, containerMetadata, model);
|
||||
"Doe");
|
||||
|
||||
var testableBinder = new TestableMutableObjectModelBinder();
|
||||
|
||||
// Act
|
||||
testableBinder.ProcessResults(bindingContext, results, modelValidationNode);
|
||||
testableBinder.ProcessResults(bindingContext, results);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("John", model.FirstName);
|
||||
Assert.Equal("Doe", model.LastName);
|
||||
Assert.Equal(dob, model.DateOfBirth);
|
||||
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]
|
||||
|
|
@ -1420,8 +1364,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var propertyMetadata = bindingContext.ModelMetadata.Properties[propertyName];
|
||||
var result = ModelBindingResult.Success(
|
||||
propertyName,
|
||||
new Simple { Name = "Hanna" },
|
||||
validationNode: null);
|
||||
new Simple { Name = "Hanna" });
|
||||
|
||||
var testableBinder = new TestableMutableObjectModelBinder();
|
||||
|
||||
|
|
@ -1496,7 +1439,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var modelExplorer = metadataProvider.GetModelExplorerForType(type, model);
|
||||
|
||||
var propertyMetadata = bindingContext.ModelMetadata.Properties[propertyName];
|
||||
var result = ModelBindingResult.Success(propertyName, collection, validationNode: null);
|
||||
var result = ModelBindingResult.Success(propertyName, collection);
|
||||
var testableBinder = new TestableMutableObjectModelBinder();
|
||||
|
||||
// Act
|
||||
|
|
@ -1519,7 +1462,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
|
||||
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();
|
||||
|
||||
// Act
|
||||
|
|
@ -1546,7 +1489,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
|
||||
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();
|
||||
|
||||
// Act
|
||||
|
|
@ -1571,7 +1514,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(Person), model);
|
||||
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();
|
||||
|
||||
// Act
|
||||
|
|
@ -1599,7 +1542,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(ModelWhosePropertySetterThrows), model);
|
||||
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();
|
||||
|
||||
// Act
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http.Internal;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -29,9 +30,10 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
Assert.NotNull(result.Model);
|
||||
Assert.Equal("modelName", result.Key);
|
||||
|
||||
Assert.NotNull(result.ValidationNode);
|
||||
Assert.Equal("modelName", result.ValidationNode.Key);
|
||||
Assert.True(result.ValidationNode.SuppressValidation);
|
||||
var entry = modelBindingContext.ValidationState[result.Model];
|
||||
Assert.True(entry.SuppressValidation);
|
||||
Assert.Null(entry.Key);
|
||||
Assert.Null(entry.Metadata);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -95,6 +97,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
},
|
||||
BinderModelName = modelMetadata.BinderModelName,
|
||||
BindingSource = modelMetadata.BindingSource,
|
||||
ValidationState = new ValidationStateDictionary(),
|
||||
};
|
||||
|
||||
return bindingContext;
|
||||
|
|
|
|||
|
|
@ -103,7 +103,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
|||
// Assert
|
||||
Assert.False(result.IsModelSet);
|
||||
Assert.Null(result.Model);
|
||||
Assert.Null(result.ValidationNode);
|
||||
|
||||
var error = Assert.Single(bindingContext.ModelState["theModelName"].Errors);
|
||||
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)
|
||||
{
|
||||
return new ModelValidationContext(
|
||||
bindingSource: null,
|
||||
modelState: null,
|
||||
validatorProvider: null,
|
||||
modelExplorer: modelExplorer);
|
||||
return new ModelValidationContext()
|
||||
{
|
||||
Container = modelExplorer.Container,
|
||||
Metadata = modelExplorer.Metadata,
|
||||
Model = modelExplorer.Model,
|
||||
};
|
||||
}
|
||||
|
||||
private class DerivedRequiredAttribute : RequiredAttribute
|
||||
|
|
|
|||
|
|
@ -107,13 +107,13 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
// Read-only collection should not be updated.
|
||||
Assert.Empty(boundModel.Address);
|
||||
|
||||
// ModelState (data is valid but is not copied into Address).
|
||||
Assert.True(modelState.IsValid);
|
||||
// ModelState (data is can't be validated).
|
||||
Assert.False(modelState.IsValid);
|
||||
var entry = Assert.Single(modelState);
|
||||
Assert.Equal("Address[0].Street", entry.Key);
|
||||
var state = entry.Value;
|
||||
Assert.NotNull(state);
|
||||
Assert.Equal(ModelValidationState.Valid, state.ValidationState);
|
||||
Assert.Equal(ModelValidationState.Unvalidated, state.ValidationState);
|
||||
Assert.Equal("SomeStreet", state.RawValue);
|
||||
Assert.Equal("SomeStreet", state.AttemptedValue);
|
||||
}
|
||||
|
|
@ -286,13 +286,13 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
// Read-only collection should not be updated.
|
||||
Assert.Empty(boundModel.Address);
|
||||
|
||||
// ModelState (data is valid but is not copied into Address).
|
||||
Assert.True(modelState.IsValid);
|
||||
// ModelState (data cannot be validated).
|
||||
Assert.False(modelState.IsValid);
|
||||
var entry = Assert.Single(modelState);
|
||||
Assert.Equal("prefix.Address[0].Street", entry.Key);
|
||||
var state = entry.Value;
|
||||
Assert.NotNull(state);
|
||||
Assert.Equal(ModelValidationState.Valid, state.ValidationState);
|
||||
Assert.Equal(ModelValidationState.Unvalidated, state.ValidationState);
|
||||
Assert.Equal("SomeStreet", state.AttemptedValue);
|
||||
Assert.Equal("SomeStreet", state.RawValue);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,11 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
|
||||
// ModelState (not set unless inner binder sets it)
|
||||
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]
|
||||
|
|
@ -294,15 +298,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
new string[] { address.Street },
|
||||
address.Street);
|
||||
|
||||
var validationNode = new ModelValidationNode(
|
||||
bindingContext.ModelName,
|
||||
bindingContext.ModelMetadata,
|
||||
address)
|
||||
{
|
||||
ValidateAllProperties = true
|
||||
};
|
||||
|
||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, address, validationNode);
|
||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, address);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -316,11 +312,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
new string[] { model },
|
||||
model);
|
||||
|
||||
var modelValidationNode = new ModelValidationNode(
|
||||
bindingContext.ModelName,
|
||||
bindingContext.ModelMetadata,
|
||||
model);
|
||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model, modelValidationNode);
|
||||
return ModelBindingResult.SuccessAsync(bindingContext.ModelName, model);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -328,7 +320,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
{
|
||||
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()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return _data.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)
|
||||
|
|
@ -650,7 +650,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
|
||||
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()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
return _data.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)
|
||||
|
|
@ -755,7 +755,7 @@ namespace Microsoft.AspNet.Mvc.IntegrationTests
|
|||
|
||||
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.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 Microsoft.AspNet.Http.Internal;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||
using Microsoft.AspNet.Mvc.ModelBinding.Validation;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
||||
|
|
@ -27,8 +28,11 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
|||
Assert.NotEqual(ModelBindingResult.NoResult, result);
|
||||
Assert.True(result.IsModelSet);
|
||||
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]
|
||||
|
|
@ -59,7 +63,8 @@ namespace Microsoft.AspNet.Mvc.WebApiCompatShim
|
|||
{
|
||||
HttpContext = new DefaultHttpContext(),
|
||||
MetadataProvider = metadataProvider,
|
||||
}
|
||||
},
|
||||
ValidationState = new ValidationStateDictionary(),
|
||||
};
|
||||
|
||||
bindingContext.OperationBindingContext.HttpContext.Request.Method = "GET";
|
||||
|
|
|
|||
|
|
@ -75,9 +75,7 @@ namespace ModelBindingWebSite.Controllers
|
|||
OrderStatus model;
|
||||
if (Enum.TryParse<OrderStatus>("Status" + request.Query["status"], out model))
|
||||
{
|
||||
var validationNode =
|
||||
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, model);
|
||||
return ModelBindingResult.SuccessAsync("status", model, validationNode);
|
||||
return ModelBindingResult.SuccessAsync("status", model);
|
||||
}
|
||||
|
||||
return ModelBindingResult.FailedAsync("status");
|
||||
|
|
@ -105,9 +103,7 @@ namespace ModelBindingWebSite.Controllers
|
|||
var value = bindingContext.ValueProvider.GetValue(key);
|
||||
model.ProductId = value.ConvertTo<int>();
|
||||
|
||||
var validationNode =
|
||||
new ModelValidationNode(bindingContext.ModelName, bindingContext.ModelMetadata, value);
|
||||
return ModelBindingResult.SuccessAsync(key, model, validationNode);
|
||||
return ModelBindingResult.SuccessAsync(key, model);
|
||||
}
|
||||
|
||||
return ModelBindingResult.NoResultAsync;
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ namespace ModelBindingWebSite
|
|||
if (!IsSimpleType(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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue