[Partial fix for #1372]Added SimpleTypesExcludeFilter to exlcude validation on simple types and also added tests to cover scenarios.
This commit is contained in:
parent
d788e87a94
commit
00b61ec1e6
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNet.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNet.Mvc.OptionDescriptors;
|
using Microsoft.AspNet.Mvc.OptionDescriptors;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc
|
namespace Microsoft.AspNet.Mvc
|
||||||
|
|
@ -13,33 +14,46 @@ namespace Microsoft.AspNet.Mvc
|
||||||
public static class ValidationExcludeFiltersExtensions
|
public static class ValidationExcludeFiltersExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a descriptor to the specified <paramref name="excludeBodyValidationDescriptorCollection" />
|
/// Adds a descriptor to the specified <paramref name="descriptorCollection" /> that excludes the properties of
|
||||||
/// that excludes the properties of the <see cref="Type"/> specified and it's derived types from validaton.
|
/// the <see cref="Type"/> specified and its derived types from validaton.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="excludeBodyValidationDescriptorCollection">A list of <see cref="ExcludeValidationDescriptor"/>
|
/// <param name="descriptorCollection">A list of <see cref="ExcludeValidationDescriptor"/> which are used to
|
||||||
/// which are used to get a collection of exclude filters to be applied for filtering model properties during validation.
|
/// get a collection of exclude filters to be applied for filtering model properties during validation.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="type"><see cref="Type"/> which should be excluded from validation.</param>
|
/// <param name="type"><see cref="Type"/> which should be excluded from validation.</param>
|
||||||
public static void Add(this IList<ExcludeValidationDescriptor> excludeBodyValidationDescriptorCollection,
|
public static void Add(this IList<ExcludeValidationDescriptor> descriptorCollection, Type type)
|
||||||
Type type)
|
|
||||||
{
|
{
|
||||||
var typeBasedExcludeFilter = new DefaultTypeBasedExcludeFilter(type);
|
var typeBasedExcludeFilter = new DefaultTypeBasedExcludeFilter(type);
|
||||||
excludeBodyValidationDescriptorCollection.Add(new ExcludeValidationDescriptor(typeBasedExcludeFilter));
|
descriptorCollection.Add(new ExcludeValidationDescriptor(typeBasedExcludeFilter));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a descriptor to the specified <paramref name="excludeBodyValidationDescriptorCollection" />
|
/// Adds a descriptor to the specified <paramref name="descriptorCollection" /> that excludes the properties of
|
||||||
/// that excludes the properties of the type specified and it's derived types from validaton.
|
/// the type specified and its derived types from validaton.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="excludeBodyValidationDescriptorCollection">A list of <see cref="ExcludeValidationDescriptor"/>
|
/// <param name="descriptorCollection">A list of <see cref="ExcludeValidationDescriptor"/> which are used to
|
||||||
/// which are used to get a collection of exclude filters to be applied for filtering model properties during validation.
|
/// get a collection of exclude filters to be applied for filtering model properties during validation.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="typeFullName">Full name of the type which should be excluded from validation.</param>
|
/// <param name="typeFullName">Full name of the type which should be excluded from validation.</param>
|
||||||
public static void Add(this IList<ExcludeValidationDescriptor> excludeBodyValidationDescriptorCollection,
|
public static void Add(this IList<ExcludeValidationDescriptor> descriptorCollection, string typeFullName)
|
||||||
string typeFullName)
|
|
||||||
{
|
{
|
||||||
var filter = new DefaultTypeNameBasedExcludeFilter(typeFullName);
|
var filter = new DefaultTypeNameBasedExcludeFilter(typeFullName);
|
||||||
excludeBodyValidationDescriptorCollection.Add(new ExcludeValidationDescriptor(filter));
|
descriptorCollection.Add(new ExcludeValidationDescriptor(filter));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a descriptor to the specified <paramref name="descriptorCollection" /> that excludes the properties of
|
||||||
|
/// the type specified and its derived types from validaton.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="descriptorCollection">A list of <see cref="ExcludeValidationDescriptor"/> which are used to
|
||||||
|
/// get a collection of exclude filters to be applied for filtering model properties during validation.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="filter"><see cref="IExcludeTypeValidationFilter"/> which should be excluded from validation.
|
||||||
|
/// </param>
|
||||||
|
public static void Add(this IList<ExcludeValidationDescriptor> descriptorCollection,
|
||||||
|
IExcludeTypeValidationFilter filter)
|
||||||
|
{
|
||||||
|
descriptorCollection.Add(new ExcludeValidationDescriptor(filter));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,70 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Identifies the simple types that the default model binding validation will exclude.
|
||||||
|
/// </summary>
|
||||||
|
public class SimpleTypesExcludeFilter : IExcludeTypeValidationFilter
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the given type will be excluded from the default model validation.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsTypeExcluded(Type type)
|
||||||
|
{
|
||||||
|
Type[] actualTypes;
|
||||||
|
|
||||||
|
var enumerable = type.ExtractGenericInterface(typeof(IEnumerable<>));
|
||||||
|
if (enumerable == null)
|
||||||
|
{
|
||||||
|
actualTypes = new Type[] { type };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
actualTypes = enumerable.GenericTypeArguments;
|
||||||
|
// The following special case is for IEnumerable<KeyValuePair<K,V>>,
|
||||||
|
// supertype of IDictionary<K,V>, and IReadOnlyDictionary<K,V>.
|
||||||
|
if (actualTypes.Length == 1
|
||||||
|
&& actualTypes[0].IsGenericType()
|
||||||
|
&& actualTypes[0].GetGenericTypeDefinition() == typeof(KeyValuePair<,>))
|
||||||
|
{
|
||||||
|
actualTypes = actualTypes[0].GenericTypeArguments;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var actualType in actualTypes)
|
||||||
|
{
|
||||||
|
var underlyingType = Nullable.GetUnderlyingType(actualType) ?? actualType;
|
||||||
|
|
||||||
|
if (!IsSimpleType(underlyingType))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the given type is the underlying types that <see cref="IsTypeExcluded"/> will exclude.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual bool IsSimpleType(Type type)
|
||||||
|
{
|
||||||
|
var result = type.GetTypeInfo().IsPrimitive ||
|
||||||
|
type.Equals(typeof(decimal)) ||
|
||||||
|
type.Equals(typeof(string)) ||
|
||||||
|
type.Equals(typeof(DateTime)) ||
|
||||||
|
type.Equals(typeof(Guid)) ||
|
||||||
|
type.Equals(typeof(DateTimeOffset)) ||
|
||||||
|
type.Equals(typeof(TimeSpan)) ||
|
||||||
|
type.Equals(typeof(Uri));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -56,9 +56,7 @@ 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 (IsTypeExcludedFromValidation(validationContext.ModelValidationContext.ExcludeFromValidationFilters, modelType))
|
||||||
IsTypeExcludedFromValidation(
|
|
||||||
validationContext.ModelValidationContext.ExcludeFromValidationFilters, modelType))
|
|
||||||
{
|
{
|
||||||
return ShallowValidate(metadata, validationContext, validators);
|
return ShallowValidate(metadata, validationContext, validators);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,9 +58,9 @@ namespace Microsoft.AspNet.Mvc
|
||||||
options.ModelValidatorProviders.Add(new DataMemberModelValidatorProvider());
|
options.ModelValidatorProviders.Add(new DataMemberModelValidatorProvider());
|
||||||
|
|
||||||
// Add types to be excluded from Validation
|
// Add types to be excluded from Validation
|
||||||
|
options.ValidationExcludeFilters.Add(new SimpleTypesExcludeFilter());
|
||||||
options.ValidationExcludeFilters.Add(typeof(XObject));
|
options.ValidationExcludeFilters.Add(typeof(XObject));
|
||||||
options.ValidationExcludeFilters.Add(typeof(Type));
|
options.ValidationExcludeFilters.Add(typeof(Type));
|
||||||
options.ValidationExcludeFilters.Add(typeof(byte[]));
|
|
||||||
options.ValidationExcludeFilters.Add(typeof(JToken));
|
options.ValidationExcludeFilters.Add(typeof(JToken));
|
||||||
options.ValidationExcludeFilters.Add(typeFullName: "System.Xml.XmlNode");
|
options.ValidationExcludeFilters.Add(typeFullName: "System.Xml.XmlNode");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,15 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Builder;
|
using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.TestHost;
|
using Microsoft.AspNet.TestHost;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Mvc.FunctionalTests
|
namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
|
|
@ -17,6 +20,28 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
private readonly IServiceProvider _services = TestHelper.CreateServices("FormatterWebSite");
|
private readonly IServiceProvider _services = TestHelper.CreateServices("FormatterWebSite");
|
||||||
private readonly Action<IApplicationBuilder> _app = new FormatterWebSite.Startup().Configure;
|
private readonly Action<IApplicationBuilder> _app = new FormatterWebSite.Startup().Configure;
|
||||||
|
|
||||||
|
// Parameters: Request Content, Expected status code, Expected model state error message
|
||||||
|
public static IEnumerable<object[]> SimpleTypePropertiesModelRequestData
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
yield return new object[] {
|
||||||
|
"{\"ByteProperty\":1, \"NullableByteProperty\":5, \"ByteArrayProperty\":[1,2,3]}",
|
||||||
|
400,
|
||||||
|
"The field ByteProperty must be between 2 and 8."};
|
||||||
|
|
||||||
|
yield return new object[] {
|
||||||
|
"{\"ByteProperty\":8, \"NullableByteProperty\":1, \"ByteArrayProperty\":[1,2,3]}",
|
||||||
|
400,
|
||||||
|
"The field NullableByteProperty must be between 2 and 8."};
|
||||||
|
|
||||||
|
yield return new object[] {
|
||||||
|
"{\"ByteProperty\":8, \"NullableByteProperty\":2, \"ByteArrayProperty\":[1]}",
|
||||||
|
400,
|
||||||
|
"The field ByteArrayProperty must be a string or array type with a minimum length of '2'."};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CheckIfObjectIsDeserializedWithoutErrors()
|
public async Task CheckIfObjectIsDeserializedWithoutErrors()
|
||||||
{
|
{
|
||||||
|
|
@ -84,9 +109,64 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
Assert.Equal("No model validation for developer, even though developer.Name is empty.", await response.Content.ReadAsStringAsync());
|
Assert.Equal("No model validation for developer, even though developer.Name is empty.",
|
||||||
|
await response.Content.ReadAsStringAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ShallowValidation_HappensOnExcluded_ComplexTypeProperties()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
var requestData = "{\"Name\":\"Library Manager\", \"Suppliers\": [{\"Name\":\"Contoso Corp\"}]}";
|
||||||
|
var content = new StringContent(requestData, Encoding.UTF8, "application/json");
|
||||||
|
var expectedModelStateErrorMessage
|
||||||
|
= "The field Suppliers must be a string or array type with a minimum length of '2'.";
|
||||||
|
var shouldNotContainMessage
|
||||||
|
= "The field Name must be a string or array type with a maximum length of '5'.";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.PostAsync("http://localhost/Validation/CreateProject", content);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
Assert.Equal(400, (int)response.StatusCode);
|
||||||
|
|
||||||
|
var responseContent = await response.Content.ReadAsStringAsync();
|
||||||
|
var responseObject = JsonConvert.DeserializeObject<Dictionary<string, ErrorCollection>>(responseContent);
|
||||||
|
var errorCollection = Assert.Single(responseObject);
|
||||||
|
var error = Assert.Single(errorCollection.Value.Errors);
|
||||||
|
Assert.Equal(expectedModelStateErrorMessage, error.ErrorMessage);
|
||||||
|
|
||||||
|
// verifies that the excluded type is not validated
|
||||||
|
Assert.NotEqual(shouldNotContainMessage, error.ErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(SimpleTypePropertiesModelRequestData))]
|
||||||
|
public async Task ShallowValidation_HappensOnExlcuded_SimpleTypeProperties(
|
||||||
|
string requestContent,
|
||||||
|
int expectedStatusCode,
|
||||||
|
string expectedModelStateErrorMessage)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var server = TestServer.Create(_services, _app);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
var content = new StringContent(requestContent, Encoding.UTF8, "application/json");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response = await client.PostAsync("http://localhost/Validation/CreateSimpleTypePropertiesModel",
|
||||||
|
content);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
Assert.Equal(expectedStatusCode, (int)response.StatusCode);
|
||||||
|
|
||||||
|
var responseContent = await response.Content.ReadAsStringAsync();
|
||||||
|
var responseObject = JsonConvert.DeserializeObject<Dictionary<string, ErrorCollection>>(responseContent);
|
||||||
|
var errorCollection = Assert.Single(responseObject);
|
||||||
|
var error = Assert.Single(errorCollection.Value.Errors);
|
||||||
|
Assert.Equal(expectedModelStateErrorMessage, error.ErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CheckIfExcludedField_IsValidatedForNonBodyBoundModels()
|
public async Task CheckIfExcludedField_IsValidatedForNonBodyBoundModels()
|
||||||
|
|
@ -103,5 +183,23 @@ namespace Microsoft.AspNet.Mvc.FunctionalTests
|
||||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||||
Assert.Equal("The Name field is required.", await response.Content.ReadAsStringAsync());
|
Assert.Equal("The Name field is required.", await response.Content.ReadAsStringAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class ErrorCollection
|
||||||
|
{
|
||||||
|
public IEnumerable<Error> Errors
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Error
|
||||||
|
{
|
||||||
|
public string ErrorMessage
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
// 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 Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Mvc.ModelBinding.Test
|
||||||
|
{
|
||||||
|
public class SimpleTypeExcluceFilterTest
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(ExcludedTypes))]
|
||||||
|
public void SimpleTypeExcluceFilter_ExcludedTypes(Type type)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var filter = new SimpleTypesExcludeFilter();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.True(filter.IsTypeExcluded(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(IncludedTypes))]
|
||||||
|
public void SimpleTypeExcluceFilter_IncludedTypes(Type type)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var filter = new SimpleTypesExcludeFilter();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
Assert.False(filter.IsTypeExcluded(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestType
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TheoryData<Type> ExcludedTypes
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new TheoryData<Type>()
|
||||||
|
{
|
||||||
|
// Simple types
|
||||||
|
typeof(int[]),
|
||||||
|
typeof(int),
|
||||||
|
typeof(List<decimal>),
|
||||||
|
typeof(SortedSet<int>),
|
||||||
|
|
||||||
|
// Nullable types
|
||||||
|
typeof(ICollection<string>),
|
||||||
|
typeof(int?[]),
|
||||||
|
typeof(SortedSet<int?>),
|
||||||
|
typeof(HashSet<Uri>),
|
||||||
|
typeof(HashSet<string>),
|
||||||
|
|
||||||
|
// Value types
|
||||||
|
typeof(IList<DateTime>),
|
||||||
|
|
||||||
|
// KeyValue types
|
||||||
|
typeof(Dictionary<int, string>),
|
||||||
|
typeof(IReadOnlyDictionary<int?, char>)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TheoryData<Type> IncludedTypes
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new TheoryData<Type>()
|
||||||
|
{
|
||||||
|
typeof(TestType),
|
||||||
|
typeof(TestType[]),
|
||||||
|
typeof(SortedSet<TestType>),
|
||||||
|
typeof(Dictionary<int, TestType>),
|
||||||
|
typeof(Dictionary<TestType, int>),
|
||||||
|
typeof(Dictionary<TestType, TestType>)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -133,21 +133,26 @@ namespace Microsoft.AspNet.Mvc
|
||||||
Assert.Equal(5, mvcOptions.ValidationExcludeFilters.Count);
|
Assert.Equal(5, mvcOptions.ValidationExcludeFilters.Count);
|
||||||
|
|
||||||
// Verify if the delegates registered by default exclude the given types.
|
// Verify if the delegates registered by default exclude the given types.
|
||||||
Assert.Equal(typeof(DefaultTypeBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[0].OptionType);
|
Assert.Equal(typeof(SimpleTypesExcludeFilter), mvcOptions.ValidationExcludeFilters[0].OptionType);
|
||||||
var instance = Assert.IsType<DefaultTypeBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[0].Instance);
|
|
||||||
Assert.Equal(instance.ExcludedType, typeof(XObject));
|
|
||||||
Assert.Equal(typeof(DefaultTypeBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[1].OptionType);
|
Assert.Equal(typeof(DefaultTypeBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[1].OptionType);
|
||||||
var instance2 = Assert.IsType<DefaultTypeBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[1].Instance);
|
var xObjectFilter
|
||||||
Assert.Equal(instance2.ExcludedType, typeof(Type));
|
= Assert.IsType<DefaultTypeBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[1].Instance);
|
||||||
|
Assert.Equal(xObjectFilter.ExcludedType, typeof(XObject));
|
||||||
|
|
||||||
Assert.Equal(typeof(DefaultTypeBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[2].OptionType);
|
Assert.Equal(typeof(DefaultTypeBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[2].OptionType);
|
||||||
var instance3 = Assert.IsType<DefaultTypeBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[2].Instance);
|
var typeFilter =
|
||||||
Assert.Equal(instance3.ExcludedType, typeof(byte[]));
|
Assert.IsType<DefaultTypeBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[2].Instance);
|
||||||
|
Assert.Equal(typeFilter.ExcludedType, typeof(Type));
|
||||||
|
|
||||||
Assert.Equal(typeof(DefaultTypeBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[3].OptionType);
|
Assert.Equal(typeof(DefaultTypeBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[3].OptionType);
|
||||||
var instance4 = Assert.IsType<DefaultTypeBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[3].Instance);
|
var jTokenFilter
|
||||||
Assert.Equal(instance4.ExcludedType, typeof(JToken));
|
= Assert.IsType<DefaultTypeBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[3].Instance);
|
||||||
|
Assert.Equal(jTokenFilter.ExcludedType, typeof(JToken));
|
||||||
|
|
||||||
Assert.Equal(typeof(DefaultTypeNameBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[4].OptionType);
|
Assert.Equal(typeof(DefaultTypeNameBasedExcludeFilter), mvcOptions.ValidationExcludeFilters[4].OptionType);
|
||||||
var instance5 = Assert.IsType<DefaultTypeNameBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[4].Instance);
|
var xmlNodeFilter =
|
||||||
Assert.Equal(instance5.ExcludedTypeName, "System.Xml.XmlNode");
|
Assert.IsType<DefaultTypeNameBasedExcludeFilter>(mvcOptions.ValidationExcludeFilters[4].Instance);
|
||||||
|
Assert.Equal(xmlNodeFilter.ExcludedTypeName, "System.Xml.XmlNode");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace FormatterWebSite
|
||||||
|
{
|
||||||
|
public class CustomObjectResult : ObjectResult
|
||||||
|
{
|
||||||
|
public CustomObjectResult(object value, int statusCode) : base(value)
|
||||||
|
{
|
||||||
|
StatusCode = statusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int StatusCode { get; private set; }
|
||||||
|
|
||||||
|
public override Task ExecuteResultAsync(ActionContext context)
|
||||||
|
{
|
||||||
|
context.HttpContext.Response.StatusCode = StatusCode;
|
||||||
|
|
||||||
|
return base.ExecuteResultAsync(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Mvc;
|
using Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
namespace FormatterWebSite
|
namespace FormatterWebSite
|
||||||
|
|
@ -54,5 +56,19 @@ namespace FormatterWebSite
|
||||||
return ModelState["Name"].Errors[0].ErrorMessage;
|
return ModelState["Name"].Errors[0].ErrorMessage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 'Developer' type is excluded but the shallow validation on the
|
||||||
|
// property Developers should happen
|
||||||
|
[ModelStateValidationFilter]
|
||||||
|
public IActionResult CreateProject([FromBody]Project project)
|
||||||
|
{
|
||||||
|
return Json(project);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ModelStateValidationFilter]
|
||||||
|
public IActionResult CreateSimpleTypePropertiesModel([FromBody] SimpleTypePropertiesModel simpleTypePropertiesModel)
|
||||||
|
{
|
||||||
|
return Json(simpleTypePropertiesModel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
// 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 Microsoft.AspNet.Mvc;
|
||||||
|
|
||||||
|
namespace FormatterWebSite
|
||||||
|
{
|
||||||
|
public class ModelStateValidationFilterAttribute : ActionFilterAttribute
|
||||||
|
{
|
||||||
|
public override void OnActionExecuting(ActionExecutingContext context)
|
||||||
|
{
|
||||||
|
if (!context.ModelState.IsValid)
|
||||||
|
{
|
||||||
|
context.Result = new CustomObjectResult(context.ModelState, 400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace FormatterWebSite
|
||||||
|
{
|
||||||
|
public class Project
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[MinLength(2)]
|
||||||
|
[MaxLength(5)]
|
||||||
|
public Supplier[] Suppliers { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Supplier
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(5)]
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace FormatterWebSite
|
||||||
|
{
|
||||||
|
public class SimpleTypePropertiesModel
|
||||||
|
{
|
||||||
|
[Range(2, 8)]
|
||||||
|
public byte ByteProperty { get; set; }
|
||||||
|
|
||||||
|
[Range(2, 8)]
|
||||||
|
public byte? NullableByteProperty { get; set; }
|
||||||
|
|
||||||
|
[MinLength(2)]
|
||||||
|
public byte[] ByteArrayProperty { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,7 @@ namespace FormatterWebSite
|
||||||
services.Configure<MvcOptions>(options =>
|
services.Configure<MvcOptions>(options =>
|
||||||
{
|
{
|
||||||
options.ValidationExcludeFilters.Add(typeof(Developer));
|
options.ValidationExcludeFilters.Add(typeof(Developer));
|
||||||
|
options.ValidationExcludeFilters.Add(typeof(Supplier));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue