Issue #1206 - DefaultBodyModelValidator throws if get accessor throws.
Fix: The MvcOptions takes in a list of ExcludeFromValidationDelegate (Func<Type,bool>). This func verifies if the type is excluded in validation or not.
This commit is contained in:
parent
34f17db94c
commit
53379e4395
|
|
@ -8,6 +8,7 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Mvc.Core;
|
using Microsoft.AspNet.Mvc.Core;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.OptionsModel;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
|
|
@ -20,14 +21,17 @@ namespace Microsoft.AspNet.Mvc
|
||||||
private readonly ActionContext _actionContext;
|
private readonly ActionContext _actionContext;
|
||||||
private readonly IInputFormatterSelector _formatterSelector;
|
private readonly IInputFormatterSelector _formatterSelector;
|
||||||
private readonly IBodyModelValidator _bodyModelValidator;
|
private readonly IBodyModelValidator _bodyModelValidator;
|
||||||
|
private readonly IOptions<MvcOptions> _mvcOptions;
|
||||||
|
|
||||||
public BodyModelBinder([NotNull] IContextAccessor<ActionContext> context,
|
public BodyModelBinder([NotNull] IContextAccessor<ActionContext> context,
|
||||||
[NotNull] IInputFormatterSelector selector,
|
[NotNull] IInputFormatterSelector selector,
|
||||||
[NotNull] IBodyModelValidator bodyModelValidator)
|
[NotNull] IBodyModelValidator bodyModelValidator,
|
||||||
|
[NotNull] IOptions<MvcOptions> mvcOptions)
|
||||||
{
|
{
|
||||||
_actionContext = context.Value;
|
_actionContext = context.Value;
|
||||||
_formatterSelector = selector;
|
_formatterSelector = selector;
|
||||||
_bodyModelValidator = bodyModelValidator;
|
_bodyModelValidator = bodyModelValidator;
|
||||||
|
_mvcOptions = mvcOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task<bool> BindAsync(ModelBindingContext bindingContext, IBodyBinderMarker marker)
|
protected override async Task<bool> BindAsync(ModelBindingContext bindingContext, IBodyBinderMarker marker)
|
||||||
|
|
@ -48,7 +52,13 @@ namespace Microsoft.AspNet.Mvc
|
||||||
bindingContext.Model = await formatter.ReadAsync(formatterContext);
|
bindingContext.Model = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Validate the deserialized object
|
// Validate the deserialized object
|
||||||
var validationContext = new ModelValidationContext(bindingContext, bindingContext.ModelMetadata);
|
var validationContext = new ModelValidationContext(
|
||||||
|
bindingContext.MetadataProvider,
|
||||||
|
bindingContext.ValidatorProvider,
|
||||||
|
bindingContext.ModelState,
|
||||||
|
bindingContext.ModelMetadata,
|
||||||
|
containerMetadata: null,
|
||||||
|
excludeFromValidationDelegate: _mvcOptions.Options.ExcludeFromValidationDelegates);
|
||||||
_bodyModelValidator.Validate(validationContext, bindingContext.ModelName);
|
_bodyModelValidator.Validate(validationContext, bindingContext.ModelName);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,10 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNet.Mvc.Core;
|
|
||||||
using Microsoft.AspNet.Mvc.OptionDescriptors;
|
|
||||||
using Microsoft.AspNet.Mvc.ApplicationModel;
|
using Microsoft.AspNet.Mvc.ApplicationModel;
|
||||||
|
using Microsoft.AspNet.Mvc.Core;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
using Microsoft.AspNet.Mvc.OptionDescriptors;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
|
|
@ -69,6 +70,13 @@ namespace Microsoft.AspNet.Mvc
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<InputFormatterDescriptor> InputFormatters { get; private set; }
|
public List<InputFormatterDescriptor> InputFormatters { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a list of <see cref="ExcludedValidationTypesPredicates"/> which return whether the given type
|
||||||
|
/// should be excluded from Validation in <see cref="IBodyModelValidator"/>
|
||||||
|
/// </summary>
|
||||||
|
public List<ExcludeFromValidationDelegate> ExcludeFromValidationDelegates { get; }
|
||||||
|
= new List<ExcludeFromValidationDelegate>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the maximum number of validation errors that are allowed by this application before further
|
/// Gets or sets the maximum number of validation errors that are allowed by this application before further
|
||||||
/// errors are ignored.
|
/// errors are ignored.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Extensions for <see cref="MvcOptions.ExcludedValidationTypesPredicates"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static class ExcludeFromValidationDelegateExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a delegate to the specified <paramref name="list" /> that excludes the properties of the specified type
|
||||||
|
/// and and it's derived types from validation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="list"><see cref="IList{T}"/> of <see cref="ExcludeFromValidationDelegate"/>.</param>
|
||||||
|
/// <param name="type"><see cref="Type"/> which should be excluded from validation.</param>
|
||||||
|
public static void Add(this IList<ExcludeFromValidationDelegate> list, Type type)
|
||||||
|
{
|
||||||
|
list.Add(t => t.IsAssignableFrom(type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,7 +17,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
public class DefaultBodyModelValidator : IBodyModelValidator
|
public class DefaultBodyModelValidator : IBodyModelValidator
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool Validate([NotNull] ModelValidationContext modelValidationContext, string keyPrefix)
|
public bool Validate(
|
||||||
|
[NotNull] ModelValidationContext modelValidationContext,
|
||||||
|
string keyPrefix)
|
||||||
{
|
{
|
||||||
var metadata = modelValidationContext.ModelMetadata;
|
var metadata = modelValidationContext.ModelMetadata;
|
||||||
var validationContext = new ValidationContext()
|
var validationContext = new ValidationContext()
|
||||||
|
|
@ -54,7 +56,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
||||||
// We don't need to recursively traverse the graph for types that shouldn't be validated
|
// We don't need to recursively traverse the graph for types that shouldn't be validated
|
||||||
var modelType = metadata.Model.GetType();
|
var modelType = metadata.Model.GetType();
|
||||||
if (TypeHelper.IsSimpleType(modelType))
|
if (TypeHelper.IsSimpleType(modelType) ||
|
||||||
|
IsTypeExcludedFromValidation(
|
||||||
|
validationContext.ModelValidationContext.ExcludeFromValidationDelegate, modelType))
|
||||||
{
|
{
|
||||||
return ShallowValidate(metadata, validationContext, validators);
|
return ShallowValidate(metadata, validationContext, validators);
|
||||||
}
|
}
|
||||||
|
|
@ -192,6 +196,18 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
return isValid;
|
return isValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool IsTypeExcludedFromValidation(
|
||||||
|
IReadOnlyList<ExcludeFromValidationDelegate> predicates, Type type)
|
||||||
|
{
|
||||||
|
// This can be set to null in ModelBinding scenarios which does not flow through this path.
|
||||||
|
if (predicates == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return predicates.Any(t => t(type));
|
||||||
|
}
|
||||||
|
|
||||||
private static Type GetElementType(Type type)
|
private static Type GetElementType(Type type)
|
||||||
{
|
{
|
||||||
Contract.Assert(typeof(IEnumerable).IsAssignableFrom(type));
|
Contract.Assert(typeof(IEnumerable).IsAssignableFrom(type));
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Delegate that determines if the specified type is excluded from validation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type"><see cref="Type"/> which needs to be checked.</param>
|
||||||
|
/// <returns><see cref="true"/> if excluded, <see cref="false"/> otherwise.</returns>
|
||||||
|
public delegate bool ExcludeFromValidationDelegate(Type type);
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
public class ModelValidationContext
|
public class ModelValidationContext
|
||||||
|
|
@ -20,12 +22,28 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
[NotNull] ModelStateDictionary modelState,
|
[NotNull] ModelStateDictionary modelState,
|
||||||
[NotNull] ModelMetadata metadata,
|
[NotNull] ModelMetadata metadata,
|
||||||
ModelMetadata containerMetadata)
|
ModelMetadata containerMetadata)
|
||||||
|
: this(metadataProvider,
|
||||||
|
validatorProvider,
|
||||||
|
modelState,
|
||||||
|
metadata,
|
||||||
|
containerMetadata,
|
||||||
|
excludeFromValidationDelegate: null)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public ModelValidationContext([NotNull] IModelMetadataProvider metadataProvider,
|
||||||
|
[NotNull] IModelValidatorProvider validatorProvider,
|
||||||
|
[NotNull] ModelStateDictionary modelState,
|
||||||
|
[NotNull] ModelMetadata metadata,
|
||||||
|
ModelMetadata containerMetadata,
|
||||||
|
IReadOnlyList<ExcludeFromValidationDelegate> excludeFromValidationDelegate)
|
||||||
{
|
{
|
||||||
ModelMetadata = metadata;
|
ModelMetadata = metadata;
|
||||||
ModelState = modelState;
|
ModelState = modelState;
|
||||||
MetadataProvider = metadataProvider;
|
MetadataProvider = metadataProvider;
|
||||||
ValidatorProvider = validatorProvider;
|
ValidatorProvider = validatorProvider;
|
||||||
ContainerMetadata = containerMetadata;
|
ContainerMetadata = containerMetadata;
|
||||||
|
ExcludeFromValidationDelegate = excludeFromValidationDelegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ModelValidationContext([NotNull] ModelValidationContext parentContext,
|
public ModelValidationContext([NotNull] ModelValidationContext parentContext,
|
||||||
|
|
@ -36,6 +54,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
ModelState = parentContext.ModelState;
|
ModelState = parentContext.ModelState;
|
||||||
MetadataProvider = parentContext.MetadataProvider;
|
MetadataProvider = parentContext.MetadataProvider;
|
||||||
ValidatorProvider = parentContext.ValidatorProvider;
|
ValidatorProvider = parentContext.ValidatorProvider;
|
||||||
|
ExcludeFromValidationDelegate = parentContext.ExcludeFromValidationDelegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ModelMetadata ModelMetadata { get; private set; }
|
public ModelMetadata ModelMetadata { get; private set; }
|
||||||
|
|
@ -47,5 +66,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
public IModelMetadataProvider MetadataProvider { get; private set; }
|
public IModelMetadataProvider MetadataProvider { get; private set; }
|
||||||
|
|
||||||
public IModelValidatorProvider ValidatorProvider { get; private set; }
|
public IModelValidatorProvider ValidatorProvider { get; private set; }
|
||||||
|
|
||||||
|
public IReadOnlyList<ExcludeFromValidationDelegate> ExcludeFromValidationDelegate { get; private set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
"Microsoft.AspNet.Mvc.Common": { "version": "6.0.0-*", "type": "build" },
|
"Microsoft.AspNet.Mvc.Common": { "version": "6.0.0-*", "type": "build" },
|
||||||
"Microsoft.DataAnnotations": "1.0.0-*",
|
"Microsoft.DataAnnotations": "1.0.0-*",
|
||||||
"Microsoft.Framework.DependencyInjection": "1.0.0-*",
|
"Microsoft.Framework.DependencyInjection": "1.0.0-*",
|
||||||
"Microsoft.AspNet.Mvc.HeaderValueAbstractions": "1.0.0-*",
|
"Microsoft.AspNet.Mvc.HeaderValueAbstractions": "1.0.0-*",
|
||||||
"Newtonsoft.Json": "6.0.4"
|
"Newtonsoft.Json": "6.0.4"
|
||||||
},
|
},
|
||||||
"frameworks": {
|
"frameworks": {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using Microsoft.AspNet.Mvc;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNet.Mvc.WebApiCompatShim;
|
using Microsoft.AspNet.Mvc.WebApiCompatShim;
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.OptionsModel;
|
||||||
|
|
||||||
namespace System.Web.Http
|
namespace System.Web.Http
|
||||||
{
|
{
|
||||||
|
|
@ -107,15 +108,18 @@ namespace System.Web.Http
|
||||||
/// </param>
|
/// </param>
|
||||||
public void Validate<TEntity>(TEntity entity, string keyPrefix)
|
public void Validate<TEntity>(TEntity entity, string keyPrefix)
|
||||||
{
|
{
|
||||||
|
var mvcOptions = Context.RequestServices.GetRequiredService<IOptions<MvcOptions>>();
|
||||||
var validator = Context.RequestServices.GetRequiredService<IBodyModelValidator>();
|
var validator = Context.RequestServices.GetRequiredService<IBodyModelValidator>();
|
||||||
var metadataProvider = Context.RequestServices.GetRequiredService<IModelMetadataProvider>();
|
var metadataProvider = Context.RequestServices.GetRequiredService<IModelMetadataProvider>();
|
||||||
var modelMetadata = metadataProvider.GetMetadataForType(() => entity, typeof(TEntity));
|
var modelMetadata = metadataProvider.GetMetadataForType(() => entity, typeof(TEntity));
|
||||||
var validatorProvider = Context.RequestServices.GetRequiredService<ICompositeModelValidatorProvider>();
|
var validatorProvider = Context.RequestServices.GetRequiredService<ICompositeModelValidatorProvider>();
|
||||||
var modelValidationContext = new ModelValidationContext(metadataProvider,
|
var modelValidationContext = new ModelValidationContext(
|
||||||
validatorProvider,
|
metadataProvider,
|
||||||
ModelState,
|
validatorProvider,
|
||||||
modelMetadata,
|
ModelState,
|
||||||
containerMetadata: null);
|
modelMetadata,
|
||||||
|
containerMetadata: null,
|
||||||
|
excludeFromValidationDelegate: mvcOptions.Options.ExcludeFromValidationDelegates);
|
||||||
validator.Validate(modelValidationContext, keyPrefix);
|
validator.Validate(modelValidationContext, keyPrefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Xml;
|
||||||
|
using System.Xml.Linq;
|
||||||
using Microsoft.AspNet.Mvc.ModelBinding;
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNet.Mvc.Razor;
|
using Microsoft.AspNet.Mvc.Razor;
|
||||||
using Microsoft.Framework.OptionsModel;
|
using Microsoft.Framework.OptionsModel;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc
|
||||||
{
|
{
|
||||||
|
|
@ -52,6 +56,13 @@ namespace Microsoft.AspNet.Mvc
|
||||||
// Set up validators
|
// Set up validators
|
||||||
options.ModelValidatorProviders.Add(new DataAnnotationsModelValidatorProvider());
|
options.ModelValidatorProviders.Add(new DataAnnotationsModelValidatorProvider());
|
||||||
options.ModelValidatorProviders.Add(new DataMemberModelValidatorProvider());
|
options.ModelValidatorProviders.Add(new DataMemberModelValidatorProvider());
|
||||||
|
|
||||||
|
// Add types to be excluded from Validation
|
||||||
|
options.ExcludeFromValidationDelegates.Add(typeof(XmlNode));
|
||||||
|
options.ExcludeFromValidationDelegates.Add(typeof(XObject));
|
||||||
|
options.ExcludeFromValidationDelegates.Add(typeof(Type));
|
||||||
|
options.ExcludeFromValidationDelegates.Add(typeof(byte[]));
|
||||||
|
options.ExcludeFromValidationDelegates.Add(typeof(JToken));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -25,7 +25,8 @@
|
||||||
"System.Linq": "4.0.0-beta-*",
|
"System.Linq": "4.0.0-beta-*",
|
||||||
"System.Reflection": "4.0.10-beta-*",
|
"System.Reflection": "4.0.10-beta-*",
|
||||||
"System.Runtime": "4.0.20-beta-*",
|
"System.Runtime": "4.0.20-beta-*",
|
||||||
"System.Runtime.Extensions": "4.0.10-beta-*"
|
"System.Runtime.Extensions": "4.0.10-beta-*",
|
||||||
|
"System.Xml.XmlDocument": "4.0.0-beta-*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNet.PipelineCore;
|
using Microsoft.AspNet.PipelineCore;
|
||||||
using Microsoft.AspNet.Routing;
|
using Microsoft.AspNet.Routing;
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.OptionsModel;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
|
@ -31,8 +32,8 @@ namespace Microsoft.AspNet.Mvc
|
||||||
|
|
||||||
var bindingContext = GetBindingContext(typeof(Person), inputFormatter: mockInputFormatter.Object);
|
var bindingContext = GetBindingContext(typeof(Person), inputFormatter: mockInputFormatter.Object);
|
||||||
bindingContext.ModelMetadata.Marker = Mock.Of<IBodyBinderMarker>();
|
bindingContext.ModelMetadata.Marker = Mock.Of<IBodyBinderMarker>();
|
||||||
|
|
||||||
var binder = GetBodyBinder(mockInputFormatter.Object, mockValidator.Object);
|
var binder = GetBodyBinder(mockInputFormatter.Object, mockValidator.Object, null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var binderResult = await binder.BindModelAsync(bindingContext);
|
var binderResult = await binder.BindModelAsync(bindingContext);
|
||||||
|
|
@ -87,7 +88,7 @@ namespace Microsoft.AspNet.Mvc
|
||||||
ModelMetadata = metadataProvider.GetMetadataForType(null, modelType),
|
ModelMetadata = metadataProvider.GetMetadataForType(null, modelType),
|
||||||
ModelName = "someName",
|
ModelName = "someName",
|
||||||
ValueProvider = Mock.Of<IValueProvider>(),
|
ValueProvider = Mock.Of<IValueProvider>(),
|
||||||
ModelBinder = GetBodyBinder(inputFormatter, null),
|
ModelBinder = GetBodyBinder(inputFormatter, null, null),
|
||||||
MetadataProvider = metadataProvider,
|
MetadataProvider = metadataProvider,
|
||||||
HttpContext = new DefaultHttpContext(),
|
HttpContext = new DefaultHttpContext(),
|
||||||
ModelState = new ModelStateDictionary()
|
ModelState = new ModelStateDictionary()
|
||||||
|
|
@ -96,7 +97,8 @@ namespace Microsoft.AspNet.Mvc
|
||||||
return bindingContext;
|
return bindingContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BodyModelBinder GetBodyBinder(IInputFormatter inputFormatter, IBodyModelValidator validator)
|
private static BodyModelBinder GetBodyBinder(
|
||||||
|
IInputFormatter inputFormatter, IBodyModelValidator validator, IOptions<MvcOptions> mvcOptions)
|
||||||
{
|
{
|
||||||
var actionContext = CreateActionContext(new DefaultHttpContext());
|
var actionContext = CreateActionContext(new DefaultHttpContext());
|
||||||
var inputFormatterSelector = new Mock<IInputFormatterSelector>();
|
var inputFormatterSelector = new Mock<IInputFormatterSelector>();
|
||||||
|
|
@ -111,9 +113,19 @@ namespace Microsoft.AspNet.Mvc
|
||||||
validator = mockValidator.Object;
|
validator = mockValidator.Object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mvcOptions == null)
|
||||||
|
{
|
||||||
|
var options = new Mock<MvcOptions>();
|
||||||
|
options.CallBase = true;
|
||||||
|
var mockMvcOptions = new Mock<IOptions<MvcOptions>>();
|
||||||
|
mockMvcOptions.SetupGet(o => o.Options).Returns(options.Object);
|
||||||
|
mvcOptions = mockMvcOptions.Object;
|
||||||
|
}
|
||||||
|
|
||||||
var binder = new BodyModelBinder(actionContext,
|
var binder = new BodyModelBinder(actionContext,
|
||||||
inputFormatterSelector.Object,
|
inputFormatterSelector.Object,
|
||||||
validator);
|
validator,
|
||||||
|
mvcOptions);
|
||||||
return binder;
|
return binder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -70,5 +70,23 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
"The field Designation must match the regular expression '[0-9a-zA-Z]*'.",
|
"The field Designation must match the regular expression '[0-9a-zA-Z]*'.",
|
||||||
await response.Content.ReadAsStringAsync());
|
await response.Content.ReadAsStringAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CheckIfExcludedFieldsAreNotValidated()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
var sampleString = "RandomString";
|
||||||
|
var input = "{ NameThatThrowsOnGet:'" + sampleString + "'}";
|
||||||
|
var content = new StringContent(input, Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.PostAsync("http://localhost/Validation/GetDeveloperName", content);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
|
Assert.Equal("Developer's get was not accessed after set.", await response.Content.ReadAsStringAsync());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -9,7 +9,6 @@ using System.Linq;
|
||||||
using Microsoft.AspNet.Testing;
|
using Microsoft.AspNet.Testing;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Microsoft.Framework.OptionsModel;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
|
|
@ -235,8 +234,6 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This case should be handled in a better way.
|
|
||||||
// Issue - https://github.com/aspnet/Mvc/issues/1206 tracks this.
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void BodyValidator_Throws_IfPropertyAccessorThrows()
|
public void BodyValidator_Throws_IfPropertyAccessorThrows()
|
||||||
|
|
@ -253,6 +250,62 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> ObjectsWithPropertiesWhichThrowOnGet
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
yield return new object[] {
|
||||||
|
new Uri("/api/values", UriKind.Relative),
|
||||||
|
typeof(Uri),
|
||||||
|
new List<Type>() { typeof(Uri) }
|
||||||
|
};
|
||||||
|
yield return new object[] {
|
||||||
|
new DerivedUri("/api/values", UriKind.Relative),
|
||||||
|
typeof(Uri),
|
||||||
|
new List<Type>() { typeof(Uri) }
|
||||||
|
};
|
||||||
|
yield return new object[] { new Dictionary<string, Uri> {
|
||||||
|
{ "values", new Uri("/api/values", UriKind.Relative) },
|
||||||
|
{ "hello", new Uri("/api/hello", UriKind.Relative) }
|
||||||
|
}, typeof(Uri), new List<Type>() { typeof(Uri) } };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(ObjectsWithPropertiesWhichThrowOnGet))]
|
||||||
|
[ReplaceCulture]
|
||||||
|
public void BodyValidator_DoesNotThrow_IfExcludedPropertyAccessorsThrow(
|
||||||
|
object input, Type type, List<Type> excludedTypes)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var validationContext = GetModelValidationContext(input, type, excludedTypes);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.DoesNotThrow(
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
new DefaultBodyModelValidator().Validate(validationContext, keyPrefix: string.Empty);
|
||||||
|
});
|
||||||
|
Assert.True(validationContext.ModelState.IsValid);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[ReplaceCulture]
|
||||||
|
public void BodyValidator_Throws_IfPropertyGetterThrows()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var validationContext = GetModelValidationContext(
|
||||||
|
new Uri("/api/values", UriKind.Relative), typeof(Uri), new List<Type>());
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.Throws<InvalidOperationException>(
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
new DefaultBodyModelValidator().Validate(validationContext, keyPrefix: string.Empty);
|
||||||
|
});
|
||||||
|
Assert.True(validationContext.ModelState.IsValid);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
[ReplaceCulture]
|
[ReplaceCulture]
|
||||||
public void MultipleValidationErrorsOnSameMemberReported()
|
public void MultipleValidationErrorsOnSameMemberReported()
|
||||||
|
|
@ -290,7 +343,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
() => new DefaultBodyModelValidator().Validate(validationContext, keyPrefix: string.Empty));
|
() => new DefaultBodyModelValidator().Validate(validationContext, keyPrefix: string.Empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
private ModelValidationContext GetModelValidationContext(object model, Type type)
|
private ModelValidationContext GetModelValidationContext(
|
||||||
|
object model, Type type, List<Type> excludedTypes = null)
|
||||||
{
|
{
|
||||||
var modelStateDictionary = new ModelStateDictionary();
|
var modelStateDictionary = new ModelStateDictionary();
|
||||||
var provider = new Mock<IModelValidatorProviderProvider>();
|
var provider = new Mock<IModelValidatorProviderProvider>();
|
||||||
|
|
@ -301,6 +355,19 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
new DataMemberModelValidatorProvider()
|
new DataMemberModelValidatorProvider()
|
||||||
});
|
});
|
||||||
var modelMetadataProvider = new EmptyModelMetadataProvider();
|
var modelMetadataProvider = new EmptyModelMetadataProvider();
|
||||||
|
List<ExcludeFromValidationDelegate> excludedValidationTypesPredicate =
|
||||||
|
new List<ExcludeFromValidationDelegate>();
|
||||||
|
if (excludedTypes != null)
|
||||||
|
{
|
||||||
|
excludedValidationTypesPredicate = new List<ExcludeFromValidationDelegate>()
|
||||||
|
{
|
||||||
|
(excludedType) =>
|
||||||
|
{
|
||||||
|
return excludedTypes.Any(t => t.IsAssignableFrom(excludedType));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return new ModelValidationContext(
|
return new ModelValidationContext(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
new CompositeModelValidatorProvider(provider.Object),
|
new CompositeModelValidatorProvider(provider.Object),
|
||||||
|
|
@ -311,7 +378,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
modelAccessor: () => model,
|
modelAccessor: () => model,
|
||||||
modelType: type,
|
modelType: type,
|
||||||
propertyName: null),
|
propertyName: null),
|
||||||
containerMetadata: null);
|
containerMetadata: null,
|
||||||
|
excludeFromValidationDelegate: excludedValidationTypesPredicate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Person
|
public class Person
|
||||||
|
|
@ -414,6 +482,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
||||||
public Team Test { get; set; }
|
public Team Test { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class DerivedUri : Uri
|
||||||
|
{
|
||||||
|
public DerivedUri(string uri, UriKind kind) :base(uri, kind)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string UriPurpose { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using Microsoft.AspNet.Mvc;
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
namespace FormatterWebSite
|
namespace FormatterWebSite
|
||||||
|
|
@ -20,5 +21,18 @@ namespace FormatterWebSite
|
||||||
|
|
||||||
return Content("User has been registerd : " + user.Name);
|
return Content("User has been registerd : " + user.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public string GetDeveloperName([FromBody]Developer developer)
|
||||||
|
{
|
||||||
|
if (ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return "Developer's get was not accessed after set.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace FormatterWebSite
|
||||||
|
{
|
||||||
|
public class Developer
|
||||||
|
{
|
||||||
|
private string _name;
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string NameThatThrowsOnGet
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_name == "RandomString")
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _name;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_name = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
using Microsoft.AspNet.Routing;
|
using Microsoft.AspNet.Routing;
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
|
||||||
|
|
@ -18,6 +19,11 @@ namespace FormatterWebSite
|
||||||
{
|
{
|
||||||
// Add MVC services to the services container
|
// Add MVC services to the services container
|
||||||
services.AddMvc(configuration);
|
services.AddMvc(configuration);
|
||||||
|
|
||||||
|
services.Configure<MvcOptions>(options =>
|
||||||
|
{
|
||||||
|
options.ExcludeFromValidationDelegates.Add(typeof(Developer));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add MVC to the request pipeline
|
// Add MVC to the request pipeline
|
||||||
|
|
@ -25,7 +31,6 @@ namespace FormatterWebSite
|
||||||
{
|
{
|
||||||
routes.MapRoute("ActionAsMethod", "{controller}/{action}",
|
routes.MapRoute("ActionAsMethod", "{controller}/{action}",
|
||||||
defaults: new { controller = "Home", action = "Index" });
|
defaults: new { controller = "Home", action = "Index" });
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue