Remove remaining `CompatibilitySwitch<T>` properties

- #4866 3 of 3
- other than ignored parameters and properties, no vestige remains of alternate behaviours
- also remove `AllowAreas` use outside src/Mvc
- left the `IEnumerable<ICompatibilitySwitch>` implementations to avoid `breakingchanges.netcore.json` churn
  - made the implementations more consistent
- left one `ConfigureCompatibilityOptions<MvcOptions>` subclass: `MvcOptionsConfigureCompatibilityOptions`
- note `AllowShortCircuitingValidationWhenNoValidatorsArePresent` default now applies to `TryValidateModel(...)` etc.

nits:
- updated `CompatibilityVersion` doc comments
- "currently unused" -> "currently ignored" in doc comments from part 2
- took a few VS suggestions
- VS seems to have cleaned up some trailing whitespace in files I had opened but didn't manually change
This commit is contained in:
Doug Bunting 2018-12-13 22:59:45 -08:00 committed by Doug Bunting
parent db0c347c23
commit f113a20dfd
63 changed files with 246 additions and 1243 deletions

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.using Microsoft.AspNetCore.Authorization; // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.using Microsoft.AspNetCore.Authorization;
using System; using System;
@ -192,7 +192,6 @@ namespace Microsoft.AspNetCore.Authentication
var additionalParts = GetAdditionalParts(); var additionalParts = GetAdditionalParts();
var mvcBuilder = services var mvcBuilder = services
.AddMvc() .AddMvc()
.AddRazorPagesOptions(o => o.AllowAreas = true)
.ConfigureApplicationPartManager(apm => .ConfigureApplicationPartManager(apm =>
{ {
foreach (var part in additionalParts) foreach (var part in additionalParts)

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.using Microsoft.AspNetCore.Authorization; // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.using Microsoft.AspNetCore.Authorization;
using System; using System;
@ -193,7 +193,6 @@ namespace Microsoft.AspNetCore.Authentication
var additionalParts = GetAdditionalParts(); var additionalParts = GetAdditionalParts();
var mvcBuilder = services var mvcBuilder = services
.AddMvc() .AddMvc()
.AddRazorPagesOptions(o => o.AllowAreas = true)
.ConfigureApplicationPartManager(apm => .ConfigureApplicationPartManager(apm =>
{ {
foreach (var part in additionalParts) foreach (var part in additionalParts)

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
@ -37,7 +37,6 @@ namespace Microsoft.AspNetCore.Identity.UI
name = name ?? throw new ArgumentNullException(nameof(name)); name = name ?? throw new ArgumentNullException(nameof(name));
options = options ?? throw new ArgumentNullException(nameof(options)); options = options ?? throw new ArgumentNullException(nameof(options));
options.AllowAreas = true;
options.Conventions.AuthorizeAreaFolder(IdentityUIDefaultAreaName, "/Account/Manage"); options.Conventions.AuthorizeAreaFolder(IdentityUIDefaultAreaName, "/Account/Manage");
options.Conventions.AuthorizeAreaPage(IdentityUIDefaultAreaName, "/Account/Logout"); options.Conventions.AuthorizeAreaPage(IdentityUIDefaultAreaName, "/Account/Logout");
var convention = new IdentityPageModelConvention<TUser>(); var convention = new IdentityPageModelConvention<TUser>();

View File

@ -7,8 +7,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
{ {
/// <summary> /// <summary>
/// Defines the set of policies that determine how the model binding system interprets exceptions /// Defines the set of policies that determine how the model binding system interprets exceptions
/// thrown by an <see cref="IInputFormatter"/>. Applications should set /// thrown by an <see cref="IInputFormatter"/>. <seealso cref="IInputFormatterExceptionPolicy"/>
/// <c>MvcOptions.InputFormatterExceptionPolicy</c> to configure this setting.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// <para> /// <para>
@ -25,12 +24,12 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
/// The policy associated with <see cref="InputFormatterExceptionPolicy.AllExceptions"/> treats /// The policy associated with <see cref="InputFormatterExceptionPolicy.AllExceptions"/> treats
/// all such categories of problems as model state errors, and usually will be reported to the client as /// all such categories of problems as model state errors, and usually will be reported to the client as
/// an HTTP 400. This was the only policy supported by model binding in ASP.NET Core MVC 1.0, 1.1, and 2.0 /// an HTTP 400. This was the only policy supported by model binding in ASP.NET Core MVC 1.0, 1.1, and 2.0
/// and is still the default for historical reasons. /// and is still the default for historical reasons.
/// </para> /// </para>
/// <para> /// <para>
/// The policy associated with <see cref="InputFormatterExceptionPolicy.MalformedInputExceptions"/> /// The policy associated with <see cref="InputFormatterExceptionPolicy.MalformedInputExceptions"/>
/// treats only <see cref="InputFormatterException"/> and its subclasses as model state errors. This means that /// treats only <see cref="InputFormatterException"/> and its subclasses as model state errors. This means that
/// exceptions that are not related to the content of the HTTP request (such as a disconnect) will be rethrown, /// exceptions that are not related to the content of the HTTP request (such as a disconnect) will be re-thrown,
/// which by default would cause an HTTP 500 response, unless there is exception-handling middleware enabled. /// which by default would cause an HTTP 500 response, unless there is exception-handling middleware enabled.
/// </para> /// </para>
/// </remarks> /// </remarks>
@ -44,7 +43,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
/// <summary> /// <summary>
/// This value indicates that only <see cref="InputFormatterException"/> and subclasses will be treated /// This value indicates that only <see cref="InputFormatterException"/> and subclasses will be treated
/// as model state errors. All other exceptions types will be rethrown and can be handled by a higher /// as model state errors. All other exceptions types will be re-thrown and can be handled by a higher
/// level exception handler, such as exception-handling middleware. /// level exception handler, such as exception-handling middleware.
/// </summary> /// </summary>
MalformedInputExceptions = 1, MalformedInputExceptions = 1,

View File

@ -53,7 +53,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
/// constraints.</param> /// constraints.</param>
/// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param> /// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
/// <param name="mapper">The <see cref="IActionResultTypeMapper"/>.</param> /// <param name="mapper">The <see cref="IActionResultTypeMapper"/>.</param>
/// <remarks>The <paramref name="mapper"/> parameter is currently unused.</remarks> /// <remarks>The <paramref name="mapper"/> parameter is currently ignored.</remarks>
[Obsolete("This constructor is obsolete and will be removed in a future release.")] [Obsolete("This constructor is obsolete and will be removed in a future release.")]
public DefaultApiDescriptionProvider( public DefaultApiDescriptionProvider(
IOptions<MvcOptions> optionsAccessor, IOptions<MvcOptions> optionsAccessor,
@ -76,7 +76,7 @@ namespace Microsoft.AspNetCore.Mvc.ApiExplorer
/// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param> /// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
/// <param name="mapper">The <see cref="IActionResultTypeMapper"/>.</param> /// <param name="mapper">The <see cref="IActionResultTypeMapper"/>.</param>
/// <param name="routeOptions">The accessor for <see cref="RouteOptions"/>.</param> /// <param name="routeOptions">The accessor for <see cref="RouteOptions"/>.</param>
/// <remarks>The <paramref name="mapper"/> parameter is currently unused.</remarks> /// <remarks>The <paramref name="mapper"/> parameter is currently ignored.</remarks>
public DefaultApiDescriptionProvider( public DefaultApiDescriptionProvider(
IOptions<MvcOptions> optionsAccessor, IOptions<MvcOptions> optionsAccessor,
IInlineConstraintResolver constraintResolver, IInlineConstraintResolver constraintResolver,

View File

@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Mvc
/// </summary> /// </summary>
public class ApiBehaviorOptions : IEnumerable<ICompatibilitySwitch> public class ApiBehaviorOptions : IEnumerable<ICompatibilitySwitch>
{ {
private readonly ICompatibilitySwitch[] _switches = Array.Empty<ICompatibilitySwitch>(); private readonly IReadOnlyList<ICompatibilitySwitch> _switches = Array.Empty<ICompatibilitySwitch>();
private Func<ActionContext, IActionResult> _invalidModelStateResponseFactory; private Func<ActionContext, IActionResult> _invalidModelStateResponseFactory;
/// <summary> /// <summary>
@ -84,10 +84,7 @@ namespace Microsoft.AspNetCore.Mvc
/// </summary> /// </summary>
public IDictionary<int, ClientErrorData> ClientErrorMapping { get; } = new Dictionary<int, ClientErrorData>(); public IDictionary<int, ClientErrorData> ClientErrorMapping { get; } = new Dictionary<int, ClientErrorData>();
IEnumerator<ICompatibilitySwitch> IEnumerable<ICompatibilitySwitch>.GetEnumerator() IEnumerator<ICompatibilitySwitch> IEnumerable<ICompatibilitySwitch>.GetEnumerator() => _switches.GetEnumerator();
{
return ((IEnumerable<ICompatibilitySwitch>)_switches).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => _switches.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _switches.GetEnumerator();
} }

View File

@ -52,14 +52,8 @@ namespace Microsoft.AspNetCore.Mvc
/// ASP.NET Core MVC 2.1. /// ASP.NET Core MVC 2.1.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// ASP.NET Core MVC 2.1 introduced compatibility switches for the following: /// ASP.NET Core MVC 2.1 introduced a compatibility switch for
/// <list type="bullet"> /// <c>MvcJsonOptions.AllowInputFormatterExceptionMessages</c>. This is now a regular property.
/// <item><description><see cref="MvcOptions.InputFormatterExceptionPolicy"/></description></item>
/// <item><description><see cref="MvcOptions.SuppressBindingUndefinedValueToEnumType"/></description></item>
/// <item><description><c>MvcJsonOptions.AllowInputFormatterExceptionMessages</c></description></item>
/// <item><description><c>RazorPagesOptions.AllowAreas</c></description></item>
/// <item><description><c>RazorPagesOptions.AllowMappingHeadRequestsToGetHandler</c></description></item>
/// </list>
/// </remarks> /// </remarks>
[Obsolete("This " + nameof(CompatibilityVersion) + " value is obsolete. The recommended alternatives are " + [Obsolete("This " + nameof(CompatibilityVersion) + " value is obsolete. The recommended alternatives are " +
nameof(Version_3_0) + " or later.")] nameof(Version_3_0) + " or later.")]
@ -73,15 +67,10 @@ namespace Microsoft.AspNetCore.Mvc
/// ASP.NET Core MVC 2.2 introduced compatibility switches for the following: /// ASP.NET Core MVC 2.2 introduced compatibility switches for the following:
/// <list type="bullet"> /// <list type="bullet">
/// <item><description><c>ApiBehaviorOptions.SuppressMapClientErrors</c></description></item> /// <item><description><c>ApiBehaviorOptions.SuppressMapClientErrors</c></description></item>
/// <item><description><c>MvcDataAnnotationsLocalizationOptions.AllowDataAnnotationsLocalizationForEnumDisplayAttributes</c></description></item>
/// <item><description><see cref="MvcOptions.EnableEndpointRouting" /></description></item> /// <item><description><see cref="MvcOptions.EnableEndpointRouting" /></description></item>
/// <item><description><see cref="MvcOptions.AllowShortCircuitingValidationWhenNoValidatorsArePresent"/></description></item>
/// <item><description><see cref="MvcOptions.MaxValidationDepth" /></description></item> /// <item><description><see cref="MvcOptions.MaxValidationDepth" /></description></item>
/// <item><description><c>RazorPagesOptions.AllowDefaultHandlingForOptionsRequests</c></description></item>
/// <item><description><c>RazorViewEngineOptions.AllowRecompilingViewsOnFileChange</c></description></item>
/// <item><description><c>MvcViewOptions.AllowRenderingMaxLengthAttribute</c></description></item>
/// <item><description><c>MvcXmlOptions.AllowRfc7807CompliantProblemDetailsFormat</c></description></item>
/// </list> /// </list>
/// All of the above are now regular properties.
/// </remarks> /// </remarks>
[Obsolete("This " + nameof(CompatibilityVersion) + " value is obsolete. The recommended alternatives are " + [Obsolete("This " + nameof(CompatibilityVersion) + " value is obsolete. The recommended alternatives are " +
nameof(Version_3_0) + " or later.")] nameof(Version_3_0) + " or later.")]

View File

@ -2,7 +2,6 @@
// 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; using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -21,12 +20,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
{ {
get get
{ {
return new Dictionary<string, object> return new Dictionary<string, object>();
{
[nameof(MvcOptions.InputFormatterExceptionPolicy)] = InputFormatterExceptionPolicy.MalformedInputExceptions,
[nameof(MvcOptions.SuppressBindingUndefinedValueToEnumType)] = true,
[nameof(MvcOptions.AllowShortCircuitingValidationWhenNoValidatorsArePresent)] = true,
};
} }
} }
} }

View File

@ -192,13 +192,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
private bool ShouldHandleException(IInputFormatter formatter) private bool ShouldHandleException(IInputFormatter formatter)
{ {
var policy = _options.InputFormatterExceptionPolicy; // Any explicit policy on the formatters overrides the default.
var policy = (formatter as IInputFormatterExceptionPolicy)?.ExceptionPolicy ??
// Any explicit policy on the formatters takes precedence over the global policy on MvcOptions InputFormatterExceptionPolicy.MalformedInputExceptions;
if (formatter is IInputFormatterExceptionPolicy exceptionPolicy)
{
policy = exceptionPolicy.ExceptionPolicy;
}
return policy == InputFormatterExceptionPolicy.AllExceptions; return policy == InputFormatterExceptionPolicy.AllExceptions;
} }

View File

@ -12,8 +12,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
/// </summary> /// </summary>
public class EnumTypeModelBinder : SimpleTypeModelBinder public class EnumTypeModelBinder : SimpleTypeModelBinder
{ {
private readonly bool _suppressBindingUndefinedValueToEnumType;
/// <summary> /// <summary>
/// Initializes a new instance of <see cref="EnumTypeModelBinder"/>. /// Initializes a new instance of <see cref="EnumTypeModelBinder"/>.
/// </summary> /// </summary>
@ -22,6 +20,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
/// </param> /// </param>
/// <param name="modelType">The model type.</param> /// <param name="modelType">The model type.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>,</param> /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>,</param>
/// <remarks>
/// The <paramref name="suppressBindingUndefinedValueToEnumType"/> parameter is currently ignored.
/// </remarks>
public EnumTypeModelBinder( public EnumTypeModelBinder(
bool suppressBindingUndefinedValueToEnumType, bool suppressBindingUndefinedValueToEnumType,
Type modelType, Type modelType,
@ -37,8 +38,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
{ {
throw new ArgumentNullException(nameof(loggerFactory)); throw new ArgumentNullException(nameof(loggerFactory));
} }
_suppressBindingUndefinedValueToEnumType = suppressBindingUndefinedValueToEnumType;
} }
protected override void CheckModel( protected override void CheckModel(
@ -46,23 +45,20 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
ValueProviderResult valueProviderResult, ValueProviderResult valueProviderResult,
object model) object model)
{ {
if (model == null || !_suppressBindingUndefinedValueToEnumType) if (model == null)
{ {
base.CheckModel(bindingContext, valueProviderResult, model); base.CheckModel(bindingContext, valueProviderResult, model);
} }
else if (IsDefinedInEnum(model, bindingContext))
{
bindingContext.Result = ModelBindingResult.Success(model);
}
else else
{ {
if (IsDefinedInEnum(model, bindingContext)) bindingContext.ModelState.TryAddModelError(
{ bindingContext.ModelName,
bindingContext.Result = ModelBindingResult.Success(model); bindingContext.ModelMetadata.ModelBindingMessageProvider.ValueIsInvalidAccessor(
} valueProviderResult.ToString()));
else
{
bindingContext.ModelState.TryAddModelError(
bindingContext.ModelName,
bindingContext.ModelMetadata.ModelBindingMessageProvider.ValueIsInvalidAccessor(
valueProviderResult.ToString()));
}
} }
} }
@ -70,14 +66,14 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
{ {
var modelType = bindingContext.ModelMetadata.UnderlyingOrModelType; var modelType = bindingContext.ModelMetadata.UnderlyingOrModelType;
// Check if the converted value is indeed defined on the enum as EnumTypeConverter // Check if the converted value is indeed defined on the enum as EnumTypeConverter
// converts value to the backing type (ex: integer) and does not check if the value is defined on the enum. // converts value to the backing type (ex: integer) and does not check if the value is defined on the enum.
if (bindingContext.ModelMetadata.IsFlagsEnum) if (bindingContext.ModelMetadata.IsFlagsEnum)
{ {
// Enum.IsDefined does not work with combined flag enum values. // Enum.IsDefined does not work with combined flag enum values.
// From EnumDataTypeAttribute.cs in CoreFX. // From EnumDataTypeAttribute.cs in CoreFX.
// Examples: // Examples:
// //
// [Flags] // [Flags]
// enum FlagsEnum { Value1 = 1, Value2 = 2, Value4 = 4 } // enum FlagsEnum { Value1 = 1, Value2 = 2, Value4 = 4 }
// //
@ -87,7 +83,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
// //
// Invalid Scenarios: // Invalid Scenarios:
// 1. valueProviderResult="2,10", model=12, underlying=12, converted=12 // 1. valueProviderResult="2,10", model=12, underlying=12, converted=12
// //
var underlying = Convert.ChangeType( var underlying = Convert.ChangeType(
model, model,
Enum.GetUnderlyingType(modelType), Enum.GetUnderlyingType(modelType),

View File

@ -12,15 +12,13 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
/// </summary> /// </summary>
public class EnumTypeModelBinderProvider : IModelBinderProvider public class EnumTypeModelBinderProvider : IModelBinderProvider
{ {
private readonly MvcOptions _options;
/// <summary> /// <summary>
/// Initializes a new instance of <see cref="EnumTypeModelBinderProvider"/>. /// Initializes a new instance of <see cref="EnumTypeModelBinderProvider"/>.
/// </summary> /// </summary>
/// <param name="options">The <see cref="MvcOptions"/>.</param> /// <param name="options">The <see cref="MvcOptions"/>.</param>
/// <remarks>The <paramref name="options"/> parameter is currently ignored.</remarks>
public EnumTypeModelBinderProvider(MvcOptions options) public EnumTypeModelBinderProvider(MvcOptions options)
{ {
_options = options;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -35,7 +33,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
{ {
var loggerFactory = context.Services.GetRequiredService<ILoggerFactory>(); var loggerFactory = context.Services.GetRequiredService<ILoggerFactory>();
return new EnumTypeModelBinder( return new EnumTypeModelBinder(
_options.SuppressBindingUndefinedValueToEnumType, suppressBindingUndefinedValueToEnumType: true,
context.Metadata.UnderlyingOrModelType, context.Metadata.UnderlyingOrModelType,
loggerFactory); loggerFactory);
} }

View File

@ -54,7 +54,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
/// <param name="validator">The <see cref="IObjectModelValidator"/>.</param> /// <param name="validator">The <see cref="IObjectModelValidator"/>.</param>
/// <param name="mvcOptions">The <see cref="MvcOptions"/> accessor.</param> /// <param name="mvcOptions">The <see cref="MvcOptions"/> accessor.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param> /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
/// <remarks>The <paramref name="mvcOptions"/> parameter is currently unused.</remarks> /// <remarks>The <paramref name="mvcOptions"/> parameter is currently ignored.</remarks>
public ParameterBinder( public ParameterBinder(
IModelMetadataProvider modelMetadataProvider, IModelMetadataProvider modelMetadataProvider,
IModelBinderFactory modelBinderFactory, IModelBinderFactory modelBinderFactory,

View File

@ -42,8 +42,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
validationState) validationState)
{ {
MaxValidationDepth = _mvcOptions.MaxValidationDepth, MaxValidationDepth = _mvcOptions.MaxValidationDepth,
AllowShortCircuitingValidationWhenNoValidatorsArePresent =
_mvcOptions.AllowShortCircuitingValidationWhenNoValidatorsArePresent,
}; };
return visitor; return visitor;

View File

@ -107,7 +107,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
/// Gets or sets a value that determines if <see cref="ValidationVisitor"/> can short circuit validation when a model /// Gets or sets a value that determines if <see cref="ValidationVisitor"/> can short circuit validation when a model
/// does not have any associated validators. /// does not have any associated validators.
/// </summary> /// </summary>
public bool AllowShortCircuitingValidationWhenNoValidatorsArePresent { get; set; } /// <value>The default value is <see langword="true"/>.</value>
/// <remarks>This property is currently ignored.</remarks>
public bool AllowShortCircuitingValidationWhenNoValidatorsArePresent { get; set; } = true;
/// <summary> /// <summary>
/// Validates a object. /// Validates a object.
@ -266,9 +268,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
} }
// If the metadata indicates that no validators exist AND the aggregate state for the key says that the model graph // If the metadata indicates that no validators exist AND the aggregate state for the key says that the model graph
// is not invalid (i.e. is one of Unvalidated, Valid, or Skipped) we can safely mark the graph as valid. // is not invalid (i.e. is one of Unvalidated, Valid, or Skipped) we can safely mark the graph as valid.
else if ( else if (metadata.HasValidators == false &&
AllowShortCircuitingValidationWhenNoValidatorsArePresent &&
metadata.HasValidators == false &&
ModelState.GetFieldValidationState(key) != ModelValidationState.Invalid) ModelState.GetFieldValidationState(key) != ModelValidationState.Invalid)
{ {
// No validators will be created for this graph of objects. Mark it as valid if it wasn't previously validated. // No validators will be created for this graph of objects. Mark it as valid if it wasn't previously validated.

View File

@ -19,12 +19,7 @@ namespace Microsoft.AspNetCore.Mvc
/// </summary> /// </summary>
public class MvcOptions : IEnumerable<ICompatibilitySwitch> public class MvcOptions : IEnumerable<ICompatibilitySwitch>
{ {
// See CompatibilitySwitch.cs for guide on how to implement these. private readonly IReadOnlyList<ICompatibilitySwitch> _switches = Array.Empty<ICompatibilitySwitch>();
private readonly CompatibilitySwitch<InputFormatterExceptionPolicy> _inputFormatterExceptionPolicy;
private readonly CompatibilitySwitch<bool> _suppressBindingUndefinedValueToEnumType;
private readonly CompatibilitySwitch<bool> _allowShortCircuitingValidationWhenNoValidatorsArePresent;
private readonly ICompatibilitySwitch[] _switches;
private int _maxModelStateErrors = ModelStateDictionary.DefaultMaxAllowedErrors; private int _maxModelStateErrors = ModelStateDictionary.DefaultMaxAllowedErrors;
private int? _maxValidationDepth = 32; private int? _maxValidationDepth = 32;
@ -44,17 +39,6 @@ namespace Microsoft.AspNetCore.Mvc
ModelMetadataDetailsProviders = new List<IMetadataDetailsProvider>(); ModelMetadataDetailsProviders = new List<IMetadataDetailsProvider>();
ModelValidatorProviders = new List<IModelValidatorProvider>(); ModelValidatorProviders = new List<IModelValidatorProvider>();
ValueProviderFactories = new List<IValueProviderFactory>(); ValueProviderFactories = new List<IValueProviderFactory>();
_inputFormatterExceptionPolicy = new CompatibilitySwitch<InputFormatterExceptionPolicy>(nameof(InputFormatterExceptionPolicy), InputFormatterExceptionPolicy.AllExceptions);
_suppressBindingUndefinedValueToEnumType = new CompatibilitySwitch<bool>(nameof(SuppressBindingUndefinedValueToEnumType));
_allowShortCircuitingValidationWhenNoValidatorsArePresent = new CompatibilitySwitch<bool>(nameof(AllowShortCircuitingValidationWhenNoValidatorsArePresent));
_switches = new ICompatibilitySwitch[]
{
_inputFormatterExceptionPolicy,
_suppressBindingUndefinedValueToEnumType,
_allowShortCircuitingValidationWhenNoValidatorsArePresent,
};
} }
/// <summary> /// <summary>
@ -102,36 +86,11 @@ namespace Microsoft.AspNetCore.Mvc
/// </summary> /// </summary>
public FormatterMappings FormatterMappings { get; } public FormatterMappings FormatterMappings { get; }
/// <summary>
/// Gets or sets a value which determines how the model binding system interprets exceptions thrown by an <see cref="IInputFormatter"/>.
/// </summary>
/// <value>
/// The default value is <see cref="InputFormatterExceptionPolicy.MalformedInputExceptions"/>.
/// </value>
public InputFormatterExceptionPolicy InputFormatterExceptionPolicy
{
get => _inputFormatterExceptionPolicy.Value;
set => _inputFormatterExceptionPolicy.Value = value;
}
/// <summary> /// <summary>
/// Gets a list of <see cref="IInputFormatter"/>s that are used by this application. /// Gets a list of <see cref="IInputFormatter"/>s that are used by this application.
/// </summary> /// </summary>
public FormatterCollection<IInputFormatter> InputFormatters { get; } public FormatterCollection<IInputFormatter> InputFormatters { get; }
/// <summary>
/// Gets or sets a value indicating whether the model binding system will bind undefined values to
/// enum types.
/// </summary>
/// <value>
/// The default value is <see langword="true"/>.
/// </value>
public bool SuppressBindingUndefinedValueToEnumType
{
get => _suppressBindingUndefinedValueToEnumType.Value;
set => _suppressBindingUndefinedValueToEnumType.Value = value;
}
/// <summary> /// <summary>
/// Gets or sets the flag to buffer the request body in input formatters. Default is <c>false</c>. /// Gets or sets the flag to buffer the request body in input formatters. Default is <c>false</c>.
/// </summary> /// </summary>
@ -250,30 +209,7 @@ namespace Microsoft.AspNetCore.Mvc
} }
} }
/// <summary> IEnumerator<ICompatibilitySwitch> IEnumerable<ICompatibilitySwitch>.GetEnumerator() => _switches.GetEnumerator();
/// Gets or sets a value that determines if <see cref="ValidationVisitor"/>
/// can short-circuit validation when a model does not have any associated validators.
/// </summary>
/// <value>
/// The default value is <see langword="true"/>.
/// </value>
/// <remarks>
/// When <see cref="ModelMetadata.HasValidators"/> is <see langword="true"/>, that is, it is determined
/// that a model or any of it's properties or collection elements cannot have any validators,
/// <see cref="ValidationVisitor"/> can short-circuit validation for the model and mark the object
/// graph as valid. Setting this property to <see langword="true"/>, allows <see cref="ValidationVisitor"/> to
/// perform this optimization.
/// </remarks>
public bool AllowShortCircuitingValidationWhenNoValidatorsArePresent
{
get => _allowShortCircuitingValidationWhenNoValidatorsArePresent.Value;
set => _allowShortCircuitingValidationWhenNoValidatorsArePresent.Value = value;
}
IEnumerator<ICompatibilitySwitch> IEnumerable<ICompatibilitySwitch>.GetEnumerator()
{
return ((IEnumerable<ICompatibilitySwitch>)_switches).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => _switches.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _switches.GetEnumerator();
} }

View File

@ -102,5 +102,25 @@
"TypeId": "public class Microsoft.AspNetCore.Mvc.MvcOptions : System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Mvc.Infrastructure.ICompatibilitySwitch>", "TypeId": "public class Microsoft.AspNetCore.Mvc.MvcOptions : System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Mvc.Infrastructure.ICompatibilitySwitch>",
"MemberId": "public System.Void set_AllowValidatingTopLevelNodes(System.Boolean value)", "MemberId": "public System.Void set_AllowValidatingTopLevelNodes(System.Boolean value)",
"Kind": "Removal" "Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Mvc.MvcOptions : System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Mvc.Infrastructure.ICompatibilitySwitch>",
"MemberId": "public Microsoft.AspNetCore.Mvc.Formatters.InputFormatterExceptionPolicy get_InputFormatterExceptionPolicy()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Mvc.MvcOptions : System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Mvc.Infrastructure.ICompatibilitySwitch>",
"MemberId": "public System.Boolean get_SuppressBindingUndefinedValueToEnumType()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Mvc.MvcOptions : System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Mvc.Infrastructure.ICompatibilitySwitch>",
"MemberId": "public System.Void set_InputFormatterExceptionPolicy(Microsoft.AspNetCore.Mvc.Formatters.InputFormatterExceptionPolicy value)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Mvc.MvcOptions : System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Mvc.Infrastructure.ICompatibilitySwitch>",
"MemberId": "public System.Void set_SuppressBindingUndefinedValueToEnumType(System.Boolean value)",
"Kind": "Removal"
} }
] ]

View File

@ -27,9 +27,6 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
<IConfigureOptions<MvcDataAnnotationsLocalizationOptions>, <IConfigureOptions<MvcDataAnnotationsLocalizationOptions>,
MvcDataAnnotationsLocalizationOptionsSetup>()); MvcDataAnnotationsLocalizationOptionsSetup>());
} }
services.TryAddEnumerable(
ServiceDescriptor.Transient<IPostConfigureOptions<MvcDataAnnotationsLocalizationOptions>, MvcDataAnnotationsLocalizationConfigureCompatibilityOptions>());
} }
} }
} }

View File

@ -184,16 +184,9 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
var namesAndValues = new Dictionary<string, string>(); var namesAndValues = new Dictionary<string, string>();
IStringLocalizer enumLocalizer = null; IStringLocalizer enumLocalizer = null;
if (_localizationOptions.AllowDataAnnotationsLocalizationForEnumDisplayAttributes) if (_stringLocalizerFactory != null && _localizationOptions.DataAnnotationLocalizerProvider != null)
{ {
if (_stringLocalizerFactory != null && _localizationOptions.DataAnnotationLocalizerProvider != null) enumLocalizer = _localizationOptions.DataAnnotationLocalizerProvider(underlyingType, _stringLocalizerFactory);
{
enumLocalizer = _localizationOptions.DataAnnotationLocalizerProvider(underlyingType, _stringLocalizerFactory);
}
}
else
{
enumLocalizer = _stringLocalizerFactory?.Create(underlyingType);
} }
var enumFields = Enum.GetNames(underlyingType) var enumFields = Enum.GetNames(underlyingType)

View File

@ -1,32 +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.AspNetCore.Mvc.DataAnnotations;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.DependencyInjection
{
internal class MvcDataAnnotationsLocalizationConfigureCompatibilityOptions : ConfigureCompatibilityOptions<MvcDataAnnotationsLocalizationOptions>
{
public MvcDataAnnotationsLocalizationConfigureCompatibilityOptions(
ILoggerFactory loggerFactory,
IOptions<MvcCompatibilityOptions> compatibilityOptions)
: base(loggerFactory, compatibilityOptions)
{
}
protected override IReadOnlyDictionary<string, object> DefaultValues
{
get
{
return new Dictionary<string, object>
{
[nameof(MvcDataAnnotationsLocalizationOptions.AllowDataAnnotationsLocalizationForEnumDisplayAttributes)] = true,
};
}
}
}
}

View File

@ -14,42 +14,14 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
/// </summary> /// </summary>
public class MvcDataAnnotationsLocalizationOptions : IEnumerable<ICompatibilitySwitch> public class MvcDataAnnotationsLocalizationOptions : IEnumerable<ICompatibilitySwitch>
{ {
private readonly CompatibilitySwitch<bool> _allowDataAnnotationsLocalizationForEnumDisplayAttributes; private readonly IReadOnlyList<ICompatibilitySwitch> _switches = Array.Empty<ICompatibilitySwitch>();
private readonly ICompatibilitySwitch[] _switches;
/// <summary> /// <summary>
/// The delegate to invoke for creating <see cref="IStringLocalizer"/>. /// The delegate to invoke for creating <see cref="IStringLocalizer"/>.
/// </summary> /// </summary>
public Func<Type, IStringLocalizerFactory, IStringLocalizer> DataAnnotationLocalizerProvider; public Func<Type, IStringLocalizerFactory, IStringLocalizer> DataAnnotationLocalizerProvider;
/// <summary> IEnumerator<ICompatibilitySwitch> IEnumerable<ICompatibilitySwitch>.GetEnumerator() => _switches.GetEnumerator();
/// Instantiates a new instance of the <see cref="MvcDataAnnotationsLocalizationOptions"/> class.
/// </summary>
public MvcDataAnnotationsLocalizationOptions()
{
_allowDataAnnotationsLocalizationForEnumDisplayAttributes = new CompatibilitySwitch<bool>(nameof(AllowDataAnnotationsLocalizationForEnumDisplayAttributes));
_switches = new ICompatibilitySwitch[]
{
_allowDataAnnotationsLocalizationForEnumDisplayAttributes
};
}
/// <summary>
/// Gets or sets a value that determines if <see cref="DataAnnotationLocalizerProvider"/> should be used while localizing <see cref="Enum"/> types.
/// If set to <c>true</c> <see cref="DataAnnotationLocalizerProvider"/> will be used in localizing <see cref="Enum"/> types.
/// If set to <c>false</c> the localization will search for values in resource files for the <see cref="Enum"/>.
/// </summary>
/// <value>
/// The default value is <see langword="true"/>.
/// </value>
public bool AllowDataAnnotationsLocalizationForEnumDisplayAttributes
{
get => _allowDataAnnotationsLocalizationForEnumDisplayAttributes.Value;
set => _allowDataAnnotationsLocalizationForEnumDisplayAttributes.Value = value;
}
public IEnumerator<ICompatibilitySwitch> GetEnumerator() => ((IEnumerable<ICompatibilitySwitch>)_switches).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _switches.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _switches.GetEnumerator();
} }

View File

@ -120,7 +120,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationJsonPatch); SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationJsonPatch);
} }
/// <inheritdoc /> /// <inheritdoc />
public override InputFormatterExceptionPolicy ExceptionPolicy public override InputFormatterExceptionPolicy ExceptionPolicy
{ {
@ -180,4 +180,4 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
return base.CanRead(context); return base.CanRead(context);
} }
} }
} }

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc
/// </summary> /// </summary>
public class MvcJsonOptions : IEnumerable<ICompatibilitySwitch> public class MvcJsonOptions : IEnumerable<ICompatibilitySwitch>
{ {
private readonly ICompatibilitySwitch[] _switches = Array.Empty<ICompatibilitySwitch>(); private readonly IReadOnlyList<ICompatibilitySwitch> _switches = Array.Empty<ICompatibilitySwitch>();
/// <summary> /// <summary>
/// Gets or sets a flag to determine whether error messages from JSON deserialization by the /// Gets or sets a flag to determine whether error messages from JSON deserialization by the
@ -38,10 +38,7 @@ namespace Microsoft.AspNetCore.Mvc
/// </summary> /// </summary>
public JsonSerializerSettings SerializerSettings { get; } = JsonSerializerSettingsProvider.CreateSerializerSettings(); public JsonSerializerSettings SerializerSettings { get; } = JsonSerializerSettingsProvider.CreateSerializerSettings();
IEnumerator<ICompatibilitySwitch> IEnumerable<ICompatibilitySwitch>.GetEnumerator() IEnumerator<ICompatibilitySwitch> IEnumerable<ICompatibilitySwitch>.GetEnumerator() => _switches.GetEnumerator();
{
return ((IEnumerable<ICompatibilitySwitch>)_switches).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => _switches.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _switches.GetEnumerator();
} }

View File

@ -119,9 +119,6 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
services.TryAddEnumerable( services.TryAddEnumerable(
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, XmlDataContractSerializerMvcOptionsSetup>()); ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, XmlDataContractSerializerMvcOptionsSetup>());
services.TryAddEnumerable(
ServiceDescriptor.Transient<IPostConfigureOptions<MvcXmlOptions>, MvcXmlOptionsConfigureCompatibilityOptions>());
} }
// Internal for testing. // Internal for testing.
@ -129,9 +126,6 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
services.TryAddEnumerable( services.TryAddEnumerable(
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, XmlSerializerMvcOptionsSetup>()); ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, XmlSerializerMvcOptionsSetup>());
services.TryAddEnumerable(
ServiceDescriptor.Transient<IPostConfigureOptions<MvcXmlOptions>, MvcXmlOptionsConfigureCompatibilityOptions>());
} }
} }
} }

View File

@ -120,9 +120,6 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
services.TryAddEnumerable( services.TryAddEnumerable(
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, XmlDataContractSerializerMvcOptionsSetup>()); ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, XmlDataContractSerializerMvcOptionsSetup>());
services.TryAddEnumerable(
ServiceDescriptor.Transient<IPostConfigureOptions<MvcXmlOptions>, MvcXmlOptionsConfigureCompatibilityOptions>());
} }
// Internal for testing. // Internal for testing.
@ -130,9 +127,6 @@ namespace Microsoft.Extensions.DependencyInjection
{ {
services.TryAddEnumerable( services.TryAddEnumerable(
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, XmlSerializerMvcOptionsSetup>()); ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, XmlSerializerMvcOptionsSetup>());
services.TryAddEnumerable(
ServiceDescriptor.Transient<IPostConfigureOptions<MvcXmlOptions>, MvcXmlOptionsConfigureCompatibilityOptions>());
} }
} }
} }

View File

@ -1,32 +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.AspNetCore.Mvc.Formatters.Xml;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.DependencyInjection
{
internal sealed class MvcXmlOptionsConfigureCompatibilityOptions : ConfigureCompatibilityOptions<MvcXmlOptions>
{
public MvcXmlOptionsConfigureCompatibilityOptions(
ILoggerFactory loggerFactory,
IOptions<MvcCompatibilityOptions> compatibilityOptions)
: base(loggerFactory, compatibilityOptions)
{
}
protected override IReadOnlyDictionary<string, object> DefaultValues
{
get
{
return new Dictionary<string, object>
{
[nameof(MvcXmlOptions.AllowRfc7807CompliantProblemDetailsFormat)] = true,
};
}
}
}
}

View File

@ -18,19 +18,14 @@ namespace Microsoft.Extensions.DependencyInjection
/// </summary> /// </summary>
internal sealed class XmlDataContractSerializerMvcOptionsSetup : IConfigureOptions<MvcOptions> internal sealed class XmlDataContractSerializerMvcOptionsSetup : IConfigureOptions<MvcOptions>
{ {
private readonly MvcXmlOptions _xmlOptions;
private readonly ILoggerFactory _loggerFactory; private readonly ILoggerFactory _loggerFactory;
/// <summary> /// <summary>
/// Initializes a new instance of <see cref="XmlDataContractSerializerMvcOptionsSetup"/>. /// Initializes a new instance of <see cref="XmlDataContractSerializerMvcOptionsSetup"/>.
/// </summary> /// </summary>
/// <param name="xmlOptions"><see cref="MvcXmlOptions"/>.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param> /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
public XmlDataContractSerializerMvcOptionsSetup( public XmlDataContractSerializerMvcOptionsSetup(ILoggerFactory loggerFactory)
IOptions<MvcXmlOptions> xmlOptions,
ILoggerFactory loggerFactory)
{ {
_xmlOptions = xmlOptions?.Value ?? throw new ArgumentNullException(nameof(xmlOptions));
_loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
} }
@ -43,11 +38,11 @@ namespace Microsoft.Extensions.DependencyInjection
options.ModelMetadataDetailsProviders.Add(new DataMemberRequiredBindingMetadataProvider()); options.ModelMetadataDetailsProviders.Add(new DataMemberRequiredBindingMetadataProvider());
var inputFormatter = new XmlDataContractSerializerInputFormatter(options); var inputFormatter = new XmlDataContractSerializerInputFormatter(options);
inputFormatter.WrapperProviderFactories.Add(new ProblemDetailsWrapperProviderFactory(_xmlOptions)); inputFormatter.WrapperProviderFactories.Add(new ProblemDetailsWrapperProviderFactory());
options.InputFormatters.Add(inputFormatter); options.InputFormatters.Add(inputFormatter);
var outputFormatter = new XmlDataContractSerializerOutputFormatter(_loggerFactory); var outputFormatter = new XmlDataContractSerializerOutputFormatter(_loggerFactory);
outputFormatter.WrapperProviderFactories.Add(new ProblemDetailsWrapperProviderFactory(_xmlOptions)); outputFormatter.WrapperProviderFactories.Add(new ProblemDetailsWrapperProviderFactory());
options.OutputFormatters.Add(outputFormatter); options.OutputFormatters.Add(outputFormatter);
// Do not override any user mapping // Do not override any user mapping

View File

@ -16,19 +16,14 @@ namespace Microsoft.Extensions.DependencyInjection
/// </summary> /// </summary>
internal sealed class XmlSerializerMvcOptionsSetup : IConfigureOptions<MvcOptions> internal sealed class XmlSerializerMvcOptionsSetup : IConfigureOptions<MvcOptions>
{ {
private readonly MvcXmlOptions _xmlOptions;
private readonly ILoggerFactory _loggerFactory; private readonly ILoggerFactory _loggerFactory;
/// <summary> /// <summary>
/// Initializes a new instance of <see cref="XmlSerializerMvcOptionsSetup"/>. /// Initializes a new instance of <see cref="XmlSerializerMvcOptionsSetup"/>.
/// </summary> /// </summary>
/// <param name="xmlOptions"><see cref="MvcXmlOptions"/>.</param>
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param> /// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
public XmlSerializerMvcOptionsSetup( public XmlSerializerMvcOptionsSetup(ILoggerFactory loggerFactory)
IOptions<MvcXmlOptions> xmlOptions,
ILoggerFactory loggerFactory)
{ {
_xmlOptions = xmlOptions?.Value ?? throw new ArgumentNullException(nameof(xmlOptions));
_loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
} }
@ -49,11 +44,11 @@ namespace Microsoft.Extensions.DependencyInjection
} }
var inputFormatter = new XmlSerializerInputFormatter(options); var inputFormatter = new XmlSerializerInputFormatter(options);
inputFormatter.WrapperProviderFactories.Add(new ProblemDetailsWrapperProviderFactory(_xmlOptions)); inputFormatter.WrapperProviderFactories.Add(new ProblemDetailsWrapperProviderFactory());
options.InputFormatters.Add(inputFormatter); options.InputFormatters.Add(inputFormatter);
var outputFormatter = new XmlSerializerOutputFormatter(_loggerFactory); var outputFormatter = new XmlSerializerOutputFormatter(_loggerFactory);
outputFormatter.WrapperProviderFactories.Add(new ProblemDetailsWrapperProviderFactory(_xmlOptions)); outputFormatter.WrapperProviderFactories.Add(new ProblemDetailsWrapperProviderFactory());
options.OutputFormatters.Add(outputFormatter); options.OutputFormatters.Add(outputFormatter);
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Infrastructure;
@ -10,39 +11,13 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
/// <summary> /// <summary>
/// Provides configuration for XML formatters. /// Provides configuration for XML formatters.
/// </summary> /// </summary>
/// <remarks>This class is currently empty.</remarks>
public class MvcXmlOptions : IEnumerable<ICompatibilitySwitch> public class MvcXmlOptions : IEnumerable<ICompatibilitySwitch>
{ {
private readonly CompatibilitySwitch<bool> _allowRfc7807CompliantProblemDetailsFormat; private readonly IReadOnlyList<ICompatibilitySwitch> _switches = Array.Empty<ICompatibilitySwitch>();
private readonly IReadOnlyList<ICompatibilitySwitch> _switches;
/// <summary> IEnumerator<ICompatibilitySwitch> IEnumerable<ICompatibilitySwitch>.GetEnumerator() => _switches.GetEnumerator();
/// Creates a new instance of <see cref="MvcXmlOptions"/>.
/// </summary>
public MvcXmlOptions()
{
_allowRfc7807CompliantProblemDetailsFormat = new CompatibilitySwitch<bool>(nameof(AllowRfc7807CompliantProblemDetailsFormat));
_switches = new ICompatibilitySwitch[] IEnumerator IEnumerable.GetEnumerator() => _switches.GetEnumerator();
{
_allowRfc7807CompliantProblemDetailsFormat,
};
}
/// <summary>
/// Gets or sets a value inidicating whether <see cref="ProblemDetails"/> and <see cref="ValidationProblemDetails"/>
/// are serialized in a format compliant with the RFC 7807 specification (https://tools.ietf.org/html/rfc7807).
/// </summary>
/// <value>
/// The default value is <see langword="true"/>.
/// </value>
public bool AllowRfc7807CompliantProblemDetailsFormat
{
get => _allowRfc7807CompliantProblemDetailsFormat.Value;
set => _allowRfc7807CompliantProblemDetailsFormat.Value = value;
}
public IEnumerator<ICompatibilitySwitch> GetEnumerator() => _switches.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
} }
} }

View File

@ -7,41 +7,16 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
{ {
internal class ProblemDetailsWrapperProviderFactory : IWrapperProviderFactory internal class ProblemDetailsWrapperProviderFactory : IWrapperProviderFactory
{ {
private readonly MvcXmlOptions _options;
public ProblemDetailsWrapperProviderFactory(MvcXmlOptions options)
{
_options = options;
}
public IWrapperProvider GetProvider(WrapperProviderContext context) public IWrapperProvider GetProvider(WrapperProviderContext context)
{ {
if (context.DeclaredType == typeof(ProblemDetails)) if (context.DeclaredType == typeof(ProblemDetails))
{ {
if (_options.AllowRfc7807CompliantProblemDetailsFormat) return new WrapperProvider(typeof(ProblemDetailsWrapper), p => new ProblemDetailsWrapper((ProblemDetails)p));
{
return new WrapperProvider(typeof(ProblemDetailsWrapper), p => new ProblemDetailsWrapper((ProblemDetails)p));
}
else
{
#pragma warning disable CS0618 // Type or member is obsolete
return new WrapperProvider(typeof(ProblemDetails21Wrapper), p => new ProblemDetails21Wrapper((ProblemDetails)p));
#pragma warning restore CS0618 // Type or member is obsolete
}
} }
if (context.DeclaredType == typeof(ValidationProblemDetails)) if (context.DeclaredType == typeof(ValidationProblemDetails))
{ {
if (_options.AllowRfc7807CompliantProblemDetailsFormat) return new WrapperProvider(typeof(ValidationProblemDetailsWrapper), p => new ValidationProblemDetailsWrapper((ValidationProblemDetails)p));
{
return new WrapperProvider(typeof(ValidationProblemDetailsWrapper), p => new ValidationProblemDetailsWrapper((ValidationProblemDetails)p));
}
else
{
#pragma warning disable CS0618 // Type or member is obsolete
return new WrapperProvider(typeof(ValidationProblemDetails21Wrapper), p => new ValidationProblemDetails21Wrapper((ValidationProblemDetails)p));
#pragma warning restore CS0618 // Type or member is obsolete
}
} }
return null; return null;
@ -62,4 +37,4 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
public object Wrap(object original) => WrapDelegate(original); public object Wrap(object original) => WrapDelegate(original);
} }
} }
} }

View File

@ -115,7 +115,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
// When RootDirectory and AreaRootDirectory overlap (e.g. RootDirectory = '/', AreaRootDirectory = '/Areas'), we // When RootDirectory and AreaRootDirectory overlap (e.g. RootDirectory = '/', AreaRootDirectory = '/Areas'), we
// only want to allow a page to be associated with the area route. // only want to allow a page to be associated with the area route.
if (_pagesOptions.AllowAreas && relativePath.StartsWith(areaRootDirectory, StringComparison.OrdinalIgnoreCase)) if (relativePath.StartsWith(areaRootDirectory, StringComparison.OrdinalIgnoreCase))
{ {
routeModel = _routeModelFactory.CreateAreaRouteModel(relativePath, routeTemplate); routeModel = _routeModelFactory.CreateAreaRouteModel(relativePath, routeTemplate);
} }

View File

@ -179,10 +179,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
pageModel.Filters.Add(_pageHandlerResultFilter); pageModel.Filters.Add(_pageHandlerResultFilter);
} }
if (_razorPagesOptions.AllowDefaultHandlingForOptionsRequests) pageModel.Filters.Add(_handleOptionsRequestsFilter);
{
pageModel.Filters.Add(_handleOptionsRequestsFilter);
}
} }
/// <summary> /// <summary>

View File

@ -5,7 +5,6 @@ using System;
using Microsoft.AspNetCore.Mvc.Abstractions; using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
@ -82,8 +81,6 @@ namespace Microsoft.Extensions.DependencyInjection
// Options // Options
services.TryAddEnumerable( services.TryAddEnumerable(
ServiceDescriptor.Transient<IConfigureOptions<RazorViewEngineOptions>, RazorPagesRazorViewEngineOptionsSetup>()); ServiceDescriptor.Transient<IConfigureOptions<RazorViewEngineOptions>, RazorPagesRazorViewEngineOptionsSetup>());
services.TryAddEnumerable(
ServiceDescriptor.Transient<IPostConfigureOptions<RazorPagesOptions>, RazorPagesOptionsConfigureCompatibilityOptions>());
// Action description and invocation // Action description and invocation
services.TryAddEnumerable( services.TryAddEnumerable(

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{ {
@ -13,20 +12,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{ {
private const string Handler = "handler"; private const string Handler = "handler";
private readonly IOptions<RazorPagesOptions> _options;
public DefaultPageHandlerMethodSelector(IOptions<RazorPagesOptions> options)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
_options = options;
}
private bool AllowFuzzyHttpMethodMatching => _options?.Value.AllowMappingHeadRequestsToGetHandler ?? false;
public HandlerMethodDescriptor Select(PageContext context) public HandlerMethodDescriptor Select(PageContext context)
{ {
var handlers = SelectHandlers(context); var handlers = SelectHandlers(context);
@ -113,7 +98,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
} }
// Step 1a: do fuzzy HTTP method matching if needed. // Step 1a: do fuzzy HTTP method matching if needed.
if (candidates.Count == 0 && AllowFuzzyHttpMethodMatching) if (candidates.Count == 0)
{ {
var fuzzyHttpMethod = GetFuzzyMatchHttpMethod(context); var fuzzyHttpMethod = GetFuzzyMatchHttpMethod(context);
if (fuzzyHttpMethod != null) if (fuzzyHttpMethod != null)

View File

@ -14,27 +14,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
/// </summary> /// </summary>
public class RazorPagesOptions : IEnumerable<ICompatibilitySwitch> public class RazorPagesOptions : IEnumerable<ICompatibilitySwitch>
{ {
private readonly CompatibilitySwitch<bool> _allowAreas; private readonly IReadOnlyList<ICompatibilitySwitch> _switches = Array.Empty<ICompatibilitySwitch>();
private readonly CompatibilitySwitch<bool> _allowMappingHeadRequestsToGetHandler;
private readonly CompatibilitySwitch<bool> _allowsDefaultHandlingForOptionsRequests;
private readonly ICompatibilitySwitch[] _switches;
private string _root = "/Pages"; private string _root = "/Pages";
public RazorPagesOptions()
{
_allowAreas = new CompatibilitySwitch<bool>(nameof(AllowAreas));
_allowMappingHeadRequestsToGetHandler = new CompatibilitySwitch<bool>(nameof(AllowMappingHeadRequestsToGetHandler));
_allowsDefaultHandlingForOptionsRequests = new CompatibilitySwitch<bool>(nameof(AllowDefaultHandlingForOptionsRequests));
_switches = new ICompatibilitySwitch[]
{
_allowAreas,
_allowMappingHeadRequestsToGetHandler,
_allowsDefaultHandlingForOptionsRequests,
};
}
/// <summary> /// <summary>
/// Gets a collection of <see cref="IPageConvention"/> instances that are applied during /// Gets a collection of <see cref="IPageConvention"/> instances that are applied during
/// route and page model construction. /// route and page model construction.
@ -64,70 +46,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
} }
} }
/// <summary> IEnumerator<ICompatibilitySwitch> IEnumerable<ICompatibilitySwitch>.GetEnumerator() => _switches.GetEnumerator();
/// Gets or sets a value that determines if areas are enabled for Razor Pages.
/// </summary>
/// <value>
/// The default value is <see langword="true"/>.
/// </value>
/// <remarks>
/// When enabled, any Razor Page under the directory structure <c>/Area/AreaName/Pages/</c>
/// will be associated with an area with the name <c>AreaName</c>.
/// </remarks>
public bool AllowAreas
{
get => _allowAreas.Value;
set => _allowAreas.Value = value;
}
/// <summary>
/// Gets or sets a value that determines if HTTP method matching for Razor Pages handler methods will use
/// fuzzy matching.
/// </summary>
/// <value>
/// The default value is <see langword="true"/>.
/// </value>
/// <remarks>
/// <para>
/// When enabled, Razor Pages handler methods will be more flexible in which HTTP methods will be accepted
/// by GET and POST handler methods. This allows a GET handler methods to accept the HEAD HTTP methods in
/// addition to GET. A more specific handler method can still be defined to accept HEAD, and the most
/// specific handler will be invoked.
/// </para>
/// <para>
/// This setting reduces the number of handler methods that must be written to correctly respond to typical
/// web traffic including requests from internet infrastructure such as web crawlers.
/// </para>
/// </remarks>
public bool AllowMappingHeadRequestsToGetHandler
{
get => _allowMappingHeadRequestsToGetHandler.Value;
set => _allowMappingHeadRequestsToGetHandler.Value = value;
}
/// <summary>
/// Gets or sets a value that determines if HTTP requests with the OPTIONS method are handled by default, if
/// no handler is available.
/// </summary>
/// <value>
/// The default value is <see langword="true"/>.
/// </value>
/// <remarks>
/// Razor Pages uses the current request's HTTP method to select a handler method. When no handler is available or selected,
/// the page is immediately executed. This may cause runtime errors if the page relies on the handler method to execute
/// and initialize some state. This setting attempts to avoid this class of error for HTTP <c>OPTIONS</c> requests by
/// returning a <c>200 OK</c> response.
/// </remarks>
public bool AllowDefaultHandlingForOptionsRequests
{
get => _allowsDefaultHandlingForOptionsRequests.Value;
set => _allowsDefaultHandlingForOptionsRequests.Value = value;
}
IEnumerator<ICompatibilitySwitch> IEnumerable<ICompatibilitySwitch>.GetEnumerator()
{
return ((IEnumerable<ICompatibilitySwitch>)_switches).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => _switches.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _switches.GetEnumerator();
} }

View File

@ -1,33 +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.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Mvc.RazorPages
{
internal class RazorPagesOptionsConfigureCompatibilityOptions : ConfigureCompatibilityOptions<RazorPagesOptions>
{
public RazorPagesOptionsConfigureCompatibilityOptions(
ILoggerFactory loggerFactory,
IOptions<MvcCompatibilityOptions> compatibilityOptions)
: base(loggerFactory, compatibilityOptions)
{
}
protected override IReadOnlyDictionary<string, object> DefaultValues
{
get
{
return new Dictionary<string, object>
{
[nameof(RazorPagesOptions.AllowAreas)] = true,
[nameof(RazorPagesOptions.AllowMappingHeadRequestsToGetHandler)] = true,
[nameof(RazorPagesOptions.AllowDefaultHandlingForOptionsRequests)] = true,
};
}
}
}
}

View File

@ -27,5 +27,25 @@
{ {
"TypeId": "public static class Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageDirectiveFeature", "TypeId": "public static class Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageDirectiveFeature",
"Kind": "Removal" "Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Mvc.RazorPages.RazorPagesOptions : System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Mvc.Infrastructure.ICompatibilitySwitch>",
"MemberId": "public System.Boolean get_AllowAreas()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Mvc.RazorPages.RazorPagesOptions : System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Mvc.Infrastructure.ICompatibilitySwitch>",
"MemberId": "public System.Boolean get_AllowMappingHeadRequestsToGetHandler()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Mvc.RazorPages.RazorPagesOptions : System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Mvc.Infrastructure.ICompatibilitySwitch>",
"MemberId": "public System.Void set_AllowAreas(System.Boolean value)",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Mvc.RazorPages.RazorPagesOptions : System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Mvc.Infrastructure.ICompatibilitySwitch>",
"MemberId": "public System.Void set_AllowMappingHeadRequestsToGetHandler(System.Boolean value)",
"Kind": "Removal"
} }
] ]

View File

@ -95,17 +95,24 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
// Underscores are fine characters in id's. // Underscores are fine characters in id's.
IdAttributeDotReplacement = optionsAccessor.Value.HtmlHelperOptions.IdAttributeDotReplacement; IdAttributeDotReplacement = optionsAccessor.Value.HtmlHelperOptions.IdAttributeDotReplacement;
AllowRenderingMaxLengthAttribute = optionsAccessor.Value.AllowRenderingMaxLengthAttribute;
} }
/// <summary> /// <summary>
/// Gets or sets a value that indicates whether the maxlength attribute should be rendered for compatible HTML input elements, /// Gets or sets a value that indicates whether the <c>maxlength</c> attribute should be rendered for
/// when they're bound to models marked with either /// compatible HTML input elements, when they're bound to models marked with either
/// <see cref="StringLengthAttribute"/> or <see cref="MaxLengthAttribute"/> attributes. /// <see cref="StringLengthAttribute"/> or <see cref="MaxLengthAttribute"/> attributes.
/// </summary> /// </summary>
/// <remarks>If both attributes are specified, the one with the smaller value will be used for the rendered `maxlength` attribute.</remarks> /// <value>The default value is <see langword="true"/>.</value>
protected bool AllowRenderingMaxLengthAttribute { get; } /// <remarks>
/// <para>
/// If both attributes are specified, the one with the smaller value will be used for the rendered
/// <c>maxlength</c> attribute.
/// </para>
/// <para>
/// This property is currently ignored.
/// </para>
/// </remarks>
protected bool AllowRenderingMaxLengthAttribute { get; } = true;
/// <inheritdoc /> /// <inheritdoc />
public string IdAttributeDotReplacement { get; } public string IdAttributeDotReplacement { get; }
@ -737,11 +744,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
} }
AddPlaceholderAttribute(viewContext.ViewData, tagBuilder, modelExplorer, expression); AddPlaceholderAttribute(viewContext.ViewData, tagBuilder, modelExplorer, expression);
if (AllowRenderingMaxLengthAttribute) AddMaxLengthAttribute(viewContext.ViewData, tagBuilder, modelExplorer, expression);
{
AddMaxLengthAttribute(viewContext.ViewData, tagBuilder, modelExplorer, expression);
}
AddValidationAttributes(viewContext, tagBuilder, modelExplorer, expression); AddValidationAttributes(viewContext, tagBuilder, modelExplorer, expression);
// If there are any errors for a named field, we add this CSS attribute. // If there are any errors for a named field, we add this CSS attribute.
@ -1257,7 +1260,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
AddPlaceholderAttribute(viewContext.ViewData, tagBuilder, modelExplorer, expression); AddPlaceholderAttribute(viewContext.ViewData, tagBuilder, modelExplorer, expression);
} }
if (AllowRenderingMaxLengthAttribute && _maxLengthInputTypes.Contains(suppliedTypeString)) if (_maxLengthInputTypes.Contains(suppliedTypeString))
{ {
AddMaxLengthAttribute(viewContext.ViewData, tagBuilder, modelExplorer, expression); AddMaxLengthAttribute(viewContext.ViewData, tagBuilder, modelExplorer, expression);
} }
@ -1397,7 +1400,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
} }
/// <summary> /// <summary>
/// Adds a maxlength attribute to the <paramref name="tagBuilder" />. /// Adds a <c>maxlength</c> attribute to the <paramref name="tagBuilder" />.
/// </summary> /// </summary>
/// <param name="viewData">A <see cref="ViewDataDictionary"/> instance for the current scope.</param> /// <param name="viewData">A <see cref="ViewDataDictionary"/> instance for the current scope.</param>
/// <param name="tagBuilder">A <see cref="TagBuilder"/> instance.</param> /// <param name="tagBuilder">A <see cref="TagBuilder"/> instance.</param>
@ -1552,8 +1555,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
} }
// Second check the Eval() call returned a collection of SelectListItems. // Second check the Eval() call returned a collection of SelectListItems.
var selectList = value as IEnumerable<SelectListItem>; if (!(value is IEnumerable<SelectListItem> selectList))
if (selectList == null)
{ {
throw new InvalidOperationException(Resources.FormatHtmlHelper_WrongSelectDataType( throw new InvalidOperationException(Resources.FormatHtmlHelper_WrongSelectDataType(
expression, expression,
@ -1606,8 +1608,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
IEnumerable<SelectListItem> selectList, IEnumerable<SelectListItem> selectList,
ICollection<string> currentValues) ICollection<string> currentValues)
{ {
var itemsList = selectList as IList<SelectListItem>; if (!(selectList is IList<SelectListItem> itemsList))
if (itemsList == null)
{ {
itemsList = selectList.ToList(); itemsList = selectList.ToList();
} }

View File

@ -143,8 +143,6 @@ namespace Microsoft.Extensions.DependencyInjection
services.TryAddEnumerable( services.TryAddEnumerable(
ServiceDescriptor.Transient<IConfigureOptions<MvcViewOptions>, MvcViewOptionsSetup>()); ServiceDescriptor.Transient<IConfigureOptions<MvcViewOptions>, MvcViewOptionsSetup>());
services.TryAddEnumerable(
ServiceDescriptor.Transient<IPostConfigureOptions<MvcViewOptions>, MvcViewOptionsConfigureCompatibilityOptions>());
services.TryAddEnumerable( services.TryAddEnumerable(
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, TempDataMvcOptionsSetup>()); ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, TempDataMvcOptionsSetup>());

View File

@ -85,10 +85,6 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Filters
List<LifecycleProperty> results = null; List<LifecycleProperty> results = null;
var propertyHelpers = PropertyHelper.GetVisibleProperties(type: type); var propertyHelpers = PropertyHelper.GetVisibleProperties(type: type);
var prefix = viewOptions.SuppressTempDataAttributePrefix ?
string.Empty :
"TempDataProperty-";
for (var i = 0; i < propertyHelpers.Length; i++) for (var i = 0; i < propertyHelpers.Length; i++)
{ {
var propertyHelper = propertyHelpers[i]; var propertyHelper = propertyHelpers[i];
@ -105,7 +101,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Filters
var key = tempDataAttribute.Key; var key = tempDataAttribute.Key;
if (string.IsNullOrEmpty(key)) if (string.IsNullOrEmpty(key))
{ {
key = prefix + property.Name; key = property.Name;
} }
results.Add(new LifecycleProperty(property, key)); results.Add(new LifecycleProperty(property, key));

View File

@ -4,7 +4,6 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation; using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.AspNetCore.Mvc.ViewEngines; using Microsoft.AspNetCore.Mvc.ViewEngines;
@ -17,22 +16,9 @@ namespace Microsoft.AspNetCore.Mvc
/// </summary> /// </summary>
public class MvcViewOptions : IEnumerable<ICompatibilitySwitch> public class MvcViewOptions : IEnumerable<ICompatibilitySwitch>
{ {
private readonly CompatibilitySwitch<bool> _suppressTempDataAttributePrefix; private readonly IReadOnlyList<ICompatibilitySwitch> _switches = Array.Empty<ICompatibilitySwitch>();
private readonly CompatibilitySwitch<bool> _allowRenderingMaxLengthAttribute;
private readonly ICompatibilitySwitch[] _switches;
private HtmlHelperOptions _htmlHelperOptions = new HtmlHelperOptions(); private HtmlHelperOptions _htmlHelperOptions = new HtmlHelperOptions();
public MvcViewOptions()
{
_suppressTempDataAttributePrefix = new CompatibilitySwitch<bool>(nameof(SuppressTempDataAttributePrefix));
_allowRenderingMaxLengthAttribute = new CompatibilitySwitch<bool>(nameof(AllowRenderingMaxLengthAttribute));
_switches = new[]
{
_suppressTempDataAttributePrefix,
_allowRenderingMaxLengthAttribute
};
}
/// <summary> /// <summary>
/// Gets or sets programmatic configuration for the HTML helpers and <see cref="Rendering.ViewContext"/>. /// Gets or sets programmatic configuration for the HTML helpers and <see cref="Rendering.ViewContext"/>.
/// </summary> /// </summary>
@ -50,45 +36,6 @@ namespace Microsoft.AspNetCore.Mvc
} }
} }
/// <summary>
/// <para>
/// Gets or sets a value that determines if the <see cref="ITempDataDictionary"/> keys for
/// properties annotated with <see cref="TempDataAttribute"/> include the prefix <c>TempDataProperty-</c>.
/// </para>
/// <para>
/// When <see cref="TempDataAttribute.Key"/> is not specified, the lookup key for properties annotated
/// with <see cref="TempDataAttribute"/> is derived from the property name. In releases prior to ASP.NET Core 2.1,
/// the calculated key was the property name prefixed by the value <c>TempDataProperty-</c>.
/// e.g. <c>TempDataProperty-SuccessMessage</c>. When this option is <c>true</c>, the calculated key for the property is
/// the property name e.g. <c>SuccessMessage</c>.
/// </para>
/// </summary>
/// <value>
/// The default value is <see langword="true"/>.
/// </value>
public bool SuppressTempDataAttributePrefix
{
get => _suppressTempDataAttributePrefix.Value;
set => _suppressTempDataAttributePrefix.Value = value;
}
/// <summary>
/// Gets or sets a value that indicates whether the maxlength attribute should be rendered for compatible HTML elements,
/// when they're bound to models marked with either
/// <see cref="StringLengthAttribute"/> or <see cref="MaxLengthAttribute"/> attributes.
/// </summary>
/// <value>
/// The default value is <see langword="true"/>.
/// </value>
/// <remarks>
/// If both attributes are specified, the one with the smaller value will be used for the rendered `maxlength` attribute.
/// </remarks>
public bool AllowRenderingMaxLengthAttribute
{
get => _allowRenderingMaxLengthAttribute.Value;
set => _allowRenderingMaxLengthAttribute.Value = value;
}
/// <summary> /// <summary>
/// Gets a list <see cref="IViewEngine"/>s used by this application. /// Gets a list <see cref="IViewEngine"/>s used by this application.
/// </summary> /// </summary>
@ -100,10 +47,7 @@ namespace Microsoft.AspNetCore.Mvc
public IList<IClientModelValidatorProvider> ClientModelValidatorProviders { get; } = public IList<IClientModelValidatorProvider> ClientModelValidatorProviders { get; } =
new List<IClientModelValidatorProvider>(); new List<IClientModelValidatorProvider>();
IEnumerator<ICompatibilitySwitch> IEnumerable<ICompatibilitySwitch>.GetEnumerator() IEnumerator<ICompatibilitySwitch> IEnumerable<ICompatibilitySwitch>.GetEnumerator() => _switches.GetEnumerator();
{
return ((IEnumerable<ICompatibilitySwitch>)_switches).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => _switches.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _switches.GetEnumerator();
} }

View File

@ -1,32 +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.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Mvc.ViewFeatures
{
internal class MvcViewOptionsConfigureCompatibilityOptions : ConfigureCompatibilityOptions<MvcViewOptions>
{
public MvcViewOptionsConfigureCompatibilityOptions(
ILoggerFactory loggerFactory,
IOptions<MvcCompatibilityOptions> compatibilityOptions)
: base(loggerFactory, compatibilityOptions)
{
}
protected override IReadOnlyDictionary<string, object> DefaultValues
{
get
{
return new Dictionary<string, object>
{
[nameof(MvcViewOptions.SuppressTempDataAttributePrefix)] = true,
[nameof(MvcViewOptions.AllowRenderingMaxLengthAttribute)] = true,
};
}
}
}
}

View File

@ -68,5 +68,15 @@
"TypeId": "public class Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor : Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor, Microsoft.AspNetCore.Mvc.Infrastructure.IActionResultExecutor<Microsoft.AspNetCore.Mvc.ViewResult>", "TypeId": "public class Microsoft.AspNetCore.Mvc.ViewFeatures.ViewResultExecutor : Microsoft.AspNetCore.Mvc.ViewFeatures.ViewExecutor, Microsoft.AspNetCore.Mvc.Infrastructure.IActionResultExecutor<Microsoft.AspNetCore.Mvc.ViewResult>",
"MemberId": "public .ctor(Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Mvc.MvcViewOptions> viewOptions, Microsoft.AspNetCore.Mvc.Infrastructure.IHttpResponseStreamWriterFactory writerFactory, Microsoft.AspNetCore.Mvc.ViewEngines.ICompositeViewEngine viewEngine, Microsoft.AspNetCore.Mvc.ViewFeatures.ITempDataDictionaryFactory tempDataFactory, System.Diagnostics.DiagnosticSource diagnosticSource, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory, Microsoft.AspNetCore.Mvc.ModelBinding.IModelMetadataProvider modelMetadataProvider)", "MemberId": "public .ctor(Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Mvc.MvcViewOptions> viewOptions, Microsoft.AspNetCore.Mvc.Infrastructure.IHttpResponseStreamWriterFactory writerFactory, Microsoft.AspNetCore.Mvc.ViewEngines.ICompositeViewEngine viewEngine, Microsoft.AspNetCore.Mvc.ViewFeatures.ITempDataDictionaryFactory tempDataFactory, System.Diagnostics.DiagnosticSource diagnosticSource, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory, Microsoft.AspNetCore.Mvc.ModelBinding.IModelMetadataProvider modelMetadataProvider)",
"Kind": "Removal" "Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Mvc.MvcViewOptions : System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Mvc.Infrastructure.ICompatibilitySwitch>",
"MemberId": "public System.Boolean get_SuppressTempDataAttributePrefix()",
"Kind": "Removal"
},
{
"TypeId": "public class Microsoft.AspNetCore.Mvc.MvcViewOptions : System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.Mvc.Infrastructure.ICompatibilitySwitch>",
"MemberId": "public System.Void set_SuppressTempDataAttributePrefix(System.Boolean value)",
"Kind": "Removal"
} }
] ]

View File

@ -195,11 +195,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
} }
// Throwing InputFormatterException // Throwing InputFormatterException
[Theory] [Fact]
[InlineData(InputFormatterExceptionPolicy.AllExceptions)] public async Task BindModel_CustomFormatter_ThrowingInputFormatterException_AddsErrorToModelState()
[InlineData(InputFormatterExceptionPolicy.MalformedInputExceptions)]
public async Task BindModel_CustomFormatter_ThrowingInputFormatterException_AddsErrorToModelState(
InputFormatterExceptionPolicy inputFormatterExceptionPolicy)
{ {
// Arrange // Arrange
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
@ -215,12 +212,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
{ {
throw new InputFormatterException("Bad input!!", expectedFormatException); throw new InputFormatterException("Bad input!!", expectedFormatException);
}); });
var binder = CreateBinder( var binder = CreateBinder(new[] { formatter }, new MvcOptions());
new[] { formatter },
new MvcOptions()
{
InputFormatterExceptionPolicy = inputFormatterExceptionPolicy
});
// Act // Act
await binder.BindModelAsync(bindingContext); await binder.BindModelAsync(bindingContext);
@ -237,16 +229,14 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
Assert.Null(entry.Value.Errors[0].Exception); Assert.Null(entry.Value.Errors[0].Exception);
} }
public static TheoryData<IInputFormatter, InputFormatterExceptionPolicy> BuiltInFormattersThrowingInputFormatterException public static TheoryData<IInputFormatter> BuiltInFormattersThrowingInputFormatterException
{ {
get get
{ {
return new TheoryData<IInputFormatter, InputFormatterExceptionPolicy>() return new TheoryData<IInputFormatter>()
{ {
{ new XmlSerializerInputFormatter(new MvcOptions()), InputFormatterExceptionPolicy.AllExceptions }, { new XmlSerializerInputFormatter(new MvcOptions()) },
{ new XmlSerializerInputFormatter(new MvcOptions()), InputFormatterExceptionPolicy.MalformedInputExceptions }, { new XmlDataContractSerializerInputFormatter(new MvcOptions()) },
{ new XmlDataContractSerializerInputFormatter(new MvcOptions()), InputFormatterExceptionPolicy.AllExceptions },
{ new XmlDataContractSerializerInputFormatter(new MvcOptions()), InputFormatterExceptionPolicy.MalformedInputExceptions },
}; };
} }
} }
@ -254,8 +244,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
[Theory] [Theory]
[MemberData(nameof(BuiltInFormattersThrowingInputFormatterException))] [MemberData(nameof(BuiltInFormattersThrowingInputFormatterException))]
public async Task BindModel_BuiltInXmlInputFormatters_ThrowingInputFormatterException_AddsErrorToModelState( public async Task BindModel_BuiltInXmlInputFormatters_ThrowingInputFormatterException_AddsErrorToModelState(
IInputFormatter formatter, IInputFormatter formatter)
InputFormatterExceptionPolicy inputFormatterExceptionPolicy)
{ {
// Arrange // Arrange
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
@ -266,10 +255,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
metadataProvider.ForType<Person>().BindingDetails(d => d.BindingSource = BindingSource.Body); metadataProvider.ForType<Person>().BindingDetails(d => d.BindingSource = BindingSource.Body);
var bindingContext = GetBindingContext(typeof(Person), httpContext, metadataProvider); var bindingContext = GetBindingContext(typeof(Person), httpContext, metadataProvider);
var binder = CreateBinder(new[] { formatter }, new MvcOptions() var binder = CreateBinder(new[] { formatter }, new MvcOptions());
{
InputFormatterExceptionPolicy = inputFormatterExceptionPolicy
});
// Act // Act
await binder.BindModelAsync(bindingContext); await binder.BindModelAsync(bindingContext);
@ -286,11 +272,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
Assert.Null(entry.Value.Errors[0].Exception); Assert.Null(entry.Value.Errors[0].Exception);
} }
[Theory] [Fact]
[InlineData(InputFormatterExceptionPolicy.AllExceptions)] public async Task BindModel_BuiltInJsonInputFormatter_ThrowingInputFormatterException_AddsErrorToModelState()
[InlineData(InputFormatterExceptionPolicy.MalformedInputExceptions)]
public async Task BindModel_BuiltInJsonInputFormatter_ThrowingInputFormatterException_AddsErrorToModelState(
InputFormatterExceptionPolicy inputFormatterExceptionPolicy)
{ {
// Arrange // Arrange
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
@ -303,10 +286,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
var bindingContext = GetBindingContext(typeof(Person), httpContext, metadataProvider); var bindingContext = GetBindingContext(typeof(Person), httpContext, metadataProvider);
var binder = CreateBinder( var binder = CreateBinder(
new[] { new TestableJsonInputFormatter(throwNonInputFormatterException: false) }, new[] { new TestableJsonInputFormatter(throwNonInputFormatterException: false) },
new MvcOptions() new MvcOptions());
{
InputFormatterExceptionPolicy = inputFormatterExceptionPolicy
});
// Act // Act
await binder.BindModelAsync(bindingContext); await binder.BindModelAsync(bindingContext);
@ -321,25 +301,21 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
Assert.NotEmpty(entry.Value.Errors[0].ErrorMessage); Assert.NotEmpty(entry.Value.Errors[0].ErrorMessage);
} }
public static TheoryData<IInputFormatter, InputFormatterExceptionPolicy> DerivedFormattersThrowingInputFormatterException public static TheoryData<IInputFormatter> DerivedFormattersThrowingInputFormatterException
{ {
get get
{ {
return new TheoryData<IInputFormatter, InputFormatterExceptionPolicy>() return new TheoryData<IInputFormatter>()
{ {
{ new DerivedXmlSerializerInputFormatter(throwNonInputFormatterException: false), InputFormatterExceptionPolicy.AllExceptions }, { new DerivedXmlSerializerInputFormatter(throwNonInputFormatterException: false) },
{ new DerivedXmlSerializerInputFormatter(throwNonInputFormatterException: false), InputFormatterExceptionPolicy.MalformedInputExceptions }, { new DerivedXmlDataContractSerializerInputFormatter(throwNonInputFormatterException: false) },
{ new DerivedXmlDataContractSerializerInputFormatter(throwNonInputFormatterException: false), InputFormatterExceptionPolicy.AllExceptions },
{ new DerivedXmlDataContractSerializerInputFormatter(throwNonInputFormatterException: false), InputFormatterExceptionPolicy.MalformedInputExceptions },
}; };
} }
} }
[Theory] [Theory]
[MemberData(nameof(DerivedFormattersThrowingInputFormatterException))] [MemberData(nameof(DerivedFormattersThrowingInputFormatterException))]
public async Task BindModel_DerivedXmlInputFormatters_AddsErrorToModelState_( public async Task BindModel_DerivedXmlInputFormatters_AddsErrorToModelState(IInputFormatter formatter)
IInputFormatter formatter,
InputFormatterExceptionPolicy inputFormatterExceptionPolicy)
{ {
// Arrange // Arrange
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
@ -350,10 +326,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
metadataProvider.ForType<Person>().BindingDetails(d => d.BindingSource = BindingSource.Body); metadataProvider.ForType<Person>().BindingDetails(d => d.BindingSource = BindingSource.Body);
var bindingContext = GetBindingContext(typeof(Person), httpContext, metadataProvider); var bindingContext = GetBindingContext(typeof(Person), httpContext, metadataProvider);
var binder = CreateBinder(new[] { formatter }, new MvcOptions() var binder = CreateBinder(new[] { formatter }, new MvcOptions());
{
InputFormatterExceptionPolicy = inputFormatterExceptionPolicy
});
// Act // Act
await binder.BindModelAsync(bindingContext); await binder.BindModelAsync(bindingContext);
@ -370,11 +343,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
Assert.Null(entry.Value.Errors[0].Exception); Assert.Null(entry.Value.Errors[0].Exception);
} }
[Theory] [Fact]
[InlineData(InputFormatterExceptionPolicy.AllExceptions)] public async Task BindModel_DerivedJsonInputFormatter_AddsErrorToModelState()
[InlineData(InputFormatterExceptionPolicy.MalformedInputExceptions)]
public async Task BindModel_DerivedJsonInputFormatter_AddsErrorToModelState(
InputFormatterExceptionPolicy inputFormatterExceptionPolicy)
{ {
// Arrange // Arrange
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
@ -387,10 +357,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
var bindingContext = GetBindingContext(typeof(Person), httpContext, metadataProvider); var bindingContext = GetBindingContext(typeof(Person), httpContext, metadataProvider);
var binder = CreateBinder( var binder = CreateBinder(
new[] { new DerivedJsonInputFormatter(throwNonInputFormatterException: false) }, new[] { new DerivedJsonInputFormatter(throwNonInputFormatterException: false) },
new MvcOptions() new MvcOptions());
{
InputFormatterExceptionPolicy = inputFormatterExceptionPolicy
});
// Act // Act
await binder.BindModelAsync(bindingContext); await binder.BindModelAsync(bindingContext);
@ -407,18 +374,15 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
} }
// Throwing Non-InputFormatterException // Throwing Non-InputFormatterException
public static TheoryData<IInputFormatter, string, InputFormatterExceptionPolicy> BuiltInFormattersThrowingNonInputFormatterException public static TheoryData<IInputFormatter, string> BuiltInFormattersThrowingNonInputFormatterException
{ {
get get
{ {
return new TheoryData<IInputFormatter, string, InputFormatterExceptionPolicy>() return new TheoryData<IInputFormatter, string>()
{ {
{ new TestableXmlSerializerInputFormatter(throwNonInputFormatterException: true), "text/xml", InputFormatterExceptionPolicy.AllExceptions }, { new TestableXmlSerializerInputFormatter(throwNonInputFormatterException: true), "text/xml" },
{ new TestableXmlSerializerInputFormatter(throwNonInputFormatterException: true), "text/xml", InputFormatterExceptionPolicy.MalformedInputExceptions }, { new TestableXmlDataContractSerializerInputFormatter(throwNonInputFormatterException: true), "text/xml" },
{ new TestableXmlDataContractSerializerInputFormatter(throwNonInputFormatterException: true), "text/xml", InputFormatterExceptionPolicy.AllExceptions }, { new TestableJsonInputFormatter(throwNonInputFormatterException: true), "text/json" },
{ new TestableXmlDataContractSerializerInputFormatter(throwNonInputFormatterException: true), "text/xml", InputFormatterExceptionPolicy.MalformedInputExceptions },
{ new TestableJsonInputFormatter(throwNonInputFormatterException: true), "text/json", InputFormatterExceptionPolicy.AllExceptions },
{ new TestableJsonInputFormatter(throwNonInputFormatterException: true), "text/json", InputFormatterExceptionPolicy.MalformedInputExceptions },
}; };
} }
} }
@ -427,8 +391,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
[MemberData(nameof(BuiltInFormattersThrowingNonInputFormatterException))] [MemberData(nameof(BuiltInFormattersThrowingNonInputFormatterException))]
public async Task BindModel_BuiltInInputFormatters_ThrowingNonInputFormatterException_Throws( public async Task BindModel_BuiltInInputFormatters_ThrowingNonInputFormatterException_Throws(
IInputFormatter formatter, IInputFormatter formatter,
string contentType, string contentType)
InputFormatterExceptionPolicy inputFormatterExceptionPolicy)
{ {
// Arrange // Arrange
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
@ -439,28 +402,22 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
metadataProvider.ForType<Person>().BindingDetails(d => d.BindingSource = BindingSource.Body); metadataProvider.ForType<Person>().BindingDetails(d => d.BindingSource = BindingSource.Body);
var bindingContext = GetBindingContext(typeof(Person), httpContext, metadataProvider); var bindingContext = GetBindingContext(typeof(Person), httpContext, metadataProvider);
var binder = CreateBinder(new[] { formatter }, new MvcOptions() var binder = CreateBinder(new[] { formatter }, new MvcOptions());
{
InputFormatterExceptionPolicy = inputFormatterExceptionPolicy
});
// Act & Assert // Act & Assert
var exception = await Assert.ThrowsAsync<IOException>(() => binder.BindModelAsync(bindingContext)); var exception = await Assert.ThrowsAsync<IOException>(() => binder.BindModelAsync(bindingContext));
Assert.Equal("Unable to read input stream!!", exception.Message); Assert.Equal("Unable to read input stream!!", exception.Message);
} }
public static TheoryData<IInputFormatter, string, InputFormatterExceptionPolicy> DerivedInputFormattersThrowingNonInputFormatterException public static TheoryData<IInputFormatter, string> DerivedInputFormattersThrowingNonInputFormatterException
{ {
get get
{ {
return new TheoryData<IInputFormatter, string, InputFormatterExceptionPolicy>() return new TheoryData<IInputFormatter, string>()
{ {
{ new DerivedXmlSerializerInputFormatter(throwNonInputFormatterException: true), "text/xml", InputFormatterExceptionPolicy.AllExceptions }, { new DerivedXmlSerializerInputFormatter(throwNonInputFormatterException: true), "text/xml" },
{ new DerivedXmlSerializerInputFormatter(throwNonInputFormatterException: true), "text/xml", InputFormatterExceptionPolicy.MalformedInputExceptions }, { new DerivedXmlDataContractSerializerInputFormatter(throwNonInputFormatterException: true), "text/xml" },
{ new DerivedXmlDataContractSerializerInputFormatter(throwNonInputFormatterException: true), "text/xml", InputFormatterExceptionPolicy.AllExceptions }, { new DerivedJsonInputFormatter(throwNonInputFormatterException: true), "text/json" },
{ new DerivedXmlDataContractSerializerInputFormatter(throwNonInputFormatterException: true), "text/xml", InputFormatterExceptionPolicy.MalformedInputExceptions },
{ new DerivedJsonInputFormatter(throwNonInputFormatterException: true), "text/json", InputFormatterExceptionPolicy.AllExceptions },
{ new DerivedJsonInputFormatter(throwNonInputFormatterException: true), "text/json", InputFormatterExceptionPolicy.MalformedInputExceptions },
}; };
} }
} }
@ -469,8 +426,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
[MemberData(nameof(DerivedInputFormattersThrowingNonInputFormatterException))] [MemberData(nameof(DerivedInputFormattersThrowingNonInputFormatterException))]
public async Task BindModel_DerivedXmlInputFormatters_ThrowingNonInputFormattingException_AddsErrorToModelState( public async Task BindModel_DerivedXmlInputFormatters_ThrowingNonInputFormattingException_AddsErrorToModelState(
IInputFormatter formatter, IInputFormatter formatter,
string contentType, string contentType)
InputFormatterExceptionPolicy inputFormatterExceptionPolicy)
{ {
// Arrange // Arrange
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
@ -481,10 +437,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
metadataProvider.ForType<Person>().BindingDetails(d => d.BindingSource = BindingSource.Body); metadataProvider.ForType<Person>().BindingDetails(d => d.BindingSource = BindingSource.Body);
var bindingContext = GetBindingContext(typeof(Person), httpContext, metadataProvider); var bindingContext = GetBindingContext(typeof(Person), httpContext, metadataProvider);
var binder = CreateBinder(new[] { formatter }, new MvcOptions() var binder = CreateBinder(new[] { formatter }, new MvcOptions());
{
InputFormatterExceptionPolicy = inputFormatterExceptionPolicy
});
// Act // Act
await binder.BindModelAsync(bindingContext); await binder.BindModelAsync(bindingContext);
@ -517,12 +470,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
{ {
throw new IOException("Unable to read input stream!!"); throw new IOException("Unable to read input stream!!");
}); });
var binder = CreateBinder( var binder = CreateBinder(new[] { formatter }, new MvcOptions());
new[] { formatter },
new MvcOptions()
{
InputFormatterExceptionPolicy = InputFormatterExceptionPolicy.MalformedInputExceptions
});
// Act // Act
var exception = await Assert.ThrowsAsync<IOException>( var exception = await Assert.ThrowsAsync<IOException>(
@ -530,44 +478,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
Assert.Equal("Unable to read input stream!!", exception.Message); Assert.Equal("Unable to read input stream!!", exception.Message);
} }
[Fact]
public async Task BindModel_CustomFormatter_ThrowingNonInputFormatterException_AddsErrorToModelState()
{
// Arrange
var httpContext = new DefaultHttpContext();
httpContext.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes("Bad data!"));
httpContext.Request.ContentType = "text/xyz";
var metadataProvider = new TestModelMetadataProvider();
metadataProvider.ForType<Person>().BindingDetails(d => d.BindingSource = BindingSource.Body);
var bindingContext = GetBindingContext(typeof(Person), httpContext, metadataProvider);
var formatter = new XyzFormatter((inputFormatterContext, encoding) =>
{
throw new IOException("Unable to read input stream!!");
});
var binder = CreateBinder(
new[] { formatter },
new MvcOptions()
{
InputFormatterExceptionPolicy = InputFormatterExceptionPolicy.AllExceptions
});
// Act
await binder.BindModelAsync(bindingContext);
// Assert
Assert.False(bindingContext.Result.IsModelSet);
Assert.Null(bindingContext.Result.Model);
// Key is the empty string because this was a top-level binding.
var entry = Assert.Single(bindingContext.ModelState);
Assert.Equal(string.Empty, entry.Key);
var errorMessage = Assert.Single(entry.Value.Errors).Exception.Message;
Assert.Equal("Unable to read input stream!!", errorMessage);
Assert.IsType<IOException>(entry.Value.Errors[0].Exception);
}
[Fact] [Fact]
public async Task NullFormatterError_AddedToModelState() public async Task NullFormatterError_AddedToModelState()
{ {

View File

@ -286,6 +286,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
bool isBindingRequired) bool isBindingRequired)
{ {
// Arrange // Arrange
var expectedErrorCount = isBindingRequired ? 1 : 0;
var mockValueProvider = new Mock<IValueProvider>(); var mockValueProvider = new Mock<IValueProvider>();
mockValueProvider mockValueProvider
.Setup(o => o.ContainsPrefix(It.IsAny<string>())) .Setup(o => o.ContainsPrefix(It.IsAny<string>()))
@ -305,7 +306,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
var bindingContext = new DefaultModelBindingContext var bindingContext = new DefaultModelBindingContext
{ {
IsTopLevelObject = true, IsTopLevelObject = true,
ModelMetadata = GetMetadataForType(typeof(Person)), ModelMetadata = metadata,
ModelName = string.Empty, ModelName = string.Empty,
ValueProvider = mockValueProvider.Object, ValueProvider = mockValueProvider.Object,
ModelState = new ModelStateDictionary(), ModelState = new ModelStateDictionary(),
@ -330,7 +331,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
// Assert // Assert
Assert.True(bindingContext.Result.IsModelSet); Assert.True(bindingContext.Result.IsModelSet);
Assert.Equal(0, bindingContext.ModelState.ErrorCount); Assert.Equal(expectedErrorCount, bindingContext.ModelState.ErrorCount);
var returnedPerson = Assert.IsType<Person>(bindingContext.Result.Model); var returnedPerson = Assert.IsType<Person>(bindingContext.Result.Model);
Assert.Same(model, returnedPerson); Assert.Same(model, returnedPerson);
@ -1115,8 +1116,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
var type = model.GetType(); var type = model.GetType();
var bindingContext = CreateContext(GetMetadataForType(type), model); var bindingContext = CreateContext(GetMetadataForType(type), model);
var modelState = bindingContext.ModelState; var modelState = bindingContext.ModelState;
var metadata = GetMetadataForType(type);
var propertyMetadata = bindingContext.ModelMetadata.Properties[propertyName]; var propertyMetadata = bindingContext.ModelMetadata.Properties[propertyName];
var result = ModelBindingResult.Success(new Simple { Name = "Hanna" }); var result = ModelBindingResult.Success(new Simple { Name = "Hanna" });
@ -1160,8 +1159,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
// Arrange // Arrange
var model = new Person(); var model = new Person();
var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model); var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
var metadata = GetMetadataForType(typeof(Person));
var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.DateOfBirth)]; var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.DateOfBirth)];
var result = ModelBindingResult.Success(new DateTime(2001, 1, 1)); var result = ModelBindingResult.Success(new DateTime(2001, 1, 1));
@ -1186,8 +1183,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
}; };
var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model); var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
var metadata = GetMetadataForType(typeof(Person));
var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.DateOfDeath)]; var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.DateOfDeath)];
var result = ModelBindingResult.Success(new DateTime(1800, 1, 1)); var result = ModelBindingResult.Success(new DateTime(1800, 1, 1));
@ -1210,8 +1205,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
var model = new ModelWhosePropertySetterThrows(); var model = new ModelWhosePropertySetterThrows();
var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model); var bindingContext = CreateContext(GetMetadataForType(model.GetType()), model);
bindingContext.ModelName = "foo"; bindingContext.ModelName = "foo";
var metadata = GetMetadataForType(typeof(ModelWhosePropertySetterThrows));
var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.NameNoAttribute)]; var propertyMetadata = bindingContext.ModelMetadata.Properties[nameof(model.NameNoAttribute)];
var result = ModelBindingResult.Success(model: null); var result = ModelBindingResult.Success(model: null);

View File

@ -12,21 +12,12 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
public class EnumTypeModelBinderTest public class EnumTypeModelBinderTest
{ {
[Theory] [Theory]
[InlineData(true, typeof(IntEnum?))] [InlineData(typeof(IntEnum?))]
[InlineData(true, typeof(FlagsEnum?))] [InlineData(typeof(FlagsEnum?))]
[InlineData(false, typeof(IntEnum?))] public async Task BindModel_SetsModel_ForEmptyValue_AndNullableEnumTypes(Type modelType)
[InlineData(false, typeof(FlagsEnum?))]
public async Task BindModel_SetsModel_ForEmptyValue_AndNullableEnumTypes(
bool suppressBindingUndefinedValueToEnumType,
Type modelType)
{ {
// Arrange // Arrange
var binderInfo = GetBinderAndContext( var (bindingContext, binder) = GetBinderAndContext(modelType, valueProviderValue: "");
modelType,
suppressBindingUndefinedValueToEnumType,
valueProviderValue: "");
var bindingContext = binderInfo.Item1;
var binder = binderInfo.Item2;
// Act // Act
await binder.BindModelAsync(bindingContext); await binder.BindModelAsync(bindingContext);
@ -37,22 +28,13 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
} }
[Theory] [Theory]
[InlineData(true, typeof(IntEnum))] [InlineData(typeof(IntEnum))]
[InlineData(true, typeof(FlagsEnum))] [InlineData(typeof(FlagsEnum))]
[InlineData(false, typeof(IntEnum))] public async Task BindModel_AddsErrorToModelState_ForEmptyValue_AndNonNullableEnumTypes(Type modelType)
[InlineData(false, typeof(FlagsEnum))]
public async Task BindModel_AddsErrorToModelState_ForEmptyValue_AndNonNullableEnumTypes(
bool suppressBindingUndefinedValueToEnumType,
Type modelType)
{ {
// Arrange // Arrange
var message = "The value '' is invalid."; var message = "The value '' is invalid.";
var binderInfo = GetBinderAndContext( var (bindingContext, binder) = GetBinderAndContext(modelType, valueProviderValue: "");
modelType,
suppressBindingUndefinedValueToEnumType,
valueProviderValue: "");
var bindingContext = binderInfo.Item1;
var binder = binderInfo.Item2;
// Act // Act
await binder.BindModelAsync(bindingContext); await binder.BindModelAsync(bindingContext);
@ -66,22 +48,15 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
} }
[Theory] [Theory]
[InlineData(true, "Value1")] [InlineData("Value1")]
[InlineData(true, "1")] [InlineData("1")]
[InlineData(false, "Value1")] public async Task BindModel_BindsEnumModels_ForValuesInArray(string enumValue)
[InlineData(false, "1")]
public async Task BindModel_BindsEnumModels_ForValuesInArray(
bool suppressBindingUndefinedValueToEnumType,
string enumValue)
{ {
// Arrange // Arrange
var modelType = typeof(IntEnum); var modelType = typeof(IntEnum);
var binderInfo = GetBinderAndContext( var (bindingContext, binder) = GetBinderAndContext(
modelType, modelType,
suppressBindingUndefinedValueToEnumType,
valueProviderValue: new object[] { enumValue }); valueProviderValue: new object[] { enumValue });
var bindingContext = binderInfo.Item1;
var binder = binderInfo.Item2;
// Act // Act
await binder.BindModelAsync(bindingContext); await binder.BindModelAsync(bindingContext);
@ -93,26 +68,19 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
} }
[Theory] [Theory]
[InlineData("1", true)] [InlineData("1")]
[InlineData("8, 1", true)] [InlineData("8, 1")]
[InlineData("Value2, Value8", true)] [InlineData("Value2, Value8")]
[InlineData("value8,value4,value2,value1", true)] [InlineData("value8,value4,value2,value1")]
[InlineData("1", false)] public async Task BindModel_BindsTo_NonNullableFlagsEnumType(string flagsEnumValue)
[InlineData("8, 1", false)]
[InlineData("Value2, Value8", false)]
[InlineData("value8,value4,value2,value1", false)]
public async Task BindModel_BindsTo_NonNullableFlagsEnumType(string flagsEnumValue, bool suppressBindingUndefinedValueToEnumType)
{ {
// Arrange // Arrange
var modelType = typeof(FlagsEnum); var modelType = typeof(FlagsEnum);
var enumConverter = TypeDescriptor.GetConverter(modelType); var enumConverter = TypeDescriptor.GetConverter(modelType);
var expected = enumConverter.ConvertFrom(flagsEnumValue).ToString(); var expected = enumConverter.ConvertFrom(flagsEnumValue).ToString();
var binderInfo = GetBinderAndContext( var (bindingContext, binder) = GetBinderAndContext(
modelType, modelType,
suppressBindingUndefinedValueToEnumType,
valueProviderValue: new object[] { flagsEnumValue }); valueProviderValue: new object[] { flagsEnumValue });
var bindingContext = binderInfo.Item1;
var binder = binderInfo.Item2;
// Act // Act
await binder.BindModelAsync(bindingContext); await binder.BindModelAsync(bindingContext);
@ -124,26 +92,19 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
} }
[Theory] [Theory]
[InlineData("1", true)] [InlineData("1")]
[InlineData("8, 1", true)] [InlineData("8, 1")]
[InlineData("Value2, Value8", true)] [InlineData("Value2, Value8")]
[InlineData("value8,value4,value2,value1", true)] [InlineData("value8,value4,value2,value1")]
[InlineData("1", false)] public async Task BindModel_BindsTo_NullableFlagsEnumType(string flagsEnumValue)
[InlineData("8, 1", false)]
[InlineData("Value2, Value8", false)]
[InlineData("value8,value4,value2,value1", false)]
public async Task BindModel_BindsTo_NullableFlagsEnumType(string flagsEnumValue, bool suppressBindingUndefinedValueToEnumType)
{ {
// Arrange // Arrange
var modelType = typeof(FlagsEnum?); var modelType = typeof(FlagsEnum?);
var enumConverter = TypeDescriptor.GetConverter(GetUnderlyingType(modelType)); var enumConverter = TypeDescriptor.GetConverter(GetUnderlyingType(modelType));
var expected = enumConverter.ConvertFrom(flagsEnumValue).ToString(); var expected = enumConverter.ConvertFrom(flagsEnumValue).ToString();
var binderInfo = GetBinderAndContext( var (bindingContext, binder) = GetBinderAndContext(
modelType, modelType,
suppressBindingUndefinedValueToEnumType,
valueProviderValue: new object[] { flagsEnumValue }); valueProviderValue: new object[] { flagsEnumValue });
var bindingContext = binderInfo.Item1;
var binder = binderInfo.Item2;
// Act // Act
await binder.BindModelAsync(bindingContext); await binder.BindModelAsync(bindingContext);
@ -154,36 +115,6 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
Assert.Equal(expected, boundModel.ToString()); Assert.Equal(expected, boundModel.ToString());
} }
[Theory]
[InlineData(typeof(FlagsEnum), "Value10")]
[InlineData(typeof(FlagsEnum), "Value1, Value10")]
[InlineData(typeof(FlagsEnum), "value10, value1")]
[InlineData(typeof(FlagsEnum?), "Value10")]
[InlineData(typeof(FlagsEnum?), "Value1, Value10")]
[InlineData(typeof(FlagsEnum?), "value10, value1")]
public async Task BindModel_AddsErrorToModelState_ForInvalidEnumValues_IsNotValidMessage(Type modelType, string suppliedValue)
{
// Arrange
var message = $"The value '{suppliedValue}' is not valid.";
var binderInfo = GetBinderAndContext(
modelType,
suppressBindingUndefinedValueToEnumType: false,
valueProviderValue: new object[] { suppliedValue });
var bindingContext = binderInfo.Item1;
var binder = binderInfo.Item2;
// Act
await binder.BindModelAsync(bindingContext);
// Assert
Assert.False(bindingContext.Result.IsModelSet);
Assert.Null(bindingContext.Result.Model);
Assert.False(bindingContext.ModelState.IsValid);
var error = Assert.Single(bindingContext.ModelState["theModelName"].Errors);
Assert.Equal(message, error.ErrorMessage);
}
[Theory] [Theory]
[InlineData(typeof(IntEnum), "")] [InlineData(typeof(IntEnum), "")]
[InlineData(typeof(IntEnum), "3")] [InlineData(typeof(IntEnum), "3")]
@ -206,12 +137,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
{ {
// Arrange // Arrange
var message = $"The value '{suppliedValue}' is invalid."; var message = $"The value '{suppliedValue}' is invalid.";
var binderInfo = GetBinderAndContext( var (bindingContext, binder) = GetBinderAndContext(
modelType, modelType,
suppressBindingUndefinedValueToEnumType: true,
valueProviderValue: new object[] { suppliedValue }); valueProviderValue: new object[] { suppliedValue });
var bindingContext = binderInfo.Item1;
var binder = binderInfo.Item2;
// Act // Act
await binder.BindModelAsync(bindingContext); await binder.BindModelAsync(bindingContext);
@ -224,49 +152,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
Assert.Equal(message, error.ErrorMessage); Assert.Equal(message, error.ErrorMessage);
} }
[Theory]
[InlineData(typeof(IntEnum), "3", 3)]
[InlineData(typeof(FlagsEnum), "19", 19)]
[InlineData(typeof(FlagsEnum), "0", 0)]
[InlineData(typeof(FlagsEnum), "1, 16", 17)]
// These two values look like big integers but are treated as two separate enum values that are
// or'd together.
[InlineData(typeof(FlagsEnum), "32,015", 47)]
[InlineData(typeof(FlagsEnum), "32,128", 160)]
[InlineData(typeof(IntEnum?), "3", 3)]
[InlineData(typeof(FlagsEnum?), "19", 19)]
[InlineData(typeof(FlagsEnum?), "0", 0)]
[InlineData(typeof(FlagsEnum?), "1, 16", 17)]
// These two values look like big integers but are treated as two separate enum values that are
// or'd together.
[InlineData(typeof(FlagsEnum?), "32,015", 47)]
[InlineData(typeof(FlagsEnum?), "32,128", 160)]
public async Task BindModel_AllowsBindingUndefinedValues_ToEnumTypes(
Type modelType,
string suppliedValue,
long expected)
{
// Arrange
var binderProviderContext = new TestModelBinderProviderContext(modelType);
var binderInfo = GetBinderAndContext(
modelType,
suppressBindingUndefinedValueToEnumType: false,
valueProviderValue: new object[] { suppliedValue });
var bindingContext = binderInfo.Item1;
var binder = binderInfo.Item2;
// Act
await binder.BindModelAsync(bindingContext);
// Assert
Assert.True(bindingContext.Result.IsModelSet);
Assert.IsType(GetUnderlyingType(modelType), bindingContext.Result.Model);
Assert.Equal(expected, Convert.ToInt64(bindingContext.Result.Model));
}
private static (DefaultModelBindingContext, IModelBinder) GetBinderAndContext( private static (DefaultModelBindingContext, IModelBinder) GetBinderAndContext(
Type modelType, Type modelType,
bool suppressBindingUndefinedValueToEnumType,
object valueProviderValue) object valueProviderValue)
{ {
var binderProviderContext = new TestModelBinderProviderContext(modelType); var binderProviderContext = new TestModelBinderProviderContext(modelType);
@ -281,10 +168,9 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
{ modelName, valueProviderValue } { modelName, valueProviderValue }
} }
}; };
var binderProvider = new EnumTypeModelBinderProvider(new MvcOptions
{ var binderProvider = new EnumTypeModelBinderProvider(new MvcOptions());
SuppressBindingUndefinedValueToEnumType = suppressBindingUndefinedValueToEnumType
});
var binder = binderProvider.GetBinder(binderProviderContext); var binder = binderProvider.GetBinder(binderProviderContext);
return (bindingContext, binder); return (bindingContext, binder);
} }

View File

@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
{ {
public class DefaultObjectValidatorTests public class DefaultObjectValidatorTests
{ {
private readonly MvcOptions _options = new MvcOptions { AllowShortCircuitingValidationWhenNoValidatorsArePresent = true }; private readonly MvcOptions _options = new MvcOptions();
private ModelMetadataProvider MetadataProvider { get; } = TestModelMetadataProvider.CreateDefaultProvider(); private ModelMetadataProvider MetadataProvider { get; } = TestModelMetadataProvider.CreateDefaultProvider();

View File

@ -275,53 +275,6 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
Assert.Equal("DisplayNameAttributeValue", context.DisplayMetadata.DisplayName()); Assert.Equal("DisplayNameAttributeValue", context.DisplayMetadata.DisplayName());
} }
[Fact]
public void CreateDisplayMetadata_DisplayNameAttribute_OnEnum_CompatSwitchWorks()
{
// Arrange
var unsharedLocalizer = new Mock<IStringLocalizer>(MockBehavior.Strict);
unsharedLocalizer
.Setup(s => s["DisplayNameValue"])
.Returns(new LocalizedString("DisplaynameValue", "didn't use shared"));
var sharedLocalizer = new Mock<IStringLocalizer>(MockBehavior.Strict);
sharedLocalizer
.Setup(s => s["DisplayNameValue"])
.Returns(() => new LocalizedString("DisplayNameValue", "used shared"));
var stringLocalizerFactoryMock = new Mock<IStringLocalizerFactory>(MockBehavior.Strict);
stringLocalizerFactoryMock
.Setup(s => s.Create(typeof(TestEnum)))
.Returns(() => unsharedLocalizer.Object);
stringLocalizerFactoryMock
.Setup(s => s.Create(typeof(EmptyClass)))
.Returns(() => sharedLocalizer.Object);
var localizationOptions = Options.Create(new MvcDataAnnotationsLocalizationOptions());
localizationOptions.Value.AllowDataAnnotationsLocalizationForEnumDisplayAttributes = false;
localizationOptions.Value.DataAnnotationLocalizerProvider = (type, stringLocalizerFactory) =>
{
return stringLocalizerFactory.Create(typeof(EmptyClass));
};
var provider = new DataAnnotationsMetadataProvider(
localizationOptions,
stringLocalizerFactory: stringLocalizerFactoryMock.Object);
var displayName = new DisplayNameAttribute("DisplayNameValue");
var attributes = new Attribute[] { displayName };
var key = ModelMetadataIdentity.ForType(typeof(TestEnum));
var context = new DisplayMetadataProviderContext(key, GetModelAttributes(attributes));
// Act
provider.CreateDisplayMetadata(context);
// Assert
Assert.Collection(context.DisplayMetadata.EnumGroupedDisplayNamesAndValues,
(e) => Assert.Equal("didn't use shared", e.Key.Name));
}
[Fact] [Fact]
public void CreateDisplayMetadata_DisplayNameAttribute_OnEnum_CompatShimOn() public void CreateDisplayMetadata_DisplayNameAttribute_OnEnum_CompatShimOn()
{ {
@ -337,7 +290,6 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
.Returns(() => sharedLocalizer.Object); .Returns(() => sharedLocalizer.Object);
var localizationOptions = Options.Create(new MvcDataAnnotationsLocalizationOptions()); var localizationOptions = Options.Create(new MvcDataAnnotationsLocalizationOptions());
localizationOptions.Value.AllowDataAnnotationsLocalizationForEnumDisplayAttributes = true;
localizationOptions.Value.DataAnnotationLocalizerProvider = (type, stringLocalizerFactory) => localizationOptions.Value.DataAnnotationLocalizerProvider = (type, stringLocalizerFactory) =>
{ {
return stringLocalizerFactory.Create(typeof(EmptyClass)); return stringLocalizerFactory.Create(typeof(EmptyClass));
@ -1269,7 +1221,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
new RequiredAttribute(), new RequiredAttribute(),
new StringLengthAttribute(5) new StringLengthAttribute(5)
}; };
Assert.Equal(expected, actual: context.ValidationMetadata.ValidatorMetadata); Assert.Equal(expected, actual: context.ValidationMetadata.ValidatorMetadata);
} }
@ -1580,7 +1532,7 @@ namespace Microsoft.AspNetCore.Mvc.DataAnnotations
private class FooCompositeValidationAttribute : ValidationProviderAttribute private class FooCompositeValidationAttribute : ValidationProviderAttribute
{ {
private IEnumerable<ValidationAttribute> _attributes; private readonly IEnumerable<ValidationAttribute> _attributes;
public FooCompositeValidationAttribute(IEnumerable<ValidationAttribute> attributes) public FooCompositeValidationAttribute(IEnumerable<ValidationAttribute> attributes)
{ {

View File

@ -725,35 +725,6 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
treatEmptyInputAsDefaultValue: treatEmptyInputAsDefaultValue); treatEmptyInputAsDefaultValue: treatEmptyInputAsDefaultValue);
} }
private IEnumerable<string> GetModelStateErrorMessages(ModelStateDictionary modelStateDictionary)
{
var allErrorMessages = new List<string>();
foreach (var keyModelStatePair in modelStateDictionary)
{
var key = keyModelStatePair.Key;
var errors = keyModelStatePair.Value.Errors;
if (errors != null && errors.Count > 0)
{
foreach (var modelError in errors)
{
if (string.IsNullOrEmpty(modelError.ErrorMessage))
{
if (modelError.Exception != null)
{
allErrorMessages.Add(modelError.Exception.Message);
}
}
else
{
allErrorMessages.Add(modelError.ErrorMessage);
}
}
}
}
return allErrorMessages;
}
private sealed class User private sealed class User
{ {
public string Name { get; set; } public string Name { get; set; }

View File

@ -3,7 +3,6 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.Mvc.Formatters.Xml;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Xunit; using Xunit;
@ -15,7 +14,7 @@ namespace Microsoft.Extensions.DependencyInjection
public void AddsFormatterMapping() public void AddsFormatterMapping()
{ {
// Arrange // Arrange
var optionsSetup = new XmlDataContractSerializerMvcOptionsSetup(Options.Options.Create(new MvcXmlOptions()), NullLoggerFactory.Instance); var optionsSetup = new XmlDataContractSerializerMvcOptionsSetup(NullLoggerFactory.Instance);
var options = new MvcOptions(); var options = new MvcOptions();
// Act // Act
@ -30,7 +29,7 @@ namespace Microsoft.Extensions.DependencyInjection
public void DoesNotOverrideExistingMapping() public void DoesNotOverrideExistingMapping()
{ {
// Arrange // Arrange
var optionsSetup = new XmlDataContractSerializerMvcOptionsSetup(Options.Options.Create(new MvcXmlOptions()), NullLoggerFactory.Instance); var optionsSetup = new XmlDataContractSerializerMvcOptionsSetup(NullLoggerFactory.Instance);
var options = new MvcOptions(); var options = new MvcOptions();
options.FormatterMappings.SetMediaTypeMappingForFormat("xml", "text/xml"); options.FormatterMappings.SetMediaTypeMappingForFormat("xml", "text/xml");
@ -46,7 +45,7 @@ namespace Microsoft.Extensions.DependencyInjection
public void AddsInputFormatter() public void AddsInputFormatter()
{ {
// Arrange // Arrange
var optionsSetup = new XmlDataContractSerializerMvcOptionsSetup(Options.Options.Create(new MvcXmlOptions()), NullLoggerFactory.Instance); var optionsSetup = new XmlDataContractSerializerMvcOptionsSetup(NullLoggerFactory.Instance);
var options = new MvcOptions(); var options = new MvcOptions();
// Act // Act
@ -60,7 +59,7 @@ namespace Microsoft.Extensions.DependencyInjection
public void AddsOutputFormatter() public void AddsOutputFormatter()
{ {
// Arrange // Arrange
var optionsSetup = new XmlDataContractSerializerMvcOptionsSetup(Options.Options.Create(new MvcXmlOptions()), NullLoggerFactory.Instance); var optionsSetup = new XmlDataContractSerializerMvcOptionsSetup(NullLoggerFactory.Instance);
var options = new MvcOptions(); var options = new MvcOptions();
// Act // Act

View File

@ -3,8 +3,6 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.Mvc.Formatters.Xml;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using Xunit; using Xunit;
@ -16,7 +14,7 @@ namespace Microsoft.Extensions.DependencyInjection
public void AddsFormatterMapping() public void AddsFormatterMapping()
{ {
// Arrange // Arrange
var optionsSetup = new XmlSerializerMvcOptionsSetup(Options.Options.Create(new MvcXmlOptions()), NullLoggerFactory.Instance); var optionsSetup = new XmlSerializerMvcOptionsSetup(NullLoggerFactory.Instance);
var options = new MvcOptions(); var options = new MvcOptions();
// Act // Act
@ -31,7 +29,7 @@ namespace Microsoft.Extensions.DependencyInjection
public void DoesNotOverrideExistingMapping() public void DoesNotOverrideExistingMapping()
{ {
// Arrange // Arrange
var optionsSetup = new XmlSerializerMvcOptionsSetup(Options.Options.Create(new MvcXmlOptions()), NullLoggerFactory.Instance); var optionsSetup = new XmlSerializerMvcOptionsSetup(NullLoggerFactory.Instance);
var options = new MvcOptions(); var options = new MvcOptions();
options.FormatterMappings.SetMediaTypeMappingForFormat("xml", "text/xml"); options.FormatterMappings.SetMediaTypeMappingForFormat("xml", "text/xml");
@ -47,7 +45,7 @@ namespace Microsoft.Extensions.DependencyInjection
public void AddsInputFormatter() public void AddsInputFormatter()
{ {
// Arrange // Arrange
var optionsSetup = new XmlSerializerMvcOptionsSetup(Options.Options.Create(new MvcXmlOptions()), NullLoggerFactory.Instance); var optionsSetup = new XmlSerializerMvcOptionsSetup(NullLoggerFactory.Instance);
var options = new MvcOptions(); var options = new MvcOptions();
// Act // Act
@ -61,7 +59,7 @@ namespace Microsoft.Extensions.DependencyInjection
public void AddsOutputFormatter() public void AddsOutputFormatter()
{ {
// Arrange // Arrange
var optionsSetup = new XmlSerializerMvcOptionsSetup(Options.Options.Create(new MvcXmlOptions()), NullLoggerFactory.Instance); var optionsSetup = new XmlSerializerMvcOptionsSetup(NullLoggerFactory.Instance);
var options = new MvcOptions(); var options = new MvcOptions();
// Act // Act

View File

@ -11,8 +11,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
public void GetProvider_ReturnsNull_IfTypeDoesNotMatch() public void GetProvider_ReturnsNull_IfTypeDoesNotMatch()
{ {
// Arrange // Arrange
var xmlOptions = new MvcXmlOptions(); var providerFactory = new ProblemDetailsWrapperProviderFactory();
var providerFactory = new ProblemDetailsWrapperProviderFactory(xmlOptions);
var context = new WrapperProviderContext(typeof(SerializableError), isSerialization: true); var context = new WrapperProviderContext(typeof(SerializableError), isSerialization: true);
// Act // Act
@ -26,8 +25,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
public void GetProvider_ReturnsWrapper_ForProblemDetails() public void GetProvider_ReturnsWrapper_ForProblemDetails()
{ {
// Arrange // Arrange
var xmlOptions = new MvcXmlOptions { AllowRfc7807CompliantProblemDetailsFormat = true }; var providerFactory = new ProblemDetailsWrapperProviderFactory();
var providerFactory = new ProblemDetailsWrapperProviderFactory(xmlOptions);
var instance = new ProblemDetails(); var instance = new ProblemDetails();
var context = new WrapperProviderContext(instance.GetType(), isSerialization: true); var context = new WrapperProviderContext(instance.GetType(), isSerialization: true);
@ -40,32 +38,11 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
Assert.Same(instance, wrapper.ProblemDetails); Assert.Same(instance, wrapper.ProblemDetails);
} }
[Fact]
public void GetProvider_Returns21CompatibleWrapper_ForProblemDetails()
{
// Arrange
var xmlOptions = new MvcXmlOptions();
var providerFactory = new ProblemDetailsWrapperProviderFactory(xmlOptions);
var instance = new ProblemDetails();
var context = new WrapperProviderContext(instance.GetType(), isSerialization: true);
// Act
var provider = providerFactory.GetProvider(context);
// Assert
var result = provider.Wrap(instance);
#pragma warning disable CS0618 // Type or member is obsolete
var wrapper = Assert.IsType<ProblemDetails21Wrapper>(result);
#pragma warning restore CS0618 // Type or member is obsolete
Assert.Same(instance, wrapper.ProblemDetails);
}
[Fact] [Fact]
public void GetProvider_ReturnsWrapper_ForValidationProblemDetails() public void GetProvider_ReturnsWrapper_ForValidationProblemDetails()
{ {
// Arrange // Arrange
var xmlOptions = new MvcXmlOptions { AllowRfc7807CompliantProblemDetailsFormat = true }; var providerFactory = new ProblemDetailsWrapperProviderFactory();
var providerFactory = new ProblemDetailsWrapperProviderFactory(xmlOptions);
var instance = new ValidationProblemDetails(); var instance = new ValidationProblemDetails();
var context = new WrapperProviderContext(instance.GetType(), isSerialization: true); var context = new WrapperProviderContext(instance.GetType(), isSerialization: true);
@ -78,32 +55,11 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Xml
Assert.Same(instance, wrapper.ProblemDetails); Assert.Same(instance, wrapper.ProblemDetails);
} }
[Fact]
public void GetProvider_Returns21CompatibleWrapper_ForValidationProblemDetails()
{
// Arrange
var xmlOptions = new MvcXmlOptions();
var providerFactory = new ProblemDetailsWrapperProviderFactory(xmlOptions);
var instance = new ValidationProblemDetails();
var context = new WrapperProviderContext(instance.GetType(), isSerialization: true);
// Act
var provider = providerFactory.GetProvider(context);
// Assert
var result = provider.Wrap(instance);
#pragma warning disable CS0618 // Type or member is obsolete
var wrapper = Assert.IsType<ValidationProblemDetails21Wrapper>(result);
#pragma warning restore CS0618 // Type or member is obsolete
Assert.Same(instance, wrapper.ProblemDetails);
}
[Fact] [Fact]
public void GetProvider_ReturnsNull_ForCustomProblemDetails() public void GetProvider_ReturnsNull_ForCustomProblemDetails()
{ {
// Arrange // Arrange
var xmlOptions = new MvcXmlOptions(); var providerFactory = new ProblemDetailsWrapperProviderFactory();
var providerFactory = new ProblemDetailsWrapperProviderFactory(xmlOptions);
var instance = new CustomProblemDetails(); var instance = new CustomProblemDetails();
var context = new WrapperProviderContext(instance.GetType(), isSerialization: true); var context = new WrapperProviderContext(instance.GetType(), isSerialization: true);

View File

@ -129,7 +129,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
return new DefaultObjectValidator( return new DefaultObjectValidator(
metadataProvider, metadataProvider,
GetModelValidatorProviders(options), GetModelValidatorProviders(options),
options?.Value ?? new MvcOptions { AllowShortCircuitingValidationWhenNoValidatorsArePresent = true }); options?.Value ?? new MvcOptions());
} }
private static IList<IModelValidatorProvider> GetModelValidatorProviders(IOptions<MvcOptions> options) private static IList<IModelValidatorProvider> GetModelValidatorProviders(IOptions<MvcOptions> options)
@ -195,8 +195,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
serviceCollection serviceCollection
.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>() .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
.AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance) .AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance)
.AddTransient<ILogger<DefaultAuthorizationService>, Logger<DefaultAuthorizationService>>() .AddTransient<ILogger<DefaultAuthorizationService>, Logger<DefaultAuthorizationService>>();
.Configure<MvcOptions>(options => options.AllowShortCircuitingValidationWhenNoValidatorsArePresent = true);
if (updateOptions != null) if (updateOptions != null)
{ {

View File

@ -169,7 +169,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
{ {
var defaultProvider = new DefaultPageApplicationModelProvider( var defaultProvider = new DefaultPageApplicationModelProvider(
TestModelMetadataProvider.CreateDefaultProvider(), TestModelMetadataProvider.CreateDefaultProvider(),
Options.Create(new RazorPagesOptions { AllowDefaultHandlingForOptionsRequests = true })); Options.Create(new RazorPagesOptions()));
var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo); var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo);
defaultProvider.OnProvidersExecuting(context); defaultProvider.OnProvidersExecuting(context);

View File

@ -137,7 +137,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
var options = new RazorPagesOptions var options = new RazorPagesOptions
{ {
AllowAreas = true,
// Setting this value should not affect area page lookup. // Setting this value should not affect area page lookup.
RootDirectory = "/Files", RootDirectory = "/Files",
}; };
@ -213,44 +212,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
}); });
} }
[Fact]
public void OnProvidersExecuting_DoesNotAddsModelsForAreaPages_IfFeatureIsDisabled()
{
// Arrange
var descriptors = new[]
{
CreateVersion_2_0_Descriptor("/Pages/About.cshtml"),
CreateVersion_2_0_Descriptor("/Areas/Accounts/Pages/Home.cshtml"),
};
var options = new RazorPagesOptions { AllowAreas = false };
var provider = CreateProvider(options: options, descriptors: descriptors);
var context = new PageRouteModelProviderContext();
// Act
provider.OnProvidersExecuting(context);
// Assert
Assert.Collection(
context.RouteModels,
result =>
{
Assert.Equal("/Pages/About.cshtml", result.RelativePath);
Assert.Equal("/About", result.ViewEnginePath);
Assert.Collection(
result.Selectors,
selector => Assert.Equal("About", selector.AttributeRouteModel.Template));
Assert.Collection(
result.RouteValues.OrderBy(k => k.Key),
kvp =>
{
Assert.Equal("page", kvp.Key);
Assert.Equal("/About", kvp.Value);
});
});
}
[Fact] [Fact]
public void OnProvidersExecuting_DoesNotAddAreaAndNonAreaRoutesForAPage() public void OnProvidersExecuting_DoesNotAddAreaAndNonAreaRoutesForAPage()
{ {
@ -265,7 +226,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
var options = new RazorPagesOptions var options = new RazorPagesOptions
{ {
AllowAreas = true,
RootDirectory = "/", RootDirectory = "/",
}; };
@ -637,4 +597,4 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
} }
} }
} }
} }

View File

@ -1036,24 +1036,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
public void OnGetUser() { } public void OnGetUser() { }
} }
[Fact]
public void PopulateFilters_With21CompatBehavior_DoesNotAddDisallowOptionsRequestsPageFilter()
{
// Arrange
var provider = new DefaultPageApplicationModelProvider(
TestModelMetadataProvider.CreateDefaultProvider(),
Options.Create(new RazorPagesOptions()));
var typeInfo = typeof(object).GetTypeInfo();
var pageModel = new PageApplicationModel(new PageActionDescriptor(), typeInfo, typeInfo.GetCustomAttributes(inherit: true));
// Act
provider.PopulateFilters(pageModel);
// Assert
Assert.Empty(pageModel.Filters);
}
[Fact] [Fact]
public void PopulateFilters_AddsDisallowOptionsRequestsPageFilter() public void PopulateFilters_AddsDisallowOptionsRequestsPageFilter()
{ {
@ -1189,7 +1171,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
{ {
return new DefaultPageApplicationModelProvider( return new DefaultPageApplicationModelProvider(
TestModelMetadataProvider.CreateDefaultProvider(), TestModelMetadataProvider.CreateDefaultProvider(),
Options.Create(new RazorPagesOptions { AllowDefaultHandlingForOptionsRequests = true })); Options.Create(new RazorPagesOptions()));
} }
} }
} }

View File

@ -144,7 +144,7 @@ namespace Microsoft.AspNetCore.Mvc.Filters
{ {
var defaultProvider = new DefaultPageApplicationModelProvider( var defaultProvider = new DefaultPageApplicationModelProvider(
TestModelMetadataProvider.CreateDefaultProvider(), TestModelMetadataProvider.CreateDefaultProvider(),
Options.Create(new RazorPagesOptions { AllowDefaultHandlingForOptionsRequests = true })); Options.Create(new RazorPagesOptions()));
var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo); var context = new PageApplicationModelProviderContext(new PageActionDescriptor(), typeInfo);
defaultProvider.OnProvidersExecuting(context); defaultProvider.OnProvidersExecuting(context);

View File

@ -80,17 +80,17 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
filter.Properties, filter.Properties,
property => property =>
{ {
Assert.Equal("TempDataProperty-Test2", property.Key); Assert.Equal("Test2", property.Key);
Assert.Equal(type.GetProperty(nameof(TestPageModel_OneTempDataProperty.Test2)), property.PropertyInfo); Assert.Equal(type.GetProperty(nameof(TestPageModel_OneTempDataProperty.Test2)), property.PropertyInfo);
}); });
} }
[Fact] [Fact]
public void OnProvidersExecuting_SetsKeyPrefixToEmptyString_IfCompatSwitchIsSet() public void OnProvidersExecuting_SetsKeyPrefixToEmptyString()
{ {
// Arrange // Arrange
var type = typeof(TestPageModel_OneTempDataProperty); var type = typeof(TestPageModel_OneTempDataProperty);
var options = Options.Create(new MvcViewOptions { SuppressTempDataAttributePrefix = true }); var options = Options.Create(new MvcViewOptions());
var provider = new TempDataFilterPageApplicationModelProvider(options); var provider = new TempDataFilterPageApplicationModelProvider(options);
var context = CreateProviderContext(type); var context = CreateProviderContext(type);

View File

@ -6,55 +6,12 @@ using System.Collections.Generic;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Options;
using Xunit; using Xunit;
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{ {
public class DefaultPageHandlerMethodSelectorTest public class DefaultPageHandlerMethodSelectorTest
{ {
[Fact]
public void LegacyBehavior_Select_ReturnsNull_WhenNoHandlerMatchesHttpMethod()
{
// Arrange
var descriptor1 = new HandlerMethodDescriptor
{
HttpMethod = "GET"
};
var descriptor2 = new HandlerMethodDescriptor
{
HttpMethod = "POST"
};
var pageContext = new PageContext
{
ActionDescriptor = new CompiledPageActionDescriptor
{
HandlerMethods = new List<HandlerMethodDescriptor>()
{
descriptor1,
descriptor2,
},
},
RouteData = new RouteData(),
HttpContext = new DefaultHttpContext
{
Request =
{
Method = "PUT"
},
},
};
var selector = CreateSelector(legacyBehavior: true);
// Act
var actual = selector.Select(pageContext);
// Assert
Assert.Null(actual);
}
[Fact] [Fact]
public void NewBehavior_Select_ReturnsFuzzyMatchForHead_WhenNoHeadHandlerDefined() public void NewBehavior_Select_ReturnsFuzzyMatchForHead_WhenNoHeadHandlerDefined()
{ {
@ -780,13 +737,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{ {
} }
private static DefaultPageHandlerMethodSelector CreateSelector(bool legacyBehavior = false) private static DefaultPageHandlerMethodSelector CreateSelector()
{ {
var options = Options.Create(new RazorPagesOptions() return new DefaultPageHandlerMethodSelector();
{
AllowMappingHeadRequestsToGetHandler = !legacyBehavior
});
return new DefaultPageHandlerMethodSelector(options);
} }
} }
} }

View File

@ -1,92 +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 Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.AspNetCore.Mvc.Formatters.Xml;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.IntegrationTest
{
// Integration tests for compatibility switches. These tests verify which compatibility
// values apply to each supported version.
//
// If you add a new compatibility switch, make sure to update ALL of these tests. Each test
// here should include verification for all of the switches.
public class CompatibilitySwitchIntegrationTest
{
[Fact]
public void CompatibilitySwitches_Version_3_0()
{
// Arrange
var serviceCollection = new ServiceCollection();
AddHostingServices(serviceCollection);
serviceCollection
.AddMvc()
.AddXmlDataContractSerializerFormatters()
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
var services = serviceCollection.BuildServiceProvider();
// Act
var mvcOptions = services.GetRequiredService<IOptions<MvcOptions>>().Value;
var jsonOptions = services.GetRequiredService<IOptions<MvcJsonOptions>>().Value;
var razorPagesOptions = services.GetRequiredService<IOptions<RazorPagesOptions>>().Value;
var apiBehaviorOptions = services.GetRequiredService<IOptions<ApiBehaviorOptions>>().Value;
var razorViewEngineOptions = services.GetRequiredService<IOptions<RazorViewEngineOptions>>().Value;
var xmlOptions = services.GetRequiredService<IOptions<MvcXmlOptions>>().Value;
// Assert
Assert.True(mvcOptions.SuppressBindingUndefinedValueToEnumType);
Assert.Equal(InputFormatterExceptionPolicy.MalformedInputExceptions, mvcOptions.InputFormatterExceptionPolicy);
Assert.True(razorPagesOptions.AllowAreas);
Assert.True(razorPagesOptions.AllowDefaultHandlingForOptionsRequests);
Assert.True(xmlOptions.AllowRfc7807CompliantProblemDetailsFormat);
Assert.True(mvcOptions.AllowShortCircuitingValidationWhenNoValidatorsArePresent);
}
[Fact]
public void CompatibilitySwitches_Version_Latest()
{
// Arrange
var serviceCollection = new ServiceCollection();
AddHostingServices(serviceCollection);
serviceCollection
.AddMvc()
.AddXmlDataContractSerializerFormatters()
.SetCompatibilityVersion(CompatibilityVersion.Latest);
var services = serviceCollection.BuildServiceProvider();
// Act
var mvcOptions = services.GetRequiredService<IOptions<MvcOptions>>().Value;
var jsonOptions = services.GetRequiredService<IOptions<MvcJsonOptions>>().Value;
var razorPagesOptions = services.GetRequiredService<IOptions<RazorPagesOptions>>().Value;
var apiBehaviorOptions = services.GetRequiredService<IOptions<ApiBehaviorOptions>>().Value;
var razorViewEngineOptions = services.GetRequiredService<IOptions<RazorViewEngineOptions>>().Value;
var xmlOptions = services.GetRequiredService<IOptions<MvcXmlOptions>>().Value;
// Assert
Assert.True(mvcOptions.SuppressBindingUndefinedValueToEnumType);
Assert.Equal(InputFormatterExceptionPolicy.MalformedInputExceptions, mvcOptions.InputFormatterExceptionPolicy);
Assert.True(razorPagesOptions.AllowAreas);
Assert.True(razorPagesOptions.AllowDefaultHandlingForOptionsRequests);
Assert.True(xmlOptions.AllowRfc7807CompliantProblemDetailsFormat);
Assert.True(mvcOptions.AllowShortCircuitingValidationWhenNoValidatorsArePresent);
}
// This just does the minimum needed to be able to resolve these options.
private static void AddHostingServices(IServiceCollection serviceCollection)
{
serviceCollection.AddSingleton(Mock.Of<IHostingEnvironment>());
serviceCollection.AddLogging();
serviceCollection.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
}
}
}

View File

@ -20,7 +20,6 @@ using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Razor.Compilation; using Microsoft.AspNetCore.Mvc.Razor.Compilation;
using Microsoft.AspNetCore.Mvc.Razor.TagHelpers; using Microsoft.AspNetCore.Mvc.Razor.TagHelpers;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
using Microsoft.AspNetCore.Mvc.TagHelpers; using Microsoft.AspNetCore.Mvc.TagHelpers;
using Microsoft.AspNetCore.Mvc.ViewComponents; using Microsoft.AspNetCore.Mvc.ViewComponents;
@ -382,13 +381,6 @@ namespace Microsoft.AspNetCore.Mvc
typeof(MvcCoreMvcOptionsSetup), typeof(MvcCoreMvcOptionsSetup),
} }
}, },
{
typeof(IPostConfigureOptions<RazorPagesOptions>),
new[]
{
typeof(RazorPagesOptionsConfigureCompatibilityOptions),
}
},
{ {
typeof(IActionConstraintProvider), typeof(IActionConstraintProvider),
new Type[] new Type[]

View File

@ -212,7 +212,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
// Assert // Assert
var attribute = Assert.Single(tagBuilder.Attributes, a => a.Key == "maxlength"); var attribute = Assert.Single(tagBuilder.Attributes, a => a.Key == "maxlength");
Assert.Equal(expectedValue, Int32.Parse(attribute.Value)); Assert.Equal(expectedValue, int.Parse(attribute.Value));
} }
[Theory] [Theory]
@ -236,7 +236,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
// Assert // Assert
var attribute = Assert.Single(tagBuilder.Attributes, a => a.Key == "maxlength"); var attribute = Assert.Single(tagBuilder.Attributes, a => a.Key == "maxlength");
Assert.Equal(expectedValue, Int32.Parse(attribute.Value)); Assert.Equal(expectedValue, int.Parse(attribute.Value));
} }
[Theory] [Theory]
@ -260,7 +260,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
// Assert // Assert
var attribute = Assert.Single(tagBuilder.Attributes, a => a.Key == "maxlength"); var attribute = Assert.Single(tagBuilder.Attributes, a => a.Key == "maxlength");
Assert.Equal(expectedValue, Int32.Parse(attribute.Value)); Assert.Equal(expectedValue, int.Parse(attribute.Value));
} }
[Fact] [Fact]
@ -282,7 +282,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
// Assert // Assert
var attribute = Assert.Single(tagBuilder.Attributes, a => a.Key == "maxlength"); var attribute = Assert.Single(tagBuilder.Attributes, a => a.Key == "maxlength");
Assert.Equal(Math.Min(ModelWithMaxLengthMetadata.MaxLengthAttributeValue, ModelWithMaxLengthMetadata.StringLengthAttributeValue), Int32.Parse(attribute.Value)); Assert.Equal(Math.Min(ModelWithMaxLengthMetadata.MaxLengthAttributeValue, ModelWithMaxLengthMetadata.StringLengthAttributeValue), int.Parse(attribute.Value));
} }
[Fact] [Fact]
@ -328,7 +328,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
// Assert // Assert
var attribute = Assert.Single(tagBuilder.Attributes, a => a.Key == "maxlength"); var attribute = Assert.Single(tagBuilder.Attributes, a => a.Key == "maxlength");
Assert.Equal(expectedValue, Int32.Parse(attribute.Value)); Assert.Equal(expectedValue, int.Parse(attribute.Value));
} }
[Theory] [Theory]
@ -923,7 +923,7 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures
private static IHtmlGenerator GetGenerator(IModelMetadataProvider metadataProvider) private static IHtmlGenerator GetGenerator(IModelMetadataProvider metadataProvider)
{ {
var mvcViewOptionsAccessor = new Mock<IOptions<MvcViewOptions>>(); var mvcViewOptionsAccessor = new Mock<IOptions<MvcViewOptions>>();
mvcViewOptionsAccessor.SetupGet(accessor => accessor.Value).Returns(new MvcViewOptions() { AllowRenderingMaxLengthAttribute = true }); mvcViewOptionsAccessor.SetupGet(accessor => accessor.Value).Returns(new MvcViewOptions());
var htmlEncoder = Mock.Of<HtmlEncoder>(); var htmlEncoder = Mock.Of<HtmlEncoder>();
var antiforgery = new Mock<IAntiforgery>(); var antiforgery = new Mock<IAntiforgery>();

View File

@ -85,15 +85,15 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Filters
Assert.NotNull(filter); Assert.NotNull(filter);
var property = Assert.Single(filter.TempDataProperties); var property = Assert.Single(filter.TempDataProperties);
Assert.Same(expected, property.PropertyInfo); Assert.Same(expected, property.PropertyInfo);
Assert.Equal("TempDataProperty-Test2", property.Key); Assert.Equal("Test2", property.Key);
} }
[Fact] [Fact]
public void OnProvidersExecuting_SetsKeyPrefixToEmptyString_IfCompatSwitchIsSet() public void OnProvidersExecuting_SetsKeyPrefixToEmptyString()
{ {
// Arrange // Arrange
var expected = typeof(TestController_OneTempDataProperty).GetProperty(nameof(TestController_OneTempDataProperty.Test2)); var expected = typeof(TestController_OneTempDataProperty).GetProperty(nameof(TestController_OneTempDataProperty.Test2));
var options = Options.Create(new MvcViewOptions { SuppressTempDataAttributePrefix = true }); var options = Options.Create(new MvcViewOptions());
var type = typeof(TestController_OneTempDataProperty); var type = typeof(TestController_OneTempDataProperty);
var provider = new TempDataApplicationModelProvider(options); var provider = new TempDataApplicationModelProvider(options);
var context = GetContext(type); var context = GetContext(type);

View File

@ -27,7 +27,6 @@ namespace RazorPagesWebSite
.AddCookieTempDataProvider() .AddCookieTempDataProvider()
.AddRazorPagesOptions(options => .AddRazorPagesOptions(options =>
{ {
options.AllowAreas = true;
options.Conventions.AuthorizePage("/Conventions/Auth"); options.Conventions.AuthorizePage("/Conventions/Auth");
options.Conventions.AuthorizeFolder("/Conventions/AuthFolder"); options.Conventions.AuthorizeFolder("/Conventions/AuthFolder");
options.Conventions.AuthorizeAreaFolder("Accounts", "/RequiresAuth"); options.Conventions.AuthorizeAreaFolder("Accounts", "/RequiresAuth");