Update the Enum Invalid Value setting for compat

Updated the naming to follow guidelines, and set the default for 2.0
apps to false. Note that I inverted the naming, which means that I had
to invert the logic in a few places.
This commit is contained in:
Ryan Nowak 2017-12-28 19:18:12 -08:00
parent 514381f16f
commit 82e32240a4
8 changed files with 67 additions and 39 deletions

View File

@ -48,6 +48,14 @@ namespace Microsoft.AspNetCore.Mvc
/// Sets the default value of settings on <see cref="MvcOptions"/> to match the behavior of
/// ASP.NET Core MVC 2.1.
/// </summary>
/// <remarks>
/// ASP.NET Core MVC 2.1 introduces compatibility switches for the following:
/// <list type="bullet">
/// <item>
/// <description><see cref="MvcOptions.SuppressBindingUndefinedValueToEnumType"/></description>
/// </item>
/// </list>
/// </remarks>
Version_2_1,
/// <summary>

View File

@ -25,6 +25,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
if (Version >= CompatibilityVersion.Version_2_1)
{
values[nameof(MvcOptions.SuppressBindingUndefinedValueToEnumType)] = true;
values[nameof(MvcOptions.InputFormatterExceptionModelStatePolicy)] = InputFormatterExceptionModelStatePolicy.MalformedInputExceptions;
}

View File

@ -11,9 +11,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
/// </summary>
public class EnumTypeModelBinder : SimpleTypeModelBinder
{
private readonly bool _allowBindingUndefinedValueToEnumType;
private readonly bool _suppressBindingUndefinedValueToEnumType;
public EnumTypeModelBinder(bool allowBindingUndefinedValueToEnumType, Type modelType)
public EnumTypeModelBinder(bool supressBindingUndefinedValueToEnumType, Type modelType)
: base(modelType)
{
if (modelType == null)
@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
throw new ArgumentNullException(nameof(modelType));
}
_allowBindingUndefinedValueToEnumType = allowBindingUndefinedValueToEnumType;
_suppressBindingUndefinedValueToEnumType = supressBindingUndefinedValueToEnumType;
}
protected override void CheckModel(
@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
ValueProviderResult valueProviderResult,
object model)
{
if (model == null || _allowBindingUndefinedValueToEnumType)
if (model == null || !_suppressBindingUndefinedValueToEnumType)
{
base.CheckModel(bindingContext, valueProviderResult, model);
}

View File

@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
if (context.Metadata.IsEnum)
{
return new EnumTypeModelBinder(
_options.AllowBindingUndefinedValueToEnumType,
_options.SuppressBindingUndefinedValueToEnumType,
context.Metadata.UnderlyingOrModelType);
}

View File

@ -22,8 +22,8 @@ namespace Microsoft.AspNetCore.Mvc
private int _maxModelStateErrors = ModelStateDictionary.DefaultMaxAllowedErrors;
// See CompatibilitySwitch.cs for guide on how to implement these.
private readonly CompatibilitySwitch<bool> _allowBindingUndefinedValueToEnumType;
private readonly CompatibilitySwitch<InputFormatterExceptionModelStatePolicy> _inputFormatterExceptionModelStatePolicy;
private readonly CompatibilitySwitch<bool> _suppressBindingUndefinedValueToEnumType;
private readonly CompatibilitySwitch<bool> _suppressJsonDeserializationExceptionMessagesInModelState;
private readonly ICompatibilitySwitch[] _switches;
@ -41,13 +41,13 @@ namespace Microsoft.AspNetCore.Mvc
ModelValidatorProviders = new List<IModelValidatorProvider>();
ValueProviderFactories = new List<IValueProviderFactory>();
_allowBindingUndefinedValueToEnumType = new CompatibilitySwitch<bool>(nameof(AllowBindingUndefinedValueToEnumType));
_inputFormatterExceptionModelStatePolicy = new CompatibilitySwitch<InputFormatterExceptionModelStatePolicy>(nameof(InputFormatterExceptionModelStatePolicy), InputFormatterExceptionModelStatePolicy.AllExceptions);
_suppressBindingUndefinedValueToEnumType = new CompatibilitySwitch<bool>(nameof(SuppressBindingUndefinedValueToEnumType));
_suppressJsonDeserializationExceptionMessagesInModelState = new CompatibilitySwitch<bool>(nameof(SuppressJsonDeserializationExceptionMessagesInModelState));
_switches = new ICompatibilitySwitch[]
{
_allowBindingUndefinedValueToEnumType,
_inputFormatterExceptionModelStatePolicy,
_suppressBindingUndefinedValueToEnumType,
_suppressJsonDeserializationExceptionMessagesInModelState,
};
}
@ -92,6 +92,35 @@ namespace Microsoft.AspNetCore.Mvc
/// </summary>
public FormatterCollection<IInputFormatter> InputFormatters { get; }
/// <summary>
/// Gets or sets an value indicating whether the model binding system will bind undefined values to
/// enum types. The default value of the property is <c>false</c>.
/// </summary>
/// <remarks>
/// <para>
/// This property is associated with a compatibility switch and can provide a different behavior depending on
/// the configured compatibility version for the application. See <see cref="CompatibilityVersion"/> for
/// guidance and examples of setting the application's compatibility version.
/// </para>
/// <para>
/// Configuring the desired of the value compatibility switch by calling this property's setter will take precedence
/// over the value implied by the application's <see cref="CompatibilityVersion"/>.
/// </para>
/// <para>
/// If the application's compatibility version is set to <see cref="CompatibilityVersion.Version_2_0"/> then
/// this setting will have value <c>false</c> if not explicitly configured.
/// </para>
/// <para>
/// If the application's compatibility version is set to <see cref="CompatibilityVersion.Version_2_1"/> or
/// higher then this setting will have value <c>true</c> if not explicitly configured.
/// </para>
/// </remarks>
public bool SuppressBindingUndefinedValueToEnumType
{
get => _suppressBindingUndefinedValueToEnumType.Value;
set => _suppressBindingUndefinedValueToEnumType.Value = value;
}
/// <summary>
/// Gets or sets the flag to buffer the request body in input formatters. Default is <c>false</c>.
/// </summary>
@ -181,15 +210,6 @@ namespace Microsoft.AspNetCore.Mvc
/// </summary>
public bool RequireHttpsPermanent { get; set; }
/// <summary>
/// Gets or sets an indication whether the model binding system will bind undefined values to enumeration types.
/// <see langword="false"/> by default.
/// </summary>
public bool AllowBindingUndefinedValueToEnumType
{
get => _allowBindingUndefinedValueToEnumType.Value;
set => _allowBindingUndefinedValueToEnumType.Value = value;
}
/// <summary>
/// Gets or sets the option to determine if model binding should convert all exceptions (including ones not related to bad input)

View File

@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
public void ReturnsBinder_ForEnumType(Type modelType)
{
// Arrange
var provider = new EnumTypeModelBinderProvider(new MvcOptions { AllowBindingUndefinedValueToEnumType = true });
var provider = new EnumTypeModelBinderProvider(new MvcOptions());
var context = new TestModelBinderProviderContext(modelType);
// Act
@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
public void ReturnsBinder_ForFlagsEnumType(Type modelType)
{
// Arrange
var provider = new EnumTypeModelBinderProvider(new MvcOptions { AllowBindingUndefinedValueToEnumType = true });
var provider = new EnumTypeModelBinderProvider(new MvcOptions());
var context = new TestModelBinderProviderContext(modelType);
// Act
@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
public void DoesNotReturnBinder_ForNonEnumTypes(Type modelType)
{
// Arrange
var provider = new EnumTypeModelBinderProvider(new MvcOptions { AllowBindingUndefinedValueToEnumType = false });
var provider = new EnumTypeModelBinderProvider(new MvcOptions());
var context = new TestModelBinderProviderContext(modelType);
// Act

View File

@ -5,7 +5,6 @@ using System;
using System.ComponentModel;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
using Microsoft.Extensions.Options;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.ModelBinding
@ -18,13 +17,13 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
[InlineData(false, typeof(IntEnum?))]
[InlineData(false, typeof(FlagsEnum?))]
public async Task BindModel_SetsModel_ForEmptyValue_AndNullableEnumTypes(
bool allowBindingUndefinedValueToEnumType,
bool suppressBindingUndefinedValueToEnumType,
Type modelType)
{
// Arrange
var binderInfo = GetBinderAndContext(
modelType,
allowBindingUndefinedValueToEnumType,
suppressBindingUndefinedValueToEnumType,
valueProviderValue: "");
var bindingContext = binderInfo.Item1;
var binder = binderInfo.Item2;
@ -43,14 +42,14 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
[InlineData(false, typeof(IntEnum))]
[InlineData(false, typeof(FlagsEnum))]
public async Task BindModel_AddsErrorToModelState_ForEmptyValue_AndNonNullableEnumTypes(
bool allowBindingUndefinedValueToEnumType,
bool suprressBindingUndefinedValueToEnumType,
Type modelType)
{
// Arrange
var message = "The value '' is invalid.";
var binderInfo = GetBinderAndContext(
modelType,
allowBindingUndefinedValueToEnumType,
suprressBindingUndefinedValueToEnumType,
valueProviderValue: "");
var bindingContext = binderInfo.Item1;
var binder = binderInfo.Item2;
@ -72,14 +71,14 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
[InlineData(false, "Value1")]
[InlineData(false, "1")]
public async Task BindModel_BindsEnumModels_ForValuesInArray(
bool allowBindingUndefinedValueToEnumType,
bool suppressBindingUndefinedValueToEnumType,
string enumValue)
{
// Arrange
var modelType = typeof(IntEnum);
var binderInfo = GetBinderAndContext(
modelType,
allowBindingUndefinedValueToEnumType,
suppressBindingUndefinedValueToEnumType,
valueProviderValue: new object[] { enumValue });
var bindingContext = binderInfo.Item1;
var binder = binderInfo.Item2;
@ -102,7 +101,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
[InlineData("8, 1", false)]
[InlineData("Value2, Value8", false)]
[InlineData("value8,value4,value2,value1", false)]
public async Task BindModel_BindsTo_NonNullableFlagsEnumType(string flagsEnumValue, bool allowBindingUndefinedValueToEnumType)
public async Task BindModel_BindsTo_NonNullableFlagsEnumType(string flagsEnumValue, bool suppressBindingUndefinedValueToEnumType)
{
// Arrange
var modelType = typeof(FlagsEnum);
@ -110,7 +109,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
var expected = enumConverter.ConvertFrom(flagsEnumValue).ToString();
var binderInfo = GetBinderAndContext(
modelType,
allowBindingUndefinedValueToEnumType,
suppressBindingUndefinedValueToEnumType,
valueProviderValue: new object[] { flagsEnumValue });
var bindingContext = binderInfo.Item1;
var binder = binderInfo.Item2;
@ -133,7 +132,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
[InlineData("8, 1", false)]
[InlineData("Value2, Value8", false)]
[InlineData("value8,value4,value2,value1", false)]
public async Task BindModel_BindsTo_NullableFlagsEnumType(string flagsEnumValue, bool allowBindingUndefinedValueToEnumType)
public async Task BindModel_BindsTo_NullableFlagsEnumType(string flagsEnumValue, bool suppressBindingUndefinedValueToEnumType)
{
// Arrange
var modelType = typeof(FlagsEnum?);
@ -141,7 +140,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
var expected = enumConverter.ConvertFrom(flagsEnumValue).ToString();
var binderInfo = GetBinderAndContext(
modelType,
allowBindingUndefinedValueToEnumType,
suppressBindingUndefinedValueToEnumType,
valueProviderValue: new object[] { flagsEnumValue });
var bindingContext = binderInfo.Item1;
var binder = binderInfo.Item2;
@ -168,7 +167,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
var message = $"The value '{suppliedValue}' is not valid.";
var binderInfo = GetBinderAndContext(
modelType,
allowBindingUndefinedValueToEnumType: true,
suppressBindingUndefinedValueToEnumType: false,
valueProviderValue: new object[] { suppliedValue });
var bindingContext = binderInfo.Item1;
var binder = binderInfo.Item2;
@ -209,7 +208,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
var message = $"The value '{suppliedValue}' is invalid.";
var binderInfo = GetBinderAndContext(
modelType,
allowBindingUndefinedValueToEnumType: false,
suppressBindingUndefinedValueToEnumType: true,
valueProviderValue: new object[] { suppliedValue });
var bindingContext = binderInfo.Item1;
var binder = binderInfo.Item2;
@ -251,7 +250,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
var binderProviderContext = new TestModelBinderProviderContext(modelType);
var binderInfo = GetBinderAndContext(
modelType,
allowBindingUndefinedValueToEnumType: true,
suppressBindingUndefinedValueToEnumType: false,
valueProviderValue: new object[] { suppliedValue });
var bindingContext = binderInfo.Item1;
var binder = binderInfo.Item2;
@ -267,7 +266,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
private static (DefaultModelBindingContext, IModelBinder) GetBinderAndContext(
Type modelType,
bool allowBindingUndefinedValueToEnumType,
bool suppressBindingUndefinedValueToEnumType,
object valueProviderValue)
{
var binderProviderContext = new TestModelBinderProviderContext(modelType);
@ -284,7 +283,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
};
var binderProvider = new EnumTypeModelBinderProvider(new MvcOptions
{
AllowBindingUndefinedValueToEnumType = allowBindingUndefinedValueToEnumType
SuppressBindingUndefinedValueToEnumType = suppressBindingUndefinedValueToEnumType
});
var binder = binderProvider.GetBinder(binderProviderContext);
return (bindingContext, binder);

View File

@ -30,7 +30,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTest
var mvcOptions = services.GetRequiredService<IOptions<MvcOptions>>().Value;
// Assert
Assert.False(mvcOptions.AllowBindingUndefinedValueToEnumType);
Assert.False(mvcOptions.SuppressBindingUndefinedValueToEnumType);
Assert.Equal(InputFormatterExceptionModelStatePolicy.AllExceptions, mvcOptions.InputFormatterExceptionModelStatePolicy);
Assert.False(mvcOptions.SuppressJsonDeserializationExceptionMessagesInModelState); // This name needs to be inverted in #7157
}
@ -49,7 +49,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTest
var mvcOptions = services.GetRequiredService<IOptions<MvcOptions>>().Value;
// Assert
Assert.True(mvcOptions.AllowBindingUndefinedValueToEnumType);
Assert.True(mvcOptions.SuppressBindingUndefinedValueToEnumType);
Assert.Equal(InputFormatterExceptionModelStatePolicy.MalformedInputExceptions, mvcOptions.InputFormatterExceptionModelStatePolicy);
Assert.True(mvcOptions.SuppressJsonDeserializationExceptionMessagesInModelState); // This name needs to be inverted in #7157
}
@ -68,7 +68,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTest
var mvcOptions = services.GetRequiredService<IOptions<MvcOptions>>().Value;
// Assert
Assert.True(mvcOptions.AllowBindingUndefinedValueToEnumType);
Assert.True(mvcOptions.SuppressBindingUndefinedValueToEnumType);
Assert.Equal(InputFormatterExceptionModelStatePolicy.MalformedInputExceptions, mvcOptions.InputFormatterExceptionModelStatePolicy);
Assert.True(mvcOptions.SuppressJsonDeserializationExceptionMessagesInModelState); // This name needs to be inverted in #7157
}