[Design] Add a MaxValidationDepth option to ValidationVisitor
Fixes #7357
This commit is contained in:
parent
af770ede87
commit
522006d2c8
|
|
@ -2,26 +2,33 @@
|
||||||
// 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.Internal;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.Internal
|
namespace Microsoft.AspNetCore.Mvc
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The default implementation of <see cref="IObjectModelValidator"/>.
|
/// The default implementation of <see cref="IObjectModelValidator"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class DefaultObjectValidator : ObjectModelValidator
|
internal class DefaultObjectValidator : ObjectModelValidator
|
||||||
{
|
{
|
||||||
|
private readonly MvcOptions _mvcOptions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of <see cref="DefaultObjectValidator"/>.
|
/// Initializes a new instance of <see cref="DefaultObjectValidator"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
|
/// <param name="modelMetadataProvider">The <see cref="IModelMetadataProvider"/>.</param>
|
||||||
/// <param name="validatorProviders">The list of <see cref="IModelValidatorProvider"/>.</param>
|
/// <param name="validatorProviders">The list of <see cref="IModelValidatorProvider"/>.</param>
|
||||||
|
/// <param name="mvcOptions">Accessor to <see cref="MvcOptions"/>.</param>
|
||||||
public DefaultObjectValidator(
|
public DefaultObjectValidator(
|
||||||
IModelMetadataProvider modelMetadataProvider,
|
IModelMetadataProvider modelMetadataProvider,
|
||||||
IList<IModelValidatorProvider> validatorProviders)
|
IList<IModelValidatorProvider> validatorProviders,
|
||||||
|
MvcOptions mvcOptions)
|
||||||
: base(modelMetadataProvider, validatorProviders)
|
: base(modelMetadataProvider, validatorProviders)
|
||||||
{
|
{
|
||||||
|
_mvcOptions = mvcOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ValidationVisitor GetValidationVisitor(
|
public override ValidationVisitor GetValidationVisitor(
|
||||||
|
|
@ -31,12 +38,16 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
IModelMetadataProvider metadataProvider,
|
IModelMetadataProvider metadataProvider,
|
||||||
ValidationStateDictionary validationState)
|
ValidationStateDictionary validationState)
|
||||||
{
|
{
|
||||||
return new ValidationVisitor(
|
var visitor = new ValidationVisitor(
|
||||||
actionContext,
|
actionContext,
|
||||||
validatorProvider,
|
validatorProvider,
|
||||||
validatorCache,
|
validatorCache,
|
||||||
metadataProvider,
|
metadataProvider,
|
||||||
validationState);
|
validationState);
|
||||||
|
|
||||||
|
visitor.MaxValidationDepth = _mvcOptions.MaxValidationDepth;
|
||||||
|
|
||||||
|
return visitor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -228,7 +228,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
{
|
{
|
||||||
var options = s.GetRequiredService<IOptions<MvcOptions>>().Value;
|
var options = s.GetRequiredService<IOptions<MvcOptions>>().Value;
|
||||||
var metadataProvider = s.GetRequiredService<IModelMetadataProvider>();
|
var metadataProvider = s.GetRequiredService<IModelMetadataProvider>();
|
||||||
return new DefaultObjectValidator(metadataProvider, options.ModelValidatorProviders);
|
return new DefaultObjectValidator(metadataProvider, options.ModelValidatorProviders, options);
|
||||||
});
|
});
|
||||||
services.TryAddSingleton<ClientValidatorCache>();
|
services.TryAddSingleton<ClientValidatorCache>();
|
||||||
services.TryAddSingleton<ParameterBinder>();
|
services.TryAddSingleton<ParameterBinder>();
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,9 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
if (Version >= CompatibilityVersion.Version_2_2)
|
if (Version >= CompatibilityVersion.Version_2_2)
|
||||||
{
|
{
|
||||||
values[nameof(MvcOptions.EnableEndpointRouting)] = true;
|
values[nameof(MvcOptions.EnableEndpointRouting)] = true;
|
||||||
|
|
||||||
|
// Matches JsonSerializerSettingsProvider.DefaultMaxDepth
|
||||||
|
values[nameof(MvcOptions.MaxValidationDepth)] = 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
return values;
|
return values;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
|
{
|
||||||
|
internal class NullableCompatibilitySwitch<TValue> : ICompatibilitySwitch where TValue : struct
|
||||||
|
{
|
||||||
|
private TValue? _value;
|
||||||
|
|
||||||
|
public NullableCompatibilitySwitch(string name)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsValueSet { get; private set; }
|
||||||
|
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
public TValue? Value
|
||||||
|
{
|
||||||
|
get => _value;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
IsValueSet = true;
|
||||||
|
_value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object ICompatibilitySwitch.Value
|
||||||
|
{
|
||||||
|
get => Value;
|
||||||
|
set => Value = (TValue?)value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,8 +4,10 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Core;
|
||||||
using Microsoft.AspNetCore.Mvc.Internal;
|
using Microsoft.AspNetCore.Mvc.Internal;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Internal;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Internal;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
||||||
{
|
{
|
||||||
|
|
@ -15,6 +17,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ValidationVisitor
|
public class ValidationVisitor
|
||||||
{
|
{
|
||||||
|
private int? _maxValidationDepth;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new <see cref="ValidationVisitor"/>.
|
/// Creates a new <see cref="ValidationVisitor"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -76,6 +80,31 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
||||||
protected ModelMetadata Metadata { get; set; }
|
protected ModelMetadata Metadata { get; set; }
|
||||||
protected IValidationStrategy Strategy { get; set; }
|
protected IValidationStrategy Strategy { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the maximum depth to constrain the validation visitor when validating.
|
||||||
|
/// <para>
|
||||||
|
/// <see cref="ValidationVisitor"/> traverses the object graph of the model being validated. For models
|
||||||
|
/// that are very deep or are infinitely recursive, validation may result in stack overflow.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// When not <see langword="null"/>, <see cref="Visit(ModelMetadata, string, object)"/> will throw if
|
||||||
|
/// current traversal depth exceeds the specified value.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
public int? MaxValidationDepth
|
||||||
|
{
|
||||||
|
get => _maxValidationDepth;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value != null && value <= 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
_maxValidationDepth = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates whether validation of a complex type should be performed if validation fails for any of its children. The default behavior is false.
|
/// Indicates whether validation of a complex type should be performed if validation fails for any of its children. The default behavior is false.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -192,6 +221,33 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (MaxValidationDepth != null && CurrentPath.Count > MaxValidationDepth)
|
||||||
|
{
|
||||||
|
// Non cyclic but too deep an object graph.
|
||||||
|
|
||||||
|
// Pop the current model to make ValidationStack.Dispose happy
|
||||||
|
CurrentPath.Pop(model);
|
||||||
|
|
||||||
|
string message;
|
||||||
|
switch (metadata.MetadataKind)
|
||||||
|
{
|
||||||
|
case ModelMetadataKind.Property:
|
||||||
|
message = Resources.FormatValidationVisitor_ExceededMaxPropertyDepth(nameof(ValidationVisitor), MaxValidationDepth, metadata.Name, metadata.ContainerType);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Since the minimum depth is never 0, MetadataKind can never be Parameter. Consequently we only special case MetadataKind.Property.
|
||||||
|
message = Resources.FormatValidationVisitor_ExceededMaxDepth(nameof(ValidationVisitor), MaxValidationDepth, metadata.ModelType);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
message += " " + Resources.FormatValidationVisitor_ExceededMaxDepthFix(nameof(MvcOptions), nameof(MvcOptions.MaxValidationDepth));
|
||||||
|
throw new InvalidOperationException(message)
|
||||||
|
{
|
||||||
|
HelpLink = "https://aka.ms/AA21ue1",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
var entry = GetValidationEntry(model);
|
var entry = GetValidationEntry(model);
|
||||||
key = entry?.Key ?? key ?? string.Empty;
|
key = entry?.Key ?? key ?? string.Empty;
|
||||||
metadata = entry?.Metadata ?? metadata;
|
metadata = entry?.Metadata ?? metadata;
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
private readonly CompatibilitySwitch<InputFormatterExceptionPolicy> _inputFormatterExceptionPolicy;
|
private readonly CompatibilitySwitch<InputFormatterExceptionPolicy> _inputFormatterExceptionPolicy;
|
||||||
private readonly CompatibilitySwitch<bool> _suppressBindingUndefinedValueToEnumType;
|
private readonly CompatibilitySwitch<bool> _suppressBindingUndefinedValueToEnumType;
|
||||||
private readonly CompatibilitySwitch<bool> _enableEndpointRouting;
|
private readonly CompatibilitySwitch<bool> _enableEndpointRouting;
|
||||||
|
private readonly NullableCompatibilitySwitch<int> _maxValidationDepth;
|
||||||
private readonly ICompatibilitySwitch[] _switches;
|
private readonly ICompatibilitySwitch[] _switches;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -56,6 +57,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
_inputFormatterExceptionPolicy = new CompatibilitySwitch<InputFormatterExceptionPolicy>(nameof(InputFormatterExceptionPolicy), InputFormatterExceptionPolicy.AllExceptions);
|
_inputFormatterExceptionPolicy = new CompatibilitySwitch<InputFormatterExceptionPolicy>(nameof(InputFormatterExceptionPolicy), InputFormatterExceptionPolicy.AllExceptions);
|
||||||
_suppressBindingUndefinedValueToEnumType = new CompatibilitySwitch<bool>(nameof(SuppressBindingUndefinedValueToEnumType));
|
_suppressBindingUndefinedValueToEnumType = new CompatibilitySwitch<bool>(nameof(SuppressBindingUndefinedValueToEnumType));
|
||||||
_enableEndpointRouting = new CompatibilitySwitch<bool>(nameof(EnableEndpointRouting));
|
_enableEndpointRouting = new CompatibilitySwitch<bool>(nameof(EnableEndpointRouting));
|
||||||
|
_maxValidationDepth = new NullableCompatibilitySwitch<int>(nameof(MaxValidationDepth));
|
||||||
|
|
||||||
_switches = new ICompatibilitySwitch[]
|
_switches = new ICompatibilitySwitch[]
|
||||||
{
|
{
|
||||||
|
|
@ -65,6 +67,7 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
_inputFormatterExceptionPolicy,
|
_inputFormatterExceptionPolicy,
|
||||||
_suppressBindingUndefinedValueToEnumType,
|
_suppressBindingUndefinedValueToEnumType,
|
||||||
_enableEndpointRouting,
|
_enableEndpointRouting,
|
||||||
|
_maxValidationDepth,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -396,6 +399,49 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool RequireHttpsPermanent { get; set; }
|
public bool RequireHttpsPermanent { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the maximum depth to constrain the validation visitor when validating. Set to <see langword="null" />
|
||||||
|
/// to disable this feature.
|
||||||
|
/// <para>
|
||||||
|
/// <see cref="ValidationVisitor"/> traverses the object graph of the model being validated. For models
|
||||||
|
/// that are very deep or are infinitely recursive, validation may result in stack overflow.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// When not <see langword="null"/>, <see cref="ValidationVisitor"/> will throw if
|
||||||
|
/// traversing an object exceeds the maximum allowed validation depth.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// This property is associated with a compatibility switch and can provide a different behavior depending on
|
||||||
|
/// the configured compatibility version for the application. See <see cref="CompatibilityVersion"/> for
|
||||||
|
/// guidance and examples of setting the application's compatibility version.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// Configuring the desired value of the compatibility switch by calling this property's setter will take precedence
|
||||||
|
/// over the value implied by the application's <see cref="CompatibilityVersion"/>.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// If the application's compatibility version is set to <see cref="CompatibilityVersion.Version_2_2"/> then
|
||||||
|
/// this setting will have the value <c>200</c> unless explicitly configured.
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// If the application's compatibility version is set to <see cref="CompatibilityVersion.Version_2_1"/> or
|
||||||
|
/// earlier then this setting will have the value <see langword="null"/> unless explicitly configured.
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
public int? MaxValidationDepth
|
||||||
|
{
|
||||||
|
get => _maxValidationDepth.Value;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value != null && value <= 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
_maxValidationDepth.Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IEnumerator<ICompatibilitySwitch> IEnumerable<ICompatibilitySwitch>.GetEnumerator()
|
IEnumerator<ICompatibilitySwitch> IEnumerable<ICompatibilitySwitch>.GetEnumerator()
|
||||||
{
|
{
|
||||||
return ((IEnumerable<ICompatibilitySwitch>)_switches).GetEnumerator();
|
return ((IEnumerable<ICompatibilitySwitch>)_switches).GetEnumerator();
|
||||||
|
|
|
||||||
|
|
@ -1536,6 +1536,48 @@ namespace Microsoft.AspNetCore.Mvc.Core
|
||||||
internal static string FormatApiConventionMethod_NoMethodFound(object p0, object p1)
|
internal static string FormatApiConventionMethod_NoMethodFound(object p0, object p1)
|
||||||
=> string.Format(CultureInfo.CurrentCulture, GetString("ApiConventionMethod_NoMethodFound"), p0, p1);
|
=> string.Format(CultureInfo.CurrentCulture, GetString("ApiConventionMethod_NoMethodFound"), p0, p1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// {0} exceeded the maximum configured validation depth '{1}' when validating type '{2}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string ValidationVisitor_ExceededMaxDepth
|
||||||
|
{
|
||||||
|
get => GetString("ValidationVisitor_ExceededMaxDepth");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// {0} exceeded the maximum configured validation depth '{1}' when validating type '{2}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatValidationVisitor_ExceededMaxDepth(object p0, object p1, object p2)
|
||||||
|
=> string.Format(CultureInfo.CurrentCulture, GetString("ValidationVisitor_ExceededMaxDepth"), p0, p1, p2);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This may indicate a very deep or infinitely recursive object graph. Consider modifying '{0}.{1}' or suppressing validation on the model type.
|
||||||
|
/// </summary>
|
||||||
|
internal static string ValidationVisitor_ExceededMaxDepthFix
|
||||||
|
{
|
||||||
|
get => GetString("ValidationVisitor_ExceededMaxDepthFix");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This may indicate a very deep or infinitely recursive object graph. Consider modifying '{0}.{1}' or suppressing validation on the model type.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatValidationVisitor_ExceededMaxDepthFix(object p0, object p1)
|
||||||
|
=> string.Format(CultureInfo.CurrentCulture, GetString("ValidationVisitor_ExceededMaxDepthFix"), p0, p1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// {0} exceeded the maximum configured validation depth '{1}' when validating property '{2}' on type '{3}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string ValidationVisitor_ExceededMaxPropertyDepth
|
||||||
|
{
|
||||||
|
get => GetString("ValidationVisitor_ExceededMaxPropertyDepth");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// {0} exceeded the maximum configured validation depth '{1}' when validating property '{2}' on type '{3}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatValidationVisitor_ExceededMaxPropertyDepth(object p0, object p1, object p2, object p3)
|
||||||
|
=> string.Format(CultureInfo.CurrentCulture, GetString("ValidationVisitor_ExceededMaxPropertyDepth"), p0, p1, p2, p3);
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -457,4 +457,13 @@
|
||||||
<data name="ApiConventionMethod_NoMethodFound" xml:space="preserve">
|
<data name="ApiConventionMethod_NoMethodFound" xml:space="preserve">
|
||||||
<value>A method named '{0}' was not found on convention type '{1}'.</value>
|
<value>A method named '{0}' was not found on convention type '{1}'.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="ValidationVisitor_ExceededMaxDepth" xml:space="preserve">
|
||||||
|
<value>{0} exceeded the maximum configured validation depth '{1}' when validating type '{2}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ValidationVisitor_ExceededMaxDepthFix" xml:space="preserve">
|
||||||
|
<value>This may indicate a very deep or infinitely recursive object graph. Consider modifying '{0}.{1}' or suppressing validation on the model type.</value>
|
||||||
|
</data>
|
||||||
|
<data name="ValidationVisitor_ExceededMaxPropertyDepth" xml:space="preserve">
|
||||||
|
<value>{0} exceeded the maximum configured validation depth '{1}' when validating property '{2}' on type '{3}'.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -2711,7 +2711,8 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
|
||||||
var controller = GetController(binder, valueProvider: null);
|
var controller = GetController(binder, valueProvider: null);
|
||||||
controller.ObjectValidator = new DefaultObjectValidator(
|
controller.ObjectValidator = new DefaultObjectValidator(
|
||||||
controller.MetadataProvider,
|
controller.MetadataProvider,
|
||||||
new[] { Mock.Of<IModelValidatorProvider>() });
|
new[] { Mock.Of<IModelValidatorProvider>() },
|
||||||
|
new MvcOptions());
|
||||||
|
|
||||||
var model = new TryValidateModelModel();
|
var model = new TryValidateModelModel();
|
||||||
|
|
||||||
|
|
@ -2747,7 +2748,8 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
|
||||||
var controller = GetController(binder, valueProvider: null);
|
var controller = GetController(binder, valueProvider: null);
|
||||||
controller.ObjectValidator = new DefaultObjectValidator(
|
controller.ObjectValidator = new DefaultObjectValidator(
|
||||||
controller.MetadataProvider,
|
controller.MetadataProvider,
|
||||||
new[] { provider.Object });
|
new[] { provider.Object },
|
||||||
|
new MvcOptions());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = controller.TryValidateModel(model, "Prefix");
|
var result = controller.TryValidateModel(model, "Prefix");
|
||||||
|
|
@ -2783,7 +2785,8 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
|
||||||
var controller = GetController(binder, valueProvider: null);
|
var controller = GetController(binder, valueProvider: null);
|
||||||
controller.ObjectValidator = new DefaultObjectValidator(
|
controller.ObjectValidator = new DefaultObjectValidator(
|
||||||
controller.MetadataProvider,
|
controller.MetadataProvider,
|
||||||
new[] { provider.Object });
|
new[] { provider.Object },
|
||||||
|
new MvcOptions());
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = controller.TryValidateModel(model);
|
var result = controller.TryValidateModel(model);
|
||||||
|
|
@ -2867,7 +2870,7 @@ namespace Microsoft.AspNetCore.Mvc.Core.Test
|
||||||
ControllerContext = controllerContext,
|
ControllerContext = controllerContext,
|
||||||
MetadataProvider = metadataProvider,
|
MetadataProvider = metadataProvider,
|
||||||
ModelBinderFactory = binderFactory.Object,
|
ModelBinderFactory = binderFactory.Object,
|
||||||
ObjectValidator = new DefaultObjectValidator(metadataProvider, validatorProviders),
|
ObjectValidator = new DefaultObjectValidator(metadataProvider, validatorProviders, new MvcOptions()),
|
||||||
};
|
};
|
||||||
|
|
||||||
return controller;
|
return controller;
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ namespace Microsoft.AspNetCore.Mvc.Controllers
|
||||||
.Returns(metadataProvider);
|
.Returns(metadataProvider);
|
||||||
services
|
services
|
||||||
.Setup(s => s.GetService(typeof(IObjectModelValidator)))
|
.Setup(s => s.GetService(typeof(IObjectModelValidator)))
|
||||||
.Returns(new DefaultObjectValidator(metadataProvider, new List<IModelValidatorProvider>()));
|
.Returns(new DefaultObjectValidator(metadataProvider, new List<IModelValidatorProvider>(), new MvcOptions()));
|
||||||
return services.Object;
|
return services.Object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,8 @@ namespace Microsoft.AspNetCore.Mvc.Controllers
|
||||||
.Setup(s => s.GetService(typeof(IObjectModelValidator)))
|
.Setup(s => s.GetService(typeof(IObjectModelValidator)))
|
||||||
.Returns(new DefaultObjectValidator(
|
.Returns(new DefaultObjectValidator(
|
||||||
metadataProvider,
|
metadataProvider,
|
||||||
TestModelValidatorProvider.CreateDefaultProvider().ValidatorProviders));
|
TestModelValidatorProvider.CreateDefaultProvider().ValidatorProviders,
|
||||||
|
new MvcOptions()));
|
||||||
return services.Object;
|
return services.Object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
// 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.Mvc.Infrastructure;
|
||||||
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.Core.Infrastructure
|
||||||
|
{
|
||||||
|
public class MvcOptionsConfigureCompatibilityOptionsTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void PostConfigure_ConfiguresMaxValidationDepth()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mvcOptions = new MvcOptions();
|
||||||
|
var mvcCompatibilityOptions = new MvcCompatibilityOptions
|
||||||
|
{
|
||||||
|
CompatibilityVersion = CompatibilityVersion.Version_2_2,
|
||||||
|
};
|
||||||
|
|
||||||
|
var configureOptions = new MvcOptionsConfigureCompatibilityOptions(
|
||||||
|
NullLoggerFactory.Instance,
|
||||||
|
Options.Create(mvcCompatibilityOptions));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
configureOptions.PostConfigure(string.Empty, mvcOptions);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(32, mvcOptions.MaxValidationDepth);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PostConfigure_DoesNotConfiguresMaxValidationDepth_WhenSetToNull()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var mvcOptions = new MvcOptions
|
||||||
|
{
|
||||||
|
MaxValidationDepth = null,
|
||||||
|
};
|
||||||
|
var mvcCompatibilityOptions = new MvcCompatibilityOptions
|
||||||
|
{
|
||||||
|
CompatibilityVersion = CompatibilityVersion.Version_2_2,
|
||||||
|
};
|
||||||
|
|
||||||
|
var configureOptions = new MvcOptionsConfigureCompatibilityOptions(
|
||||||
|
NullLoggerFactory.Instance,
|
||||||
|
Options.Create(mvcCompatibilityOptions));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
configureOptions.PostConfigure(string.Empty, mvcOptions);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(mvcOptions.MaxValidationDepth);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PostConfigure_DoesNotConfiguresMaxValidationDepth_WhenSetToValue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = 13;
|
||||||
|
var mvcOptions = new MvcOptions
|
||||||
|
{
|
||||||
|
MaxValidationDepth = expected,
|
||||||
|
};
|
||||||
|
var mvcCompatibilityOptions = new MvcCompatibilityOptions
|
||||||
|
{
|
||||||
|
CompatibilityVersion = CompatibilityVersion.Version_2_2,
|
||||||
|
};
|
||||||
|
|
||||||
|
var configureOptions = new MvcOptionsConfigureCompatibilityOptions(
|
||||||
|
NullLoggerFactory.Instance,
|
||||||
|
Options.Create(mvcCompatibilityOptions));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
configureOptions.PostConfigure(string.Empty, mvcOptions);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expected, mvcOptions.MaxValidationDepth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
// 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 Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.Infrastructure
|
||||||
|
{
|
||||||
|
public class NullableCompatibilitySwitchTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WithName_IsValueSetIsFalse()
|
||||||
|
{
|
||||||
|
// Arrange & Act
|
||||||
|
var @switch = new NullableCompatibilitySwitch<bool>("TestProperty");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(@switch.Value);
|
||||||
|
Assert.False(@switch.IsValueSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ValueNonInterface_SettingValueToNull_SetsIsValueSetToTrue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var @switch = new NullableCompatibilitySwitch<bool>("TestProperty");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
@switch.Value = null;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(@switch.Value);
|
||||||
|
Assert.True(@switch.IsValueSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ValueNonInterface_SettingValue_SetsIsValueSetToTrue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var @switch = new NullableCompatibilitySwitch<bool>("TestProperty");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
@switch.Value = false;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(@switch.Value);
|
||||||
|
Assert.True(@switch.IsValueSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ValueInterface_SettingValueToNull_SetsIsValueSetToTrue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var @switch = new NullableCompatibilitySwitch<bool>("TestProperty");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
((ICompatibilitySwitch)@switch).Value = null;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(@switch.Value);
|
||||||
|
Assert.True(@switch.IsValueSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ValueInterface_SettingValue_SetsIsValueSetToTrue()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var @switch = new NullableCompatibilitySwitch<bool>("TestProperty");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
((ICompatibilitySwitch)@switch).Value = true;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(@switch.Value);
|
||||||
|
Assert.True(@switch.IsValueSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -62,14 +62,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
var factory = GetModelBinderFactory(binder.Object);
|
var factory = GetModelBinderFactory(binder.Object);
|
||||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var controller = new TestController();
|
var controller = new TestController();
|
||||||
var parameterBinder = new ParameterBinder(
|
|
||||||
|
var parameterBinder = GetParameterBinder(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
factory,
|
factory,
|
||||||
new DefaultObjectValidator(
|
GetModelValidatorProvider(mockValidator.Object));
|
||||||
modelMetadataProvider,
|
|
||||||
new[] { GetModelValidatorProvider(mockValidator.Object) }),
|
|
||||||
_optionsAccessor,
|
|
||||||
NullLoggerFactory.Instance);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
|
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
|
||||||
|
|
@ -115,18 +112,16 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
var mockValidator = new Mock<IModelValidator>(MockBehavior.Strict);
|
var mockValidator = new Mock<IModelValidator>(MockBehavior.Strict);
|
||||||
mockValidator.Setup(o => o.Validate(It.IsAny<ModelValidationContext>()));
|
mockValidator.Setup(o => o.Validate(It.IsAny<ModelValidationContext>()));
|
||||||
|
|
||||||
|
var validatorProvider = GetModelValidatorProvider(mockValidator.Object);
|
||||||
var factory = GetModelBinderFactory(binder.Object);
|
var factory = GetModelBinderFactory(binder.Object);
|
||||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var controller = new TestController();
|
var controller = new TestController();
|
||||||
|
|
||||||
var parameterBinder = new ParameterBinder(
|
var parameterBinder = GetParameterBinder(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
factory,
|
factory,
|
||||||
new DefaultObjectValidator(
|
GetModelValidatorProvider(mockValidator.Object));
|
||||||
modelMetadataProvider,
|
|
||||||
new[] { GetModelValidatorProvider(mockValidator.Object) }),
|
|
||||||
_optionsAccessor,
|
|
||||||
NullLoggerFactory.Instance);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
|
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
|
||||||
|
|
@ -170,14 +165,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
var factory = GetModelBinderFactory(binder.Object);
|
var factory = GetModelBinderFactory(binder.Object);
|
||||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var parameterBinder = new ParameterBinder(
|
var parameterBinder = GetParameterBinder(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
factory,
|
factory);
|
||||||
new DefaultObjectValidator(
|
|
||||||
modelMetadataProvider,
|
|
||||||
new IModelValidatorProvider[] { }),
|
|
||||||
_optionsAccessor,
|
|
||||||
NullLoggerFactory.Instance);
|
|
||||||
|
|
||||||
var controllerContext = GetControllerContext(actionDescriptor);
|
var controllerContext = GetControllerContext(actionDescriptor);
|
||||||
var controller = new TestController();
|
var controller = new TestController();
|
||||||
|
|
@ -217,14 +207,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
var factory = GetModelBinderFactory(binder.Object);
|
var factory = GetModelBinderFactory(binder.Object);
|
||||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var parameterBinder = new ParameterBinder(
|
var parameterBinder = GetParameterBinder(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
factory,
|
factory);
|
||||||
new DefaultObjectValidator(
|
|
||||||
modelMetadataProvider,
|
|
||||||
new IModelValidatorProvider[] { }),
|
|
||||||
_optionsAccessor,
|
|
||||||
NullLoggerFactory.Instance);
|
|
||||||
|
|
||||||
var controllerContext = GetControllerContext(actionDescriptor);
|
var controllerContext = GetControllerContext(actionDescriptor);
|
||||||
var controller = new TestController();
|
var controller = new TestController();
|
||||||
|
|
@ -272,14 +257,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
var factory = GetModelBinderFactory(binder.Object);
|
var factory = GetModelBinderFactory(binder.Object);
|
||||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var parameterBinder = new ParameterBinder(
|
var parameterBinder = GetParameterBinder(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
factory,
|
factory);
|
||||||
new DefaultObjectValidator(
|
|
||||||
modelMetadataProvider,
|
|
||||||
new IModelValidatorProvider[] { }),
|
|
||||||
_optionsAccessor,
|
|
||||||
NullLoggerFactory.Instance);
|
|
||||||
|
|
||||||
var controllerContext = GetControllerContext(actionDescriptor);
|
var controllerContext = GetControllerContext(actionDescriptor);
|
||||||
var controller = new TestController();
|
var controller = new TestController();
|
||||||
|
|
@ -329,14 +309,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
.Setup(p => p.GetMetadataForParameter(ParameterInfos.NoAttributesParameterInfo))
|
.Setup(p => p.GetMetadataForParameter(ParameterInfos.NoAttributesParameterInfo))
|
||||||
.Returns(modelMetadata.Object);
|
.Returns(modelMetadata.Object);
|
||||||
|
|
||||||
var parameterBinder = new ParameterBinder(
|
var parameterBinder = GetParameterBinder(
|
||||||
mockMetadataProvider.Object,
|
mockMetadataProvider.Object,
|
||||||
factory,
|
factory);
|
||||||
new DefaultObjectValidator(
|
|
||||||
mockMetadataProvider.Object,
|
|
||||||
new IModelValidatorProvider[] { }),
|
|
||||||
_optionsAccessor,
|
|
||||||
NullLoggerFactory.Instance);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
|
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
|
||||||
|
|
@ -382,14 +357,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
.Setup(p => p.GetMetadataForType(typeof(Person)))
|
.Setup(p => p.GetMetadataForType(typeof(Person)))
|
||||||
.Returns(modelMetadata.Object);
|
.Returns(modelMetadata.Object);
|
||||||
|
|
||||||
var parameterBinder = new ParameterBinder(
|
var parameterBinder = GetParameterBinder(
|
||||||
mockMetadataProvider.Object,
|
mockMetadataProvider.Object,
|
||||||
factory,
|
factory);
|
||||||
new DefaultObjectValidator(
|
|
||||||
mockMetadataProvider.Object,
|
|
||||||
new IModelValidatorProvider[] { }),
|
|
||||||
_optionsAccessor,
|
|
||||||
NullLoggerFactory.Instance);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
|
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
|
||||||
|
|
@ -431,14 +401,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
.Returns(new[] { new ModelValidationResult("memberName", "some message") });
|
.Returns(new[] { new ModelValidationResult("memberName", "some message") });
|
||||||
|
|
||||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var parameterBinder = new ParameterBinder(
|
var parameterBinder = GetParameterBinder(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
factory,
|
factory,
|
||||||
new DefaultObjectValidator(
|
GetModelValidatorProvider(mockValidator.Object));
|
||||||
modelMetadataProvider,
|
|
||||||
new[] { GetModelValidatorProvider(mockValidator.Object) }),
|
|
||||||
_optionsAccessor,
|
|
||||||
NullLoggerFactory.Instance);
|
|
||||||
var controller = new TestController();
|
var controller = new TestController();
|
||||||
var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
var arguments = new Dictionary<string, object>(StringComparer.Ordinal);
|
||||||
|
|
||||||
|
|
@ -489,9 +456,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
var parameterBinder = new ParameterBinder(
|
var parameterBinder = new ParameterBinder(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
factory,
|
factory,
|
||||||
new DefaultObjectValidator(
|
GetObjectValidator(modelMetadataProvider, GetModelValidatorProvider(mockValidator.Object)),
|
||||||
modelMetadataProvider,
|
|
||||||
new[] { GetModelValidatorProvider(mockValidator.Object) }),
|
|
||||||
Options.Create(mvcOptions),
|
Options.Create(mvcOptions),
|
||||||
NullLoggerFactory.Instance);
|
NullLoggerFactory.Instance);
|
||||||
var controller = new TestController();
|
var controller = new TestController();
|
||||||
|
|
@ -538,14 +503,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
var factory = GetModelBinderFactory(binder.Object);
|
var factory = GetModelBinderFactory(binder.Object);
|
||||||
var controller = new TestController();
|
var controller = new TestController();
|
||||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var parameterBinder = new ParameterBinder(
|
var parameterBinder = GetParameterBinder(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
factory,
|
factory,
|
||||||
new DefaultObjectValidator(
|
GetModelValidatorProvider(mockValidator.Object));
|
||||||
modelMetadataProvider,
|
|
||||||
new IModelValidatorProvider[] { }),
|
|
||||||
_optionsAccessor,
|
|
||||||
NullLoggerFactory.Instance);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
|
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
|
||||||
|
|
@ -590,9 +551,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
var parameterBinder = new ParameterBinder(
|
var parameterBinder = new ParameterBinder(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
factory,
|
factory,
|
||||||
new DefaultObjectValidator(
|
GetObjectValidator(modelMetadataProvider, GetModelValidatorProvider(mockValidator.Object)),
|
||||||
modelMetadataProvider,
|
|
||||||
new[] { GetModelValidatorProvider(mockValidator.Object) }),
|
|
||||||
_optionsAccessor,
|
_optionsAccessor,
|
||||||
NullLoggerFactory.Instance);
|
NullLoggerFactory.Instance);
|
||||||
|
|
||||||
|
|
@ -689,9 +648,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
var parameterBinder = new ParameterBinder(
|
var parameterBinder = new ParameterBinder(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
factory,
|
factory,
|
||||||
new DefaultObjectValidator(
|
GetObjectValidator(modelMetadataProvider, GetModelValidatorProvider(mockValidator.Object)),
|
||||||
modelMetadataProvider,
|
|
||||||
new[] { GetModelValidatorProvider(mockValidator.Object) }),
|
|
||||||
_optionsAccessor,
|
_optionsAccessor,
|
||||||
NullLoggerFactory.Instance);
|
NullLoggerFactory.Instance);
|
||||||
|
|
||||||
|
|
@ -731,14 +688,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
var factory = GetModelBinderFactory("Hello");
|
var factory = GetModelBinderFactory("Hello");
|
||||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var parameterBinder = new ParameterBinder(
|
var parameterBinder = GetParameterBinder(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
factory,
|
factory);
|
||||||
new DefaultObjectValidator(
|
|
||||||
modelMetadataProvider,
|
|
||||||
new IModelValidatorProvider[] { }),
|
|
||||||
_optionsAccessor,
|
|
||||||
NullLoggerFactory.Instance);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
|
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
|
||||||
|
|
@ -776,14 +728,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
var expected = new List<string> { "Hello", "World", "!!" };
|
var expected = new List<string> { "Hello", "World", "!!" };
|
||||||
var factory = GetModelBinderFactory(expected);
|
var factory = GetModelBinderFactory(expected);
|
||||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var parameterBinder = new ParameterBinder(
|
var parameterBinder = GetParameterBinder(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
factory,
|
factory);
|
||||||
new DefaultObjectValidator(
|
|
||||||
modelMetadataProvider,
|
|
||||||
new IModelValidatorProvider[] { }),
|
|
||||||
_optionsAccessor,
|
|
||||||
NullLoggerFactory.Instance);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
|
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
|
||||||
|
|
@ -821,14 +768,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
var binder = new StubModelBinder(ModelBindingResult.Success(model: null));
|
var binder = new StubModelBinder(ModelBindingResult.Success(model: null));
|
||||||
var factory = GetModelBinderFactory(binder);
|
var factory = GetModelBinderFactory(binder);
|
||||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var parameterBinder = new ParameterBinder(
|
var parameterBinder = GetParameterBinder(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
factory,
|
factory);
|
||||||
new DefaultObjectValidator(
|
|
||||||
modelMetadataProvider,
|
|
||||||
new IModelValidatorProvider[] { }),
|
|
||||||
_optionsAccessor,
|
|
||||||
NullLoggerFactory.Instance);
|
|
||||||
|
|
||||||
// Some non default value.
|
// Some non default value.
|
||||||
controller.NonNullableProperty = -1;
|
controller.NonNullableProperty = -1;
|
||||||
|
|
@ -867,14 +809,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
var binder = new StubModelBinder(ModelBindingResult.Success(model: null));
|
var binder = new StubModelBinder(ModelBindingResult.Success(model: null));
|
||||||
var factory = GetModelBinderFactory(binder);
|
var factory = GetModelBinderFactory(binder);
|
||||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var parameterBinder = new ParameterBinder(
|
var parameterBinder = GetParameterBinder(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
factory,
|
factory);
|
||||||
new DefaultObjectValidator(
|
|
||||||
modelMetadataProvider,
|
|
||||||
new IModelValidatorProvider[] { }),
|
|
||||||
_optionsAccessor,
|
|
||||||
NullLoggerFactory.Instance);
|
|
||||||
|
|
||||||
// Some non default value.
|
// Some non default value.
|
||||||
controller.NullableProperty = -1;
|
controller.NullableProperty = -1;
|
||||||
|
|
@ -934,14 +871,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
var binder = new StubModelBinder(ModelBindingResult.Success(model: null));
|
var binder = new StubModelBinder(ModelBindingResult.Success(model: null));
|
||||||
var factory = GetModelBinderFactory(binder);
|
var factory = GetModelBinderFactory(binder);
|
||||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var parameterBinder = new ParameterBinder(
|
var parameterBinder = GetParameterBinder(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
factory,
|
factory);
|
||||||
new DefaultObjectValidator(
|
|
||||||
modelMetadataProvider,
|
|
||||||
new IModelValidatorProvider[] { }),
|
|
||||||
_optionsAccessor,
|
|
||||||
NullLoggerFactory.Instance);
|
|
||||||
|
|
||||||
// Some non default value.
|
// Some non default value.
|
||||||
controller.NullableProperty = -1;
|
controller.NullableProperty = -1;
|
||||||
|
|
@ -1002,14 +934,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
var binder = new StubModelBinder(ModelBindingResult.Success(model: null));
|
var binder = new StubModelBinder(ModelBindingResult.Success(model: null));
|
||||||
var factory = GetModelBinderFactory(binder);
|
var factory = GetModelBinderFactory(binder);
|
||||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var parameterBinder = new ParameterBinder(
|
var parameterBinder = GetParameterBinder(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
factory,
|
factory);
|
||||||
new DefaultObjectValidator(
|
|
||||||
modelMetadataProvider,
|
|
||||||
new IModelValidatorProvider[] { }),
|
|
||||||
_optionsAccessor,
|
|
||||||
NullLoggerFactory.Instance);
|
|
||||||
|
|
||||||
// Some non default value.
|
// Some non default value.
|
||||||
controller.NullableProperty = -1;
|
controller.NullableProperty = -1;
|
||||||
|
|
@ -1094,14 +1021,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
var factory = GetModelBinderFactory(inputValue);
|
var factory = GetModelBinderFactory(inputValue);
|
||||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var parameterBinder = new ParameterBinder(
|
var parameterBinder = GetParameterBinder(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
factory,
|
factory);
|
||||||
new DefaultObjectValidator(
|
|
||||||
modelMetadataProvider,
|
|
||||||
new IModelValidatorProvider[] { }),
|
|
||||||
_optionsAccessor,
|
|
||||||
NullLoggerFactory.Instance);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
|
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
|
||||||
|
|
@ -1174,14 +1096,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
controllerContext.ValueProviderFactories.Add(new SimpleValueProviderFactory());
|
controllerContext.ValueProviderFactories.Add(new SimpleValueProviderFactory());
|
||||||
|
|
||||||
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
var modelMetadataProvider = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var parameterBinder = new ParameterBinder(
|
var parameterBinder = GetParameterBinder(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
factory,
|
factory);
|
||||||
new DefaultObjectValidator(
|
|
||||||
modelMetadataProvider,
|
|
||||||
new IModelValidatorProvider[] { }),
|
|
||||||
_optionsAccessor,
|
|
||||||
NullLoggerFactory.Instance);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
|
var binderDelegate = ControllerBinderDelegateProvider.CreateBinderDelegate(
|
||||||
|
|
@ -1278,7 +1195,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
var parameterBinder = new Mock<ParameterBinder>(
|
var parameterBinder = new Mock<ParameterBinder>(
|
||||||
new EmptyModelMetadataProvider(),
|
new EmptyModelMetadataProvider(),
|
||||||
factory,
|
factory,
|
||||||
new DefaultObjectValidator(modelMetadataProvider, new[] { modelValidatorProvider }),
|
GetObjectValidator(modelMetadataProvider, modelValidatorProvider),
|
||||||
_optionsAccessor,
|
_optionsAccessor,
|
||||||
NullLoggerFactory.Instance);
|
NullLoggerFactory.Instance);
|
||||||
parameterBinder.Setup(p => p.BindModelAsync(
|
parameterBinder.Setup(p => p.BindModelAsync(
|
||||||
|
|
@ -1415,16 +1332,10 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ParameterBinder GetParameterBinder(
|
private static ParameterBinder GetParameterBinder(
|
||||||
IModelBinderFactory factory = null,
|
|
||||||
IObjectModelValidator validator = null,
|
|
||||||
IModelMetadataProvider modelMetadataProvider = null,
|
IModelMetadataProvider modelMetadataProvider = null,
|
||||||
|
IModelBinderFactory factory = null,
|
||||||
IModelValidatorProvider modelValidatorProvider = null)
|
IModelValidatorProvider modelValidatorProvider = null)
|
||||||
{
|
{
|
||||||
if (validator == null)
|
|
||||||
{
|
|
||||||
validator = CreateObjectValidator();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (factory == null)
|
if (factory == null)
|
||||||
{
|
{
|
||||||
factory = TestModelBinderFactory.CreateDefault();
|
factory = TestModelBinderFactory.CreateDefault();
|
||||||
|
|
@ -1436,9 +1347,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
var metadataProvider = modelMetadataProvider ?? TestModelMetadataProvider.CreateDefaultProvider();
|
var metadataProvider = modelMetadataProvider ?? TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
var objectModelValidator = new DefaultObjectValidator(
|
var objectModelValidator = GetObjectValidator(modelMetadataProvider, modelValidatorProvider);
|
||||||
metadataProvider,
|
|
||||||
new[] { modelValidatorProvider });
|
|
||||||
|
|
||||||
return new ParameterBinder(
|
return new ParameterBinder(
|
||||||
metadataProvider,
|
metadataProvider,
|
||||||
|
|
@ -1448,16 +1357,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
NullLoggerFactory.Instance);
|
NullLoggerFactory.Instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IObjectModelValidator CreateObjectValidator()
|
private static DefaultObjectValidator GetObjectValidator(
|
||||||
|
IModelMetadataProvider modelMetadataProvider,
|
||||||
|
IModelValidatorProvider validatorProvider)
|
||||||
{
|
{
|
||||||
var mockValidator = new Mock<IObjectModelValidator>(MockBehavior.Strict);
|
return new DefaultObjectValidator(
|
||||||
mockValidator
|
modelMetadataProvider,
|
||||||
.Setup(o => o.Validate(
|
new[] { validatorProvider },
|
||||||
It.IsAny<ActionContext>(),
|
_options);
|
||||||
It.IsAny<ValidationStateDictionary>(),
|
|
||||||
It.IsAny<string>(),
|
|
||||||
It.IsAny<object>()));
|
|
||||||
return mockValidator.Object;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// No need for bind-related attributes on properties in this controller class. Properties are added directly
|
// No need for bind-related attributes on properties in this controller class. Properties are added directly
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||||
|
|
@ -22,7 +23,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
{
|
{
|
||||||
public class DefaultObjectValidatorTests
|
public class DefaultObjectValidatorTests
|
||||||
{
|
{
|
||||||
private IModelMetadataProvider MetadataProvider { get; } = TestModelMetadataProvider.CreateDefaultProvider();
|
private readonly MvcOptions _options = new MvcOptions();
|
||||||
|
|
||||||
|
private ModelMetadataProvider MetadataProvider { get; } = TestModelMetadataProvider.CreateDefaultProvider();
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Validate_SimpleValueType_Valid_WithPrefix()
|
public void Validate_SimpleValueType_Valid_WithPrefix()
|
||||||
|
|
@ -1258,6 +1261,70 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Validate_Throws_IfValidationDepthExceedsMaxDepth()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var maxDepth = 5;
|
||||||
|
var expected = $"ValidationVisitor exceeded the maximum configured validation depth '{maxDepth}' when validating property '{nameof(DepthObject.Depth)}' on type '{typeof(DepthObject)}'. " +
|
||||||
|
"This may indicate a very deep or infinitely recursive object graph. Consider modifying 'MvcOptions.MaxValidationDepth' or suppressing validation on the model type.";
|
||||||
|
_options.MaxValidationDepth = maxDepth;
|
||||||
|
var actionContext = new ActionContext();
|
||||||
|
var validator = CreateValidator();
|
||||||
|
var model = new DepthObject(maxDepth);
|
||||||
|
var validationState = new ValidationStateDictionary
|
||||||
|
{
|
||||||
|
{ model, new ValidationStateEntry() }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var ex = Assert.Throws<InvalidOperationException>(() => validator.Validate(actionContext, validationState, prefix: string.Empty, model));
|
||||||
|
Assert.Equal(expected, ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Validate_WorksIfObjectGraphIsSmallerThanMaxDepth()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var maxDepth = 5;
|
||||||
|
_options.MaxValidationDepth = maxDepth;
|
||||||
|
var actionContext = new ActionContext();
|
||||||
|
var validator = CreateValidator();
|
||||||
|
var model = new DepthObject(maxDepth - 1);
|
||||||
|
var validationState = new ValidationStateDictionary
|
||||||
|
{
|
||||||
|
{ model, new ValidationStateEntry() }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
validator.Validate(actionContext, validationState, prefix: string.Empty, model);
|
||||||
|
Assert.True(actionContext.ModelState.IsValid);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Validate_Throws_WithMaxDepth_1()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var maxDepth = 1;
|
||||||
|
var expected = $"ValidationVisitor exceeded the maximum configured validation depth '{maxDepth}' when validating property '{nameof(DepthObject.Depth)}' on type '{typeof(DepthObject)}'. " +
|
||||||
|
"This may indicate a very deep or infinitely recursive object graph. Consider modifying 'MvcOptions.MaxValidationDepth' or suppressing validation on the model type.";
|
||||||
|
_options.MaxValidationDepth = maxDepth;
|
||||||
|
var actionContext = new ActionContext();
|
||||||
|
var validator = CreateValidator();
|
||||||
|
var model = new DepthObject(maxDepth + 1);
|
||||||
|
var validationState = new ValidationStateDictionary
|
||||||
|
{
|
||||||
|
{ model, new ValidationStateEntry() }
|
||||||
|
};
|
||||||
|
var method = GetType().GetMethod(nameof(Validate_Throws_ForTopLeveleMetadataData), BindingFlags.NonPublic | BindingFlags.Instance);
|
||||||
|
var metadata = MetadataProvider.GetMetadataForParameter(method.GetParameters()[0]);
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var ex = Assert.Throws<InvalidOperationException>(() => validator.Validate(actionContext, validationState, prefix: string.Empty, model));
|
||||||
|
Assert.Equal(expected, ex.Message);
|
||||||
|
Assert.NotNull(ex.HelpLink);
|
||||||
|
}
|
||||||
|
|
||||||
private static DefaultObjectValidator CreateValidator(Type excludedType)
|
private static DefaultObjectValidator CreateValidator(Type excludedType)
|
||||||
{
|
{
|
||||||
var excludeFilters = new List<SuppressChildValidationMetadataProvider>();
|
var excludeFilters = new List<SuppressChildValidationMetadataProvider>();
|
||||||
|
|
@ -1268,14 +1335,14 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
|
|
||||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(excludeFilters.ToArray());
|
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(excludeFilters.ToArray());
|
||||||
var validatorProviders = TestModelValidatorProvider.CreateDefaultProvider().ValidatorProviders;
|
var validatorProviders = TestModelValidatorProvider.CreateDefaultProvider().ValidatorProviders;
|
||||||
return new DefaultObjectValidator(metadataProvider, validatorProviders);
|
return new DefaultObjectValidator(metadataProvider, validatorProviders, new MvcOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DefaultObjectValidator CreateValidator(params IMetadataDetailsProvider[] providers)
|
private DefaultObjectValidator CreateValidator(params IMetadataDetailsProvider[] providers)
|
||||||
{
|
{
|
||||||
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(providers);
|
var metadataProvider = TestModelMetadataProvider.CreateDefaultProvider(providers);
|
||||||
var validatorProviders = TestModelValidatorProvider.CreateDefaultProvider().ValidatorProviders;
|
var validatorProviders = TestModelValidatorProvider.CreateDefaultProvider().ValidatorProviders;
|
||||||
return new DefaultObjectValidator(metadataProvider, validatorProviders);
|
return new DefaultObjectValidator(metadataProvider, validatorProviders, _options);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AssertKeysEqual(ModelStateDictionary modelState, params string[] keys)
|
private static void AssertKeysEqual(ModelStateDictionary modelState, params string[] keys)
|
||||||
|
|
@ -1398,6 +1465,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
void DoSomething();
|
void DoSomething();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Validate_Throws_ForTopLeveleMetadataData(DepthObject depthObject) { }
|
||||||
|
|
||||||
// Custom validation attribute that returns multiple entries in ValidationResult.MemberNames and those member
|
// Custom validation attribute that returns multiple entries in ValidationResult.MemberNames and those member
|
||||||
// names are indexers. An example scenario is an attribute that confirms all entries in a list are unique.
|
// names are indexers. An example scenario is an attribute that confirms all entries in a list are unique.
|
||||||
private class InvalidItemsAttribute : ValidationAttribute
|
private class InvalidItemsAttribute : ValidationAttribute
|
||||||
|
|
@ -1442,5 +1511,30 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
{
|
{
|
||||||
public string City { get; set; }
|
public string City { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class DepthObject
|
||||||
|
{
|
||||||
|
public DepthObject(int maxAllowedDepth, int depth = 0)
|
||||||
|
{
|
||||||
|
MaxAllowedDepth = maxAllowedDepth;
|
||||||
|
Depth = depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Depth { get; }
|
||||||
|
public int MaxAllowedDepth { get; }
|
||||||
|
|
||||||
|
public DepthObject Instance
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Depth == MaxAllowedDepth - 1)
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DepthObject(MaxAllowedDepth, Depth + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||||
metadataProvider,
|
metadataProvider,
|
||||||
GetModelBinderFactory(binderProviders),
|
GetModelBinderFactory(binderProviders),
|
||||||
valueProvider,
|
valueProvider,
|
||||||
new DefaultObjectValidator(metadataProvider, new[] { validator }));
|
new DefaultObjectValidator(metadataProvider, new[] { validator }, new MvcOptions()));
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result);
|
Assert.False(result);
|
||||||
|
|
@ -124,7 +124,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||||
metadataProvider,
|
metadataProvider,
|
||||||
GetModelBinderFactory(binderProviders),
|
GetModelBinderFactory(binderProviders),
|
||||||
valueProvider,
|
valueProvider,
|
||||||
new DefaultObjectValidator(metadataProvider, new[] { validator }));
|
new DefaultObjectValidator(metadataProvider, new[] { validator }, new MvcOptions()));
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(result);
|
Assert.True(result);
|
||||||
|
|
@ -202,7 +202,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||||
metadataProvider,
|
metadataProvider,
|
||||||
GetModelBinderFactory(binderProviders),
|
GetModelBinderFactory(binderProviders),
|
||||||
valueProvider,
|
valueProvider,
|
||||||
new DefaultObjectValidator(metadataProvider, new[] { validator }),
|
new DefaultObjectValidator(metadataProvider, new[] { validator }, new MvcOptions()),
|
||||||
propertyFilter);
|
propertyFilter);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -278,7 +278,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||||
TestModelMetadataProvider.CreateDefaultProvider(),
|
TestModelMetadataProvider.CreateDefaultProvider(),
|
||||||
GetModelBinderFactory(binderProviders),
|
GetModelBinderFactory(binderProviders),
|
||||||
valueProvider,
|
valueProvider,
|
||||||
new DefaultObjectValidator(metadataProvider, new[] { validator }),
|
new DefaultObjectValidator(metadataProvider, new[] { validator }, new MvcOptions()),
|
||||||
m => m.IncludedProperty,
|
m => m.IncludedProperty,
|
||||||
m => m.MyProperty);
|
m => m.MyProperty);
|
||||||
|
|
||||||
|
|
@ -329,7 +329,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||||
metadataProvider,
|
metadataProvider,
|
||||||
GetModelBinderFactory(binderProviders),
|
GetModelBinderFactory(binderProviders),
|
||||||
valueProvider,
|
valueProvider,
|
||||||
new DefaultObjectValidator(metadataProvider, new[] { validator }));
|
new DefaultObjectValidator(metadataProvider, new[] { validator }, new MvcOptions()));
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
// Includes everything.
|
// Includes everything.
|
||||||
|
|
@ -531,7 +531,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||||
metadataProvider,
|
metadataProvider,
|
||||||
GetModelBinderFactory(binderProviders),
|
GetModelBinderFactory(binderProviders),
|
||||||
valueProvider,
|
valueProvider,
|
||||||
new DefaultObjectValidator(metadataProvider, new[] { validator }),
|
new DefaultObjectValidator(metadataProvider, new[] { validator }, new MvcOptions()),
|
||||||
propertyFilter);
|
propertyFilter);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
|
@ -599,7 +599,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||||
metadataProvider,
|
metadataProvider,
|
||||||
GetModelBinderFactory(binderProviders),
|
GetModelBinderFactory(binderProviders),
|
||||||
valueProvider,
|
valueProvider,
|
||||||
new DefaultObjectValidator(metadataProvider, new[] { validator }));
|
new DefaultObjectValidator(metadataProvider, new[] { validator }, new MvcOptions()));
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(result);
|
Assert.True(result);
|
||||||
|
|
|
||||||
|
|
@ -522,7 +522,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||||
Mock.Of<IModelBinderFactory>(),
|
Mock.Of<IModelBinderFactory>(),
|
||||||
new DefaultObjectValidator(
|
new DefaultObjectValidator(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
new[] { TestModelValidatorProvider.CreateDefaultProvider() }),
|
new[] { TestModelValidatorProvider.CreateDefaultProvider() },
|
||||||
|
new MvcOptions()),
|
||||||
_optionsAccessor,
|
_optionsAccessor,
|
||||||
NullLoggerFactory.Instance);
|
NullLoggerFactory.Instance);
|
||||||
|
|
||||||
|
|
@ -576,7 +577,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||||
Mock.Of<IModelBinderFactory>(),
|
Mock.Of<IModelBinderFactory>(),
|
||||||
new DefaultObjectValidator(
|
new DefaultObjectValidator(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
new[] { TestModelValidatorProvider.CreateDefaultProvider() }),
|
new[] { TestModelValidatorProvider.CreateDefaultProvider() },
|
||||||
|
new MvcOptions()),
|
||||||
_optionsAccessor,
|
_optionsAccessor,
|
||||||
NullLoggerFactory.Instance);
|
NullLoggerFactory.Instance);
|
||||||
|
|
||||||
|
|
@ -630,7 +632,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||||
Mock.Of<IModelBinderFactory>(),
|
Mock.Of<IModelBinderFactory>(),
|
||||||
new DefaultObjectValidator(
|
new DefaultObjectValidator(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
new[] { TestModelValidatorProvider.CreateDefaultProvider() }),
|
new[] { TestModelValidatorProvider.CreateDefaultProvider() },
|
||||||
|
new MvcOptions()),
|
||||||
_optionsAccessor,
|
_optionsAccessor,
|
||||||
NullLoggerFactory.Instance);
|
NullLoggerFactory.Instance);
|
||||||
|
|
||||||
|
|
@ -683,7 +686,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||||
Mock.Of<IModelBinderFactory>(),
|
Mock.Of<IModelBinderFactory>(),
|
||||||
new DefaultObjectValidator(
|
new DefaultObjectValidator(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
new[] { TestModelValidatorProvider.CreateDefaultProvider() }),
|
new[] { TestModelValidatorProvider.CreateDefaultProvider() },
|
||||||
|
new MvcOptions()),
|
||||||
_optionsAccessor,
|
_optionsAccessor,
|
||||||
NullLoggerFactory.Instance);
|
NullLoggerFactory.Instance);
|
||||||
|
|
||||||
|
|
@ -741,7 +745,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||||
Mock.Of<IModelBinderFactory>(),
|
Mock.Of<IModelBinderFactory>(),
|
||||||
new DefaultObjectValidator(
|
new DefaultObjectValidator(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
new[] { TestModelValidatorProvider.CreateDefaultProvider() }),
|
new[] { TestModelValidatorProvider.CreateDefaultProvider() },
|
||||||
|
new MvcOptions()),
|
||||||
_optionsAccessor,
|
_optionsAccessor,
|
||||||
NullLoggerFactory.Instance);
|
NullLoggerFactory.Instance);
|
||||||
|
|
||||||
|
|
@ -802,7 +807,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||||
Mock.Of<IModelBinderFactory>(),
|
Mock.Of<IModelBinderFactory>(),
|
||||||
new DefaultObjectValidator(
|
new DefaultObjectValidator(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
new[] { TestModelValidatorProvider.CreateDefaultProvider() }),
|
new[] { TestModelValidatorProvider.CreateDefaultProvider() },
|
||||||
|
new MvcOptions()),
|
||||||
_optionsAccessor,
|
_optionsAccessor,
|
||||||
NullLoggerFactory.Instance);
|
NullLoggerFactory.Instance);
|
||||||
|
|
||||||
|
|
@ -868,7 +874,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||||
Mock.Of<IModelBinderFactory>(),
|
Mock.Of<IModelBinderFactory>(),
|
||||||
new DefaultObjectValidator(
|
new DefaultObjectValidator(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
new[] { TestModelValidatorProvider.CreateDefaultProvider() }),
|
new[] { TestModelValidatorProvider.CreateDefaultProvider() },
|
||||||
|
new MvcOptions()),
|
||||||
_optionsAccessor,
|
_optionsAccessor,
|
||||||
NullLoggerFactory.Instance);
|
NullLoggerFactory.Instance);
|
||||||
|
|
||||||
|
|
@ -947,7 +954,8 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||||
mockModelBinderFactory.Object,
|
mockModelBinderFactory.Object,
|
||||||
new DefaultObjectValidator(
|
new DefaultObjectValidator(
|
||||||
mockModelMetadataProvider.Object,
|
mockModelMetadataProvider.Object,
|
||||||
new[] { GetModelValidatorProvider(validator) }),
|
new[] { GetModelValidatorProvider(validator) },
|
||||||
|
new MvcOptions()),
|
||||||
optionsAccessor,
|
optionsAccessor,
|
||||||
loggerFactory ?? NullLoggerFactory.Instance);
|
loggerFactory ?? NullLoggerFactory.Instance);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
// 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.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using FormatterWebSite;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Testing.xunit;
|
using Microsoft.AspNetCore.Testing.xunit;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
@ -243,5 +245,22 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
var responseContent = await response.Content.ReadAsStringAsync();
|
var responseContent = await response.Content.ReadAsStringAsync();
|
||||||
Assert.Equal(expectedError, actual: responseContent);
|
Assert.Equal(expectedError, actual: responseContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test for https://github.com/aspnet/Mvc/issues/7357
|
||||||
|
[Fact]
|
||||||
|
public async Task ValidationThrowsError_WhenValidationExceedsMaxValidationDepth()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expected = $"ValidationVisitor exceeded the maximum configured validation depth '32' when validating property 'Value' on type '{typeof(RecursiveIdentifier)}'. " +
|
||||||
|
"This may indicate a very deep or infinitely recursive object graph. Consider modifying 'MvcOptions.MaxValidationDepth' or suppressing validation on the model type.";
|
||||||
|
var requestMessage = new HttpRequestMessage(HttpMethod.Post, "Validation/ValidationThrowsError_WhenValidationExceedsMaxValidationDepth")
|
||||||
|
{
|
||||||
|
Content = new StringContent(@"{ ""Id"": ""S-1-5-21-1004336348-1177238915-682003330-512"" }", Encoding.UTF8, "application/json"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => Client.SendAsync(requestMessage));
|
||||||
|
Assert.Equal(expected, ex.Message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -9,7 +9,6 @@ using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
using Microsoft.AspNetCore.Mvc.ApplicationParts;
|
||||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
using Microsoft.AspNetCore.Mvc.Internal;
|
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
|
@ -79,7 +78,8 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
||||||
new ModelBinderFactory(metadataProvider, options, serviceProvider),
|
new ModelBinderFactory(metadataProvider, options, serviceProvider),
|
||||||
new DefaultObjectValidator(
|
new DefaultObjectValidator(
|
||||||
metadataProvider,
|
metadataProvider,
|
||||||
new[] { new CompositeModelValidatorProvider(GetModelValidatorProviders(options)) }),
|
new[] { new CompositeModelValidatorProvider(GetModelValidatorProviders(options)) },
|
||||||
|
options.Value),
|
||||||
options,
|
options,
|
||||||
NullLoggerFactory.Instance);
|
NullLoggerFactory.Instance);
|
||||||
}
|
}
|
||||||
|
|
@ -102,7 +102,8 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
||||||
new ModelBinderFactory(metadataProvider, options, services),
|
new ModelBinderFactory(metadataProvider, options, services),
|
||||||
new DefaultObjectValidator(
|
new DefaultObjectValidator(
|
||||||
metadataProvider,
|
metadataProvider,
|
||||||
new[] { new CompositeModelValidatorProvider(GetModelValidatorProviders(options)) }),
|
new[] { new CompositeModelValidatorProvider(GetModelValidatorProviders(options)) },
|
||||||
|
options.Value),
|
||||||
options,
|
options,
|
||||||
NullLoggerFactory.Instance);
|
NullLoggerFactory.Instance);
|
||||||
}
|
}
|
||||||
|
|
@ -127,7 +128,8 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
||||||
{
|
{
|
||||||
return new DefaultObjectValidator(
|
return new DefaultObjectValidator(
|
||||||
metadataProvider,
|
metadataProvider,
|
||||||
GetModelValidatorProviders(options));
|
GetModelValidatorProviders(options),
|
||||||
|
options?.Value ?? new MvcOptions());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IList<IModelValidatorProvider> GetModelValidatorProviders(IOptions<MvcOptions> options)
|
private static IList<IModelValidatorProvider> GetModelValidatorProviders(IOptions<MvcOptions> options)
|
||||||
|
|
|
||||||
|
|
@ -1555,7 +1555,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
return new ParameterBinder(
|
return new ParameterBinder(
|
||||||
metadataProvider,
|
metadataProvider,
|
||||||
factory,
|
factory,
|
||||||
new DefaultObjectValidator(metadataProvider, new[] { validator }),
|
new DefaultObjectValidator(metadataProvider, new[] { validator }, new MvcOptions()),
|
||||||
Options.Create(mvcOptions),
|
Options.Create(mvcOptions),
|
||||||
NullLoggerFactory.Instance);
|
NullLoggerFactory.Instance);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -553,7 +553,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
modelBinderFactory,
|
modelBinderFactory,
|
||||||
new DefaultObjectValidator(
|
new DefaultObjectValidator(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
new[] { TestModelValidatorProvider.CreateDefaultProvider() }),
|
new[] { TestModelValidatorProvider.CreateDefaultProvider() },
|
||||||
|
new MvcOptions()),
|
||||||
_optionsAccessor,
|
_optionsAccessor,
|
||||||
NullLoggerFactory.Instance);
|
NullLoggerFactory.Instance);
|
||||||
|
|
||||||
|
|
@ -678,7 +679,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
modelBinderFactory,
|
modelBinderFactory,
|
||||||
new DefaultObjectValidator(
|
new DefaultObjectValidator(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
new[] { TestModelValidatorProvider.CreateDefaultProvider() }),
|
new[] { TestModelValidatorProvider.CreateDefaultProvider() },
|
||||||
|
new MvcOptions()),
|
||||||
_optionsAccessor,
|
_optionsAccessor,
|
||||||
NullLoggerFactory.Instance);
|
NullLoggerFactory.Instance);
|
||||||
|
|
||||||
|
|
@ -732,7 +734,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
modelBinderFactory,
|
modelBinderFactory,
|
||||||
new DefaultObjectValidator(
|
new DefaultObjectValidator(
|
||||||
modelMetadataProvider,
|
modelMetadataProvider,
|
||||||
new[] { TestModelValidatorProvider.CreateDefaultProvider() }),
|
new[] { TestModelValidatorProvider.CreateDefaultProvider() },
|
||||||
|
new MvcOptions()),
|
||||||
Options.Create(mvcOptions),
|
Options.Create(mvcOptions),
|
||||||
NullLoggerFactory.Instance);
|
NullLoggerFactory.Instance);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTest
|
||||||
Assert.False(jsonOptions.AllowInputFormatterExceptionMessages);
|
Assert.False(jsonOptions.AllowInputFormatterExceptionMessages);
|
||||||
Assert.False(razorPagesOptions.AllowAreas);
|
Assert.False(razorPagesOptions.AllowAreas);
|
||||||
Assert.False(mvcOptions.EnableEndpointRouting);
|
Assert.False(mvcOptions.EnableEndpointRouting);
|
||||||
|
Assert.Null(mvcOptions.MaxValidationDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -65,6 +66,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTest
|
||||||
Assert.True(jsonOptions.AllowInputFormatterExceptionMessages);
|
Assert.True(jsonOptions.AllowInputFormatterExceptionMessages);
|
||||||
Assert.True(razorPagesOptions.AllowAreas);
|
Assert.True(razorPagesOptions.AllowAreas);
|
||||||
Assert.False(mvcOptions.EnableEndpointRouting);
|
Assert.False(mvcOptions.EnableEndpointRouting);
|
||||||
|
Assert.Null(mvcOptions.MaxValidationDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -90,6 +92,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTest
|
||||||
Assert.True(jsonOptions.AllowInputFormatterExceptionMessages);
|
Assert.True(jsonOptions.AllowInputFormatterExceptionMessages);
|
||||||
Assert.True(razorPagesOptions.AllowAreas);
|
Assert.True(razorPagesOptions.AllowAreas);
|
||||||
Assert.True(mvcOptions.EnableEndpointRouting);
|
Assert.True(mvcOptions.EnableEndpointRouting);
|
||||||
|
Assert.Equal(32, mvcOptions.MaxValidationDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -115,6 +118,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTest
|
||||||
Assert.True(jsonOptions.AllowInputFormatterExceptionMessages);
|
Assert.True(jsonOptions.AllowInputFormatterExceptionMessages);
|
||||||
Assert.True(razorPagesOptions.AllowAreas);
|
Assert.True(razorPagesOptions.AllowAreas);
|
||||||
Assert.True(mvcOptions.EnableEndpointRouting);
|
Assert.True(mvcOptions.EnableEndpointRouting);
|
||||||
|
Assert.Equal(32, mvcOptions.MaxValidationDepth);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This just does the minimum needed to be able to resolve these options.
|
// This just does the minimum needed to be able to resolve these options.
|
||||||
|
|
|
||||||
|
|
@ -458,7 +458,7 @@ namespace Microsoft.AspNetCore.Mvc.Test
|
||||||
{
|
{
|
||||||
ControllerContext = controllerContext,
|
ControllerContext = controllerContext,
|
||||||
MetadataProvider = metadataProvider,
|
MetadataProvider = metadataProvider,
|
||||||
ObjectValidator = new DefaultObjectValidator(metadataProvider, valiatorProviders),
|
ObjectValidator = new DefaultObjectValidator(metadataProvider, valiatorProviders, new MvcOptions()),
|
||||||
TempData = tempData,
|
TempData = tempData,
|
||||||
ViewData = viewData,
|
ViewData = viewData,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -78,5 +78,11 @@ namespace FormatterWebSite
|
||||||
|
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public IActionResult ValidationThrowsError_WhenValidationExceedsMaxValidationDepth([FromBody] InfinitelyRecursiveModel model)
|
||||||
|
{
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace FormatterWebSite
|
||||||
|
{
|
||||||
|
public class InfinitelyRecursiveModel
|
||||||
|
{
|
||||||
|
[JsonConverter(typeof(StringIdentifierConverter))]
|
||||||
|
public RecursiveIdentifier Id { get; set; }
|
||||||
|
|
||||||
|
private class StringIdentifierConverter : JsonConverter
|
||||||
|
{
|
||||||
|
public override bool CanConvert(Type objectType) => objectType == typeof(RecursiveIdentifier);
|
||||||
|
|
||||||
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
return new RecursiveIdentifier(reader.Value.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
namespace FormatterWebSite
|
||||||
|
{
|
||||||
|
// A System.Security.Principal.SecurityIdentifier like type that works on xplat
|
||||||
|
public class RecursiveIdentifier
|
||||||
|
{
|
||||||
|
public RecursiveIdentifier(string identifier)
|
||||||
|
{
|
||||||
|
Value = identifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Value { get; }
|
||||||
|
|
||||||
|
public RecursiveIdentifier AccountIdentifier => new RecursiveIdentifier(Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,12 +19,12 @@ namespace FormatterWebSite
|
||||||
|
|
||||||
options.InputFormatters.Add(new StringInputFormatter());
|
options.InputFormatters.Add(new StringInputFormatter());
|
||||||
})
|
})
|
||||||
.AddXmlDataContractSerializerFormatters();
|
.AddXmlDataContractSerializerFormatters()
|
||||||
|
.SetCompatibilityVersion(CompatibilityVersion.Latest);
|
||||||
|
|
||||||
services.Configure<MvcJsonOptions>(options => { options.SerializerSettings.Converters.Insert(0, new IModelConverter()); });
|
services.Configure<MvcJsonOptions>(options => { options.SerializerSettings.Converters.Insert(0, new IModelConverter()); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void Configure(IApplicationBuilder app)
|
public void Configure(IApplicationBuilder app)
|
||||||
{
|
{
|
||||||
app.UseMvc(routes =>
|
app.UseMvc(routes =>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue