[Fixes #6858] Changes to MvcOption's settings (SuppressInputFormatterBuffering & AllowBindingUndefinedValueToEnumType) are not taking affect
This commit is contained in:
parent
3ca5955b31
commit
629f87181a
|
|
@ -46,7 +46,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
options.ModelBinderProviders.Add(new BodyModelBinderProvider(options.InputFormatters, _readerFactory, _loggerFactory, options));
|
||||
options.ModelBinderProviders.Add(new HeaderModelBinderProvider());
|
||||
options.ModelBinderProviders.Add(new FloatingPointTypeModelBinderProvider());
|
||||
options.ModelBinderProviders.Add(new EnumTypeModelBinderProvider(options.AllowBindingUndefinedValueToEnumType));
|
||||
options.ModelBinderProviders.Add(new EnumTypeModelBinderProvider(options));
|
||||
options.ModelBinderProviders.Add(new SimpleTypeModelBinderProvider());
|
||||
options.ModelBinderProviders.Add(new CancellationTokenModelBinderProvider());
|
||||
options.ModelBinderProviders.Add(new ByteArrayModelBinderProvider());
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
|
|||
/// </summary>
|
||||
public class EnumTypeModelBinderProvider : IModelBinderProvider
|
||||
{
|
||||
private readonly bool _allowBindingUndefinedValueToEnumType;
|
||||
private readonly MvcOptions _options;
|
||||
|
||||
public EnumTypeModelBinderProvider(bool allowBindingUndefinedValueToEnumType)
|
||||
public EnumTypeModelBinderProvider(MvcOptions options)
|
||||
{
|
||||
_allowBindingUndefinedValueToEnumType = allowBindingUndefinedValueToEnumType;
|
||||
_options = options;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
|
@ -28,7 +28,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
|
|||
if (context.Metadata.IsEnum)
|
||||
{
|
||||
return new EnumTypeModelBinder(
|
||||
_allowBindingUndefinedValueToEnumType,
|
||||
_options.AllowBindingUndefinedValueToEnumType,
|
||||
context.Metadata.UnderlyingOrModelType);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,8 +69,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Json.Internal
|
|||
_jsonSerializerSettings,
|
||||
_charPool,
|
||||
_objectPoolProvider,
|
||||
options.SuppressInputFormatterBuffering,
|
||||
options.SuppressJsonDeserializationExceptionMessagesInModelState));
|
||||
options));
|
||||
|
||||
var jsonInputLogger = _loggerFactory.CreateLogger<JsonInputFormatter>();
|
||||
options.InputFormatters.Add(new JsonInputFormatter(
|
||||
|
|
@ -78,8 +77,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Json.Internal
|
|||
_jsonSerializerSettings,
|
||||
_charPool,
|
||||
_objectPoolProvider,
|
||||
options.SuppressInputFormatterBuffering,
|
||||
options.SuppressJsonDeserializationExceptionMessagesInModelState));
|
||||
options));
|
||||
|
||||
options.FormatterMappings.SetMediaTypeMappingForFormat("json", MediaTypeHeaderValue.Parse("application/json"));
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
private readonly IArrayPool<char> _charPool;
|
||||
private readonly ILogger _logger;
|
||||
private readonly ObjectPoolProvider _objectPoolProvider;
|
||||
private readonly MvcOptions _options;
|
||||
private readonly bool _suppressInputFormatterBuffering;
|
||||
private readonly bool _suppressJsonDeserializationExceptionMessages;
|
||||
|
||||
|
|
@ -43,6 +44,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
/// </param>
|
||||
/// <param name="charPool">The <see cref="ArrayPool{Char}"/>.</param>
|
||||
/// <param name="objectPoolProvider">The <see cref="ObjectPoolProvider"/>.</param>
|
||||
[Obsolete("This constructor is obsolete and will be removed in a future version.")]
|
||||
public JsonInputFormatter(
|
||||
ILogger logger,
|
||||
JsonSerializerSettings serializerSettings,
|
||||
|
|
@ -65,6 +67,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
/// <param name="charPool">The <see cref="ArrayPool{Char}"/>.</param>
|
||||
/// <param name="objectPoolProvider">The <see cref="ObjectPoolProvider"/>.</param>
|
||||
/// <param name="suppressInputFormatterBuffering">Flag to buffer entire request body before deserializing it.</param>
|
||||
[Obsolete("This constructor is obsolete and will be removed in a future version.")]
|
||||
public JsonInputFormatter(
|
||||
ILogger logger,
|
||||
JsonSerializerSettings serializerSettings,
|
||||
|
|
@ -90,6 +93,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
/// <param name="objectPoolProvider">The <see cref="ObjectPoolProvider"/>.</param>
|
||||
/// <param name="suppressInputFormatterBuffering">Flag to buffer entire request body before deserializing it.</param>
|
||||
/// <param name="suppressJsonDeserializationExceptionMessages">If <see langword="true"/>, JSON deserialization exception messages will replaced by a generic message in model state.</param>
|
||||
[Obsolete("This constructor is obsolete and will be removed in a future version.")]
|
||||
public JsonInputFormatter(
|
||||
ILogger logger,
|
||||
JsonSerializerSettings serializerSettings,
|
||||
|
|
@ -133,6 +137,59 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationAnyJsonSyntax);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="JsonInputFormatter"/>.
|
||||
/// </summary>
|
||||
/// <param name="logger">The <see cref="ILogger"/>.</param>
|
||||
/// <param name="serializerSettings">
|
||||
/// The <see cref="JsonSerializerSettings"/>. Should be either the application-wide settings
|
||||
/// (<see cref="MvcJsonOptions.SerializerSettings"/>) or an instance
|
||||
/// <see cref="JsonSerializerSettingsProvider.CreateSerializerSettings"/> initially returned.
|
||||
/// </param>
|
||||
/// <param name="charPool">The <see cref="ArrayPool{Char}"/>.</param>
|
||||
/// <param name="objectPoolProvider">The <see cref="ObjectPoolProvider"/>.</param>
|
||||
/// <param name="options">The <see cref="MvcOptions"/>.</param>
|
||||
public JsonInputFormatter(
|
||||
ILogger logger,
|
||||
JsonSerializerSettings serializerSettings,
|
||||
ArrayPool<char> charPool,
|
||||
ObjectPoolProvider objectPoolProvider,
|
||||
MvcOptions options)
|
||||
{
|
||||
if (logger == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
if (serializerSettings == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(serializerSettings));
|
||||
}
|
||||
|
||||
if (charPool == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(charPool));
|
||||
}
|
||||
|
||||
if (objectPoolProvider == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(objectPoolProvider));
|
||||
}
|
||||
|
||||
_logger = logger;
|
||||
SerializerSettings = serializerSettings;
|
||||
_charPool = new JsonArrayPool<char>(charPool);
|
||||
_objectPoolProvider = objectPoolProvider;
|
||||
_options = options;
|
||||
|
||||
SupportedEncodings.Add(UTF8EncodingWithoutBOM);
|
||||
SupportedEncodings.Add(UTF16EncodingLittleEndian);
|
||||
|
||||
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationJson);
|
||||
SupportedMediaTypes.Add(MediaTypeHeaderValues.TextJson);
|
||||
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationAnyJsonSyntax);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual InputFormatterExceptionModelStatePolicy ExceptionPolicy
|
||||
{
|
||||
|
|
@ -172,7 +229,9 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
|
||||
var request = context.HttpContext.Request;
|
||||
|
||||
if (!request.Body.CanSeek && !_suppressInputFormatterBuffering)
|
||||
var suppressInputFormatterBuffering = _options?.SuppressInputFormatterBuffering ?? _suppressInputFormatterBuffering;
|
||||
|
||||
if (!request.Body.CanSeek && !suppressInputFormatterBuffering)
|
||||
{
|
||||
// JSON.Net does synchronous reads. In order to avoid blocking on the stream, we asynchronously
|
||||
// read everything into a buffer, and then seek back to the beginning.
|
||||
|
|
@ -352,8 +411,9 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
// we regard as having safe messages to expose to clients
|
||||
var isJsonExceptionType =
|
||||
exception is JsonReaderException || exception is JsonSerializationException;
|
||||
var suppressJsonDeserializationExceptionMessages = _options?.SuppressJsonDeserializationExceptionMessagesInModelState ?? _suppressJsonDeserializationExceptionMessages;
|
||||
var suppressOriginalMessage =
|
||||
_suppressJsonDeserializationExceptionMessages || !isJsonExceptionType;
|
||||
suppressJsonDeserializationExceptionMessages || !isJsonExceptionType;
|
||||
return suppressOriginalMessage
|
||||
? exception
|
||||
: new InputFormatterException(exception.Message, exception);
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
/// </param>
|
||||
/// <param name="charPool">The <see cref="ArrayPool{Char}"/>.</param>
|
||||
/// <param name="objectPoolProvider">The <see cref="ObjectPoolProvider"/>.</param>
|
||||
[Obsolete("This constructor is obsolete and will be removed in a future version.")]
|
||||
public JsonPatchInputFormatter(
|
||||
ILogger logger,
|
||||
JsonSerializerSettings serializerSettings,
|
||||
|
|
@ -51,6 +52,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
/// <param name="charPool">The <see cref="ArrayPool{Char}"/>.</param>
|
||||
/// <param name="objectPoolProvider">The <see cref="ObjectPoolProvider"/>.</param>
|
||||
/// <param name="suppressInputFormatterBuffering">Flag to buffer entire request body before deserializing it.</param>
|
||||
[Obsolete("This constructor is obsolete and will be removed in a future version.")]
|
||||
public JsonPatchInputFormatter(
|
||||
ILogger logger,
|
||||
JsonSerializerSettings serializerSettings,
|
||||
|
|
@ -74,6 +76,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
/// <param name="objectPoolProvider">The <see cref="ObjectPoolProvider"/>.</param>
|
||||
/// <param name="suppressInputFormatterBuffering">Flag to buffer entire request body before deserializing it.</param>
|
||||
/// <param name="suppressJsonDeserializationExceptionMessages">If <see langword="true"/>, JSON deserialization exception messages will replaced by a generic message in model state.</param>
|
||||
[Obsolete("This constructor is obsolete and will be removed in a future version.")]
|
||||
public JsonPatchInputFormatter(
|
||||
ILogger logger,
|
||||
JsonSerializerSettings serializerSettings,
|
||||
|
|
@ -89,6 +92,32 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationJsonPatch);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="JsonPatchInputFormatter"/> instance.
|
||||
/// </summary>
|
||||
/// <param name="logger">The <see cref="ILogger"/>.</param>
|
||||
/// <param name="serializerSettings">
|
||||
/// The <see cref="JsonSerializerSettings"/>. Should be either the application-wide settings
|
||||
/// (<see cref="MvcJsonOptions.SerializerSettings"/>) or an instance
|
||||
/// <see cref="JsonSerializerSettingsProvider.CreateSerializerSettings"/> initially returned.
|
||||
/// </param>
|
||||
/// <param name="charPool">The <see cref="ArrayPool{Char}"/>.</param>
|
||||
/// <param name="objectPoolProvider">The <see cref="ObjectPoolProvider"/>.</param>
|
||||
/// <param name="options">The <see cref="MvcOptions"/>.</param>
|
||||
public JsonPatchInputFormatter(
|
||||
ILogger logger,
|
||||
JsonSerializerSettings serializerSettings,
|
||||
ArrayPool<char> charPool,
|
||||
ObjectPoolProvider objectPoolProvider,
|
||||
MvcOptions options)
|
||||
: base(logger, serializerSettings, charPool, objectPoolProvider, options)
|
||||
{
|
||||
// Clear all values and only include json-patch+json value.
|
||||
SupportedMediaTypes.Clear();
|
||||
|
||||
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationJsonPatch);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override InputFormatterExceptionModelStatePolicy ExceptionPolicy
|
||||
{
|
||||
|
|
|
|||
|
|
@ -799,7 +799,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
|
|||
private readonly bool _throwNonInputFormatterException;
|
||||
|
||||
public TestableJsonInputFormatter(bool throwNonInputFormatterException)
|
||||
: base(GetLogger(), new JsonSerializerSettings(), ArrayPool<char>.Shared, new DefaultObjectPoolProvider())
|
||||
: base(GetLogger(), new JsonSerializerSettings(), ArrayPool<char>.Shared, new DefaultObjectPoolProvider(), new MvcOptions())
|
||||
{
|
||||
_throwNonInputFormatterException = throwNonInputFormatterException;
|
||||
}
|
||||
|
|
@ -863,7 +863,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
|
|||
private readonly bool _throwNonInputFormatterException;
|
||||
|
||||
public DerivedJsonInputFormatter(bool throwNonInputFormatterException)
|
||||
: base(GetLogger(), new JsonSerializerSettings(), ArrayPool<char>.Shared, new DefaultObjectPoolProvider())
|
||||
: base(GetLogger(), new JsonSerializerSettings(), ArrayPool<char>.Shared, new DefaultObjectPoolProvider(), new MvcOptions())
|
||||
{
|
||||
_throwNonInputFormatterException = throwNonInputFormatterException;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
public void ReturnsBinder_ForEnumType(Type modelType)
|
||||
{
|
||||
// Arrange
|
||||
var provider = new EnumTypeModelBinderProvider(allowBindingUndefinedValueToEnumType: true);
|
||||
var provider = new EnumTypeModelBinderProvider(new MvcOptions { AllowBindingUndefinedValueToEnumType = true });
|
||||
var context = new TestModelBinderProviderContext(modelType);
|
||||
|
||||
// Act
|
||||
|
|
@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
public void ReturnsBinder_ForFlagsEnumType(Type modelType)
|
||||
{
|
||||
// Arrange
|
||||
var provider = new EnumTypeModelBinderProvider(allowBindingUndefinedValueToEnumType: true);
|
||||
var provider = new EnumTypeModelBinderProvider(new MvcOptions { AllowBindingUndefinedValueToEnumType = true });
|
||||
var context = new TestModelBinderProviderContext(modelType);
|
||||
|
||||
// Act
|
||||
|
|
@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
public void DoesNotReturnBinder_ForNonEnumTypes(Type modelType)
|
||||
{
|
||||
// Arrange
|
||||
var provider = new EnumTypeModelBinderProvider(allowBindingUndefinedValueToEnumType: false);
|
||||
var provider = new EnumTypeModelBinderProvider(new MvcOptions { AllowBindingUndefinedValueToEnumType = false });
|
||||
var context = new TestModelBinderProviderContext(modelType);
|
||||
|
||||
// Act
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System;
|
|||
using System.ComponentModel;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Binders;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
||||
|
|
@ -281,7 +282,10 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding
|
|||
{ modelName, valueProviderValue }
|
||||
}
|
||||
};
|
||||
var binderProvider = new EnumTypeModelBinderProvider(allowBindingUndefinedValueToEnumType);
|
||||
var binderProvider = new EnumTypeModelBinderProvider(new MvcOptions
|
||||
{
|
||||
AllowBindingUndefinedValueToEnumType = allowBindingUndefinedValueToEnumType
|
||||
});
|
||||
var binder = binderProvider.GetBinder(binderProviderContext);
|
||||
return (bindingContext, binder);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,8 +33,59 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
// Arrange
|
||||
var content = "{name: 'Person Name', Age: '30'}";
|
||||
var logger = GetLogger();
|
||||
#pragma warning disable CS0618
|
||||
var formatter =
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
||||
#pragma warning restore CS0618
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
|
||||
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
|
||||
httpContext.Request.ContentType = "application/json";
|
||||
var provider = new EmptyModelMetadataProvider();
|
||||
var metadata = provider.GetMetadataForType(typeof(User));
|
||||
var context = new InputFormatterContext(
|
||||
httpContext,
|
||||
modelName: string.Empty,
|
||||
modelState: modelState,
|
||||
metadata: metadata,
|
||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
var userModel = Assert.IsType<User>(result.Model);
|
||||
Assert.Equal("Person Name", userModel.Name);
|
||||
Assert.Equal(30, userModel.Age);
|
||||
|
||||
Assert.True(httpContext.Request.Body.CanSeek);
|
||||
httpContext.Request.Body.Seek(0L, SeekOrigin.Begin);
|
||||
|
||||
result = await formatter.ReadAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
userModel = Assert.IsType<User>(result.Model);
|
||||
Assert.Equal("Person Name", userModel.Name);
|
||||
Assert.Equal(30, userModel.Age);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BuffersRequestBody_UsingDefaultOptions()
|
||||
{
|
||||
// Arrange
|
||||
var content = "{name: 'Person Name', Age: '30'}";
|
||||
var logger = GetLogger();
|
||||
var formatter = new JsonInputFormatter(
|
||||
logger,
|
||||
_serializerSettings,
|
||||
ArrayPool<char>.Shared,
|
||||
_objectPoolProvider,
|
||||
new MvcOptions());
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
|
|
@ -78,8 +129,10 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
// Arrange
|
||||
var content = "{name: 'Person Name', Age: '30'}";
|
||||
var logger = GetLogger();
|
||||
#pragma warning disable CS0618
|
||||
var formatter =
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, suppressInputFormatterBuffering: true);
|
||||
#pragma warning restore CS0618
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
|
|
@ -105,7 +158,99 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
Assert.Equal("Person Name", userModel.Name);
|
||||
Assert.Equal(30, userModel.Age);
|
||||
|
||||
// Reading again should not fail as the request body should have been buffered by the formatter
|
||||
Assert.False(httpContext.Request.Body.CanSeek);
|
||||
result = await formatter.ReadAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
Assert.Null(result.Model);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SuppressInputFormatterBufferingSetToTrue_UsingMvcOptions_DoesNotBufferRequestBody()
|
||||
{
|
||||
// Arrange
|
||||
var content = "{name: 'Person Name', Age: '30'}";
|
||||
var logger = GetLogger();
|
||||
var mvcOptions = new MvcOptions();
|
||||
mvcOptions.SuppressInputFormatterBuffering = true;
|
||||
var formatter = new JsonInputFormatter(
|
||||
logger,
|
||||
_serializerSettings,
|
||||
ArrayPool<char>.Shared,
|
||||
_objectPoolProvider,
|
||||
mvcOptions);
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
|
||||
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
|
||||
httpContext.Request.ContentType = "application/json";
|
||||
var provider = new EmptyModelMetadataProvider();
|
||||
var metadata = provider.GetMetadataForType(typeof(User));
|
||||
var context = new InputFormatterContext(
|
||||
httpContext,
|
||||
modelName: string.Empty,
|
||||
modelState: modelState,
|
||||
metadata: metadata,
|
||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
var userModel = Assert.IsType<User>(result.Model);
|
||||
Assert.Equal("Person Name", userModel.Name);
|
||||
Assert.Equal(30, userModel.Age);
|
||||
|
||||
Assert.False(httpContext.Request.Body.CanSeek);
|
||||
result = await formatter.ReadAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
Assert.Null(result.Model);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SuppressInputFormatterBufferingSetToTrue_UsingMutatedOptions()
|
||||
{
|
||||
// Arrange
|
||||
var content = "{name: 'Person Name', Age: '30'}";
|
||||
var logger = GetLogger();
|
||||
var mvcOptions = new MvcOptions();
|
||||
mvcOptions.SuppressInputFormatterBuffering = false;
|
||||
var formatter =
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, mvcOptions);
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
|
||||
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
|
||||
httpContext.Request.ContentType = "application/json";
|
||||
var provider = new EmptyModelMetadataProvider();
|
||||
var metadata = provider.GetMetadataForType(typeof(User));
|
||||
var context = new InputFormatterContext(
|
||||
httpContext,
|
||||
modelName: string.Empty,
|
||||
modelState: modelState,
|
||||
metadata: metadata,
|
||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
||||
|
||||
// Act
|
||||
// Mutate options after passing into the constructor to make sure that the value type is not store in the constructor
|
||||
mvcOptions.SuppressInputFormatterBuffering = true;
|
||||
var result = await formatter.ReadAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
var userModel = Assert.IsType<User>(result.Model);
|
||||
Assert.Equal("Person Name", userModel.Name);
|
||||
Assert.Equal(30, userModel.Age);
|
||||
|
||||
Assert.False(httpContext.Request.Body.CanSeek);
|
||||
result = await formatter.ReadAsync(context);
|
||||
|
||||
// Assert
|
||||
|
|
@ -135,7 +280,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
var loggerMock = GetLogger();
|
||||
|
||||
var formatter =
|
||||
new JsonInputFormatter(loggerMock, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
||||
new JsonInputFormatter(loggerMock, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var contentBytes = Encoding.UTF8.GetBytes("content");
|
||||
|
||||
var httpContext = GetHttpContext(contentBytes, contentType: requestContentType);
|
||||
|
|
@ -161,7 +306,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
// Arrange
|
||||
var loggerMock = GetLogger();
|
||||
var formatter =
|
||||
new JsonInputFormatter(loggerMock, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
||||
new JsonInputFormatter(loggerMock, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
|
||||
// Act
|
||||
var mediaType = formatter.SupportedMediaTypes[0];
|
||||
|
|
@ -188,7 +333,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
// Arrange
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
|
|
@ -216,7 +361,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
var content = "{name: 'Person Name', Age: '30'}";
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
|
|
@ -246,7 +391,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
var content = "[0, 23, 300]";
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
|
|
@ -280,7 +425,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
var content = "[0, 23, 300]";
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
|
|
@ -310,7 +455,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
var content = "{name: 'Person Name', Age: 'not-an-age'}";
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
|
|
@ -341,7 +486,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
var content = "[0, 23, 300]";
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
|
|
@ -371,7 +516,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
var content = "[{name: 'Name One', Age: 30}, {name: 'Name Two', Small: 300}]";
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
|
|
@ -402,7 +547,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
var content = "{name: 'Person Name', Age: 'not-an-age'}";
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
|
|
@ -440,7 +585,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
// Arrange
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
|
|
@ -488,7 +633,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
// by default we ignore missing members, so here explicitly changing it
|
||||
var serializerSettings = new JsonSerializerSettings { MissingMemberHandling = MissingMemberHandling.Error };
|
||||
var jsonFormatter =
|
||||
new JsonInputFormatter(logger, serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
||||
new JsonInputFormatter(logger, serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var httpContext = GetHttpContext(contentBytes, "application/json;charset=utf-8");
|
||||
|
|
@ -547,7 +692,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
// Arrange
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
|
|
@ -579,8 +724,15 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
// Arrange
|
||||
var logger = GetLogger();
|
||||
var formatter = new JsonInputFormatter(
|
||||
logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider,
|
||||
suppressInputFormatterBuffering: false, suppressJsonDeserializationExceptionMessages: true);
|
||||
logger,
|
||||
_serializerSettings,
|
||||
ArrayPool<char>.Shared,
|
||||
_objectPoolProvider,
|
||||
new MvcOptions()
|
||||
{
|
||||
SuppressInputFormatterBuffering = false,
|
||||
SuppressJsonDeserializationExceptionMessagesInModelState = true
|
||||
});
|
||||
var contentBytes = Encoding.UTF8.GetBytes("{");
|
||||
var modelStateKey = string.Empty;
|
||||
|
||||
|
|
@ -611,7 +763,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
private class TestableJsonInputFormatter : JsonInputFormatter
|
||||
{
|
||||
public TestableJsonInputFormatter(JsonSerializerSettings settings)
|
||||
: base(GetLogger(), settings, ArrayPool<char>.Shared, _objectPoolProvider)
|
||||
: base(GetLogger(), settings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions())
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,8 +30,57 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
{
|
||||
// Arrange
|
||||
var logger = GetLogger();
|
||||
#pragma warning disable CS0618
|
||||
var formatter =
|
||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
||||
#pragma warning restore CS0618
|
||||
var content = "[{\"op\":\"add\",\"path\":\"Customer/Name\",\"value\":\"John\"}]";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
|
||||
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
|
||||
httpContext.Request.ContentType = "application/json";
|
||||
var provider = new EmptyModelMetadataProvider();
|
||||
var metadata = provider.GetMetadataForType(typeof(JsonPatchDocument<Customer>));
|
||||
var context = new InputFormatterContext(
|
||||
httpContext,
|
||||
modelName: string.Empty,
|
||||
modelState: modelState,
|
||||
metadata: metadata,
|
||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
var patchDoc = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
||||
Assert.Equal("add", patchDoc.Operations[0].op);
|
||||
Assert.Equal("Customer/Name", patchDoc.Operations[0].path);
|
||||
Assert.Equal("John", patchDoc.Operations[0].value);
|
||||
|
||||
Assert.True(httpContext.Request.Body.CanSeek);
|
||||
httpContext.Request.Body.Seek(0L, SeekOrigin.Begin);
|
||||
|
||||
result = await formatter.ReadAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
patchDoc = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
||||
Assert.Equal("add", patchDoc.Operations[0].op);
|
||||
Assert.Equal("Customer/Name", patchDoc.Operations[0].path);
|
||||
Assert.Equal("John", patchDoc.Operations[0].value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BuffersRequestBody_UsingDefaultOptions()
|
||||
{
|
||||
// Arrange
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var content = "[{\"op\":\"add\",\"path\":\"Customer/Name\",\"value\":\"John\"}]";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
|
|
@ -77,8 +126,10 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
{
|
||||
// Arrange
|
||||
var logger = GetLogger();
|
||||
#pragma warning disable CS0618
|
||||
var formatter =
|
||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, suppressInputFormatterBuffering: true);
|
||||
#pragma warning restore CS0618
|
||||
var content = "[{\"op\":\"add\",\"path\":\"Customer/Name\",\"value\":\"John\"}]";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
|
|
@ -106,6 +157,57 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
Assert.Equal("Customer/Name", patchDoc.Operations[0].path);
|
||||
Assert.Equal("John", patchDoc.Operations[0].value);
|
||||
|
||||
Assert.False(httpContext.Request.Body.CanSeek);
|
||||
result = await formatter.ReadAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
Assert.Null(result.Model);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SuppressInputFormatterBufferingSetToTrue_UsingMutatedOptions_DoesNotBufferRequestBody()
|
||||
{
|
||||
// Arrange
|
||||
var logger = GetLogger();
|
||||
var mvcOptions = new MvcOptions();
|
||||
mvcOptions.SuppressInputFormatterBuffering = false;
|
||||
var formatter = new JsonPatchInputFormatter(
|
||||
logger,
|
||||
_serializerSettings,
|
||||
ArrayPool<char>.Shared,
|
||||
_objectPoolProvider,
|
||||
mvcOptions);
|
||||
var content = "[{\"op\":\"add\",\"path\":\"Customer/Name\",\"value\":\"John\"}]";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
|
||||
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
|
||||
httpContext.Request.ContentType = "application/json";
|
||||
var provider = new EmptyModelMetadataProvider();
|
||||
var metadata = provider.GetMetadataForType(typeof(JsonPatchDocument<Customer>));
|
||||
var context = new InputFormatterContext(
|
||||
httpContext,
|
||||
modelName: string.Empty,
|
||||
modelState: modelState,
|
||||
metadata: metadata,
|
||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
||||
|
||||
// Act
|
||||
// Mutate options after passing into the constructor to make sure that the value type is not store in the constructor
|
||||
mvcOptions.SuppressInputFormatterBuffering = true;
|
||||
var result = await formatter.ReadAsync(context);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
var patchDoc = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
||||
Assert.Equal("add", patchDoc.Operations[0].op);
|
||||
Assert.Equal("Customer/Name", patchDoc.Operations[0].path);
|
||||
Assert.Equal("John", patchDoc.Operations[0].value);
|
||||
|
||||
Assert.False(httpContext.Request.Body.CanSeek);
|
||||
result = await formatter.ReadAsync(context);
|
||||
|
||||
// Assert
|
||||
|
|
@ -119,7 +221,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
// Arrange
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var content = "[{\"op\":\"add\",\"path\":\"Customer/Name\",\"value\":\"John\"}]";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
|
|
@ -151,7 +253,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
// Arrange
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var content = "[{\"op\": \"add\", \"path\" : \"Customer/Name\", \"value\":\"John\"}," +
|
||||
"{\"op\": \"remove\", \"path\" : \"Customer/Name\"}]";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
|
@ -190,7 +292,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
// Arrange
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var content = "[{\"op\": \"add\", \"path\" : \"Customer/Name\", \"value\":\"John\"}]";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
|
|
@ -220,7 +322,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
// Arrange
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var content = "[{\"op\": \"add\", \"path\" : \"Customer/Name\", \"value\":\"John\"}]";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
|
|
@ -251,7 +353,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var content = "[{\"op\": \"add\", \"path\" : \"Customer/Name\", \"value\":\"John\"}]";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue