Move option for JSON errors to MvcJsonOptions
This was in the wrong place - JSON formatters have their own options type already. Moved the option to MvcJsonOptions and updated the naming + defaults to reflect our plan. Also did a bunch of general cleanup on these tests, which were a bit sloppy.
This commit is contained in:
parent
bfb5f23647
commit
2e73bab2a4
|
|
@ -51,7 +51,9 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
/// <remarks>
|
||||
/// ASP.NET Core MVC 2.1 introduces compatibility switches for the following:
|
||||
/// <list type="bullet">
|
||||
/// <item><description><see cref="MvcOptions.InputFormatterExceptionPolicy"/></description></item>
|
||||
/// <item><description><see cref="MvcOptions.SuppressBindingUndefinedValueToEnumType"/></description></item>
|
||||
/// <item><description><c>MvcJsonOptions.AllowInputFormatterExceptionMessages</c></description></item>
|
||||
/// <item><description><c>RazorPagesOptions.AllowAreas</c></description></item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
|
|
|
|||
|
|
@ -24,9 +24,11 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
// See CompatibilitySwitch.cs for guide on how to implement these.
|
||||
private readonly CompatibilitySwitch<InputFormatterExceptionPolicy> _inputFormatterExceptionPolicy;
|
||||
private readonly CompatibilitySwitch<bool> _suppressBindingUndefinedValueToEnumType;
|
||||
private readonly CompatibilitySwitch<bool> _suppressJsonDeserializationExceptionMessagesInModelState;
|
||||
private readonly ICompatibilitySwitch[] _switches;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="MvcOptions"/>.
|
||||
/// </summary>
|
||||
public MvcOptions()
|
||||
{
|
||||
CacheProfiles = new Dictionary<string, CacheProfile>(StringComparer.OrdinalIgnoreCase);
|
||||
|
|
@ -43,12 +45,11 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
|
||||
_inputFormatterExceptionPolicy = new CompatibilitySwitch<InputFormatterExceptionPolicy>(nameof(InputFormatterExceptionPolicy), InputFormatterExceptionPolicy.AllExceptions);
|
||||
_suppressBindingUndefinedValueToEnumType = new CompatibilitySwitch<bool>(nameof(SuppressBindingUndefinedValueToEnumType));
|
||||
_suppressJsonDeserializationExceptionMessagesInModelState = new CompatibilitySwitch<bool>(nameof(SuppressJsonDeserializationExceptionMessagesInModelState));
|
||||
|
||||
_switches = new ICompatibilitySwitch[]
|
||||
{
|
||||
_inputFormatterExceptionPolicy,
|
||||
_suppressBindingUndefinedValueToEnumType,
|
||||
_suppressJsonDeserializationExceptionMessagesInModelState,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -241,20 +242,6 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
/// </summary>
|
||||
public bool RequireHttpsPermanent { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a flag to determine whether, if an action receives invalid JSON in
|
||||
/// the request body, the JSON deserialization exception message should be replaced
|
||||
/// by a generic error message in model state.
|
||||
/// <see langword="false"/> by default, meaning that clients may receive details about
|
||||
/// why the JSON they posted is considered invalid.
|
||||
/// </summary>
|
||||
public bool SuppressJsonDeserializationExceptionMessagesInModelState
|
||||
{
|
||||
get => _suppressJsonDeserializationExceptionMessagesInModelState.Value;
|
||||
set => _suppressJsonDeserializationExceptionMessagesInModelState.Value = value;
|
||||
}
|
||||
|
||||
IEnumerator<ICompatibilitySwitch> IEnumerable<ICompatibilitySwitch>.GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<ICompatibilitySwitch>)_switches).GetEnumerator();
|
||||
|
|
|
|||
|
|
@ -75,6 +75,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
{
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, MvcJsonMvcOptionsSetup>());
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Transient<IPostConfigureOptions<MvcJsonOptions>, MvcJsonOptionsConfigureCompatibilityOptions>());
|
||||
services.TryAddEnumerable(
|
||||
ServiceDescriptor.Transient<IApiDescriptionProvider, JsonPatchOperationsArrayProvider>());
|
||||
services.TryAddSingleton<JsonResultExecutor>();
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ using Microsoft.Extensions.Logging;
|
|||
using Microsoft.Extensions.ObjectPool;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Formatters.Json.Internal
|
||||
|
|
@ -20,7 +19,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Json.Internal
|
|||
public class MvcJsonMvcOptionsSetup : IConfigureOptions<MvcOptions>
|
||||
{
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
private readonly JsonSerializerSettings _jsonSerializerSettings;
|
||||
private readonly MvcJsonOptions _jsonOptions;
|
||||
private readonly ArrayPool<char> _charPool;
|
||||
private readonly ObjectPoolProvider _objectPoolProvider;
|
||||
|
||||
|
|
@ -51,14 +50,14 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Json.Internal
|
|||
}
|
||||
|
||||
_loggerFactory = loggerFactory;
|
||||
_jsonSerializerSettings = jsonOptions.Value.SerializerSettings;
|
||||
_jsonOptions = jsonOptions.Value;
|
||||
_charPool = charPool;
|
||||
_objectPoolProvider = objectPoolProvider;
|
||||
}
|
||||
|
||||
public void Configure(MvcOptions options)
|
||||
{
|
||||
options.OutputFormatters.Add(new JsonOutputFormatter(_jsonSerializerSettings, _charPool));
|
||||
options.OutputFormatters.Add(new JsonOutputFormatter(_jsonOptions.SerializerSettings, _charPool));
|
||||
|
||||
// Register JsonPatchInputFormatter before JsonInputFormatter, otherwise
|
||||
// JsonInputFormatter would consume "application/json-patch+json" requests
|
||||
|
|
@ -66,18 +65,20 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Json.Internal
|
|||
var jsonInputPatchLogger = _loggerFactory.CreateLogger<JsonPatchInputFormatter>();
|
||||
options.InputFormatters.Add(new JsonPatchInputFormatter(
|
||||
jsonInputPatchLogger,
|
||||
_jsonSerializerSettings,
|
||||
_jsonOptions.SerializerSettings,
|
||||
_charPool,
|
||||
_objectPoolProvider,
|
||||
options));
|
||||
options,
|
||||
_jsonOptions));
|
||||
|
||||
var jsonInputLogger = _loggerFactory.CreateLogger<JsonInputFormatter>();
|
||||
options.InputFormatters.Add(new JsonInputFormatter(
|
||||
jsonInputLogger,
|
||||
_jsonSerializerSettings,
|
||||
_jsonOptions.SerializerSettings,
|
||||
_charPool,
|
||||
_objectPoolProvider,
|
||||
options));
|
||||
options,
|
||||
_jsonOptions));
|
||||
|
||||
options.FormatterMappings.SetMediaTypeMappingForFormat("json", MediaTypeHeaderValue.Parse("application/json"));
|
||||
|
||||
|
|
|
|||
|
|
@ -28,8 +28,12 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
private readonly ILogger _logger;
|
||||
private readonly ObjectPoolProvider _objectPoolProvider;
|
||||
private readonly MvcOptions _options;
|
||||
private readonly MvcJsonOptions _jsonOptions;
|
||||
|
||||
// These fields are used when one of the legacy constructors is called that doesn't provide the MvcOptions or
|
||||
// MvcJsonOptions.
|
||||
private readonly bool _suppressInputFormatterBuffering;
|
||||
private readonly bool _suppressJsonDeserializationExceptionMessages;
|
||||
private readonly bool _allowInputFormatterExceptionMessages;
|
||||
|
||||
private ObjectPool<JsonSerializer> _jsonSerializerPool;
|
||||
|
||||
|
|
@ -74,10 +78,10 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
ArrayPool<char> charPool,
|
||||
ObjectPoolProvider objectPoolProvider,
|
||||
bool suppressInputFormatterBuffering)
|
||||
: this(logger, serializerSettings, charPool, objectPoolProvider, suppressInputFormatterBuffering, suppressJsonDeserializationExceptionMessages: false)
|
||||
: this(logger, serializerSettings, charPool, objectPoolProvider, suppressInputFormatterBuffering, allowInputFormatterExceptionMessages: false)
|
||||
{
|
||||
// This constructor by default treats JSON deserialization exceptions as safe
|
||||
// because this is the default for applications generally
|
||||
// This constructor by default treats JSON deserialization exceptions as unsafe
|
||||
// because this is the default in 2.0
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -92,7 +96,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>
|
||||
/// <param name="suppressJsonDeserializationExceptionMessages">If <see langword="true"/>, JSON deserialization exception messages will replaced by a generic message in model state.</param>
|
||||
/// <param name="allowInputFormatterExceptionMessages">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,
|
||||
|
|
@ -100,7 +104,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
ArrayPool<char> charPool,
|
||||
ObjectPoolProvider objectPoolProvider,
|
||||
bool suppressInputFormatterBuffering,
|
||||
bool suppressJsonDeserializationExceptionMessages)
|
||||
bool allowInputFormatterExceptionMessages)
|
||||
{
|
||||
if (logger == null)
|
||||
{
|
||||
|
|
@ -127,7 +131,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
_charPool = new JsonArrayPool<char>(charPool);
|
||||
_objectPoolProvider = objectPoolProvider;
|
||||
_suppressInputFormatterBuffering = suppressInputFormatterBuffering;
|
||||
_suppressJsonDeserializationExceptionMessages = suppressJsonDeserializationExceptionMessages;
|
||||
_allowInputFormatterExceptionMessages = allowInputFormatterExceptionMessages;
|
||||
|
||||
SupportedEncodings.Add(UTF8EncodingWithoutBOM);
|
||||
SupportedEncodings.Add(UTF16EncodingLittleEndian);
|
||||
|
|
@ -149,12 +153,14 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
/// <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>
|
||||
/// <param name="jsonOptions">The <see cref="MvcJsonOptions"/>.</param>
|
||||
public JsonInputFormatter(
|
||||
ILogger logger,
|
||||
JsonSerializerSettings serializerSettings,
|
||||
ArrayPool<char> charPool,
|
||||
ObjectPoolProvider objectPoolProvider,
|
||||
MvcOptions options)
|
||||
MvcOptions options,
|
||||
MvcJsonOptions jsonOptions)
|
||||
{
|
||||
if (logger == null)
|
||||
{
|
||||
|
|
@ -181,6 +187,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
_charPool = new JsonArrayPool<char>(charPool);
|
||||
_objectPoolProvider = objectPoolProvider;
|
||||
_options = options;
|
||||
_jsonOptions = jsonOptions;
|
||||
|
||||
SupportedEncodings.Add(UTF8EncodingWithoutBOM);
|
||||
SupportedEncodings.Add(UTF16EncodingLittleEndian);
|
||||
|
|
@ -406,17 +413,26 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
|
||||
private Exception WrapExceptionForModelState(Exception exception)
|
||||
{
|
||||
// In 2.0 and earlier we always gave a generic error message for errors that come from JSON.NET
|
||||
// We only allow it in 2.1 and newer if the app opts-in.
|
||||
if (!(_jsonOptions?.AllowInputFormatterExceptionMessages ?? _allowInputFormatterExceptionMessages))
|
||||
{
|
||||
// This app is not opted-in to JSON.NET messages, return the original exception.
|
||||
return exception;
|
||||
}
|
||||
|
||||
// It's not known that Json.NET currently ever raises error events with exceptions
|
||||
// other than these two types, but we're being conservative and limiting which ones
|
||||
// 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;
|
||||
return suppressOriginalMessage
|
||||
? exception
|
||||
: new InputFormatterException(exception.Message, exception);
|
||||
if (exception is JsonReaderException || exception is JsonSerializationException)
|
||||
{
|
||||
// InputFormatterException specifies that the message is safe to return to a client, it will
|
||||
// be added to model state.
|
||||
return new InputFormatterException(exception.Message, exception);
|
||||
}
|
||||
|
||||
// Not a known exception type, so we're not going to assume that it's safe.
|
||||
return exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
ArrayPool<char> charPool,
|
||||
ObjectPoolProvider objectPoolProvider,
|
||||
bool suppressInputFormatterBuffering)
|
||||
: this(logger, serializerSettings, charPool, objectPoolProvider, suppressInputFormatterBuffering, suppressJsonDeserializationExceptionMessages: false)
|
||||
: this(logger, serializerSettings, charPool, objectPoolProvider, suppressInputFormatterBuffering, allowInputFormatterExceptionMessages: false)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +75,9 @@ 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>
|
||||
/// <param name="suppressJsonDeserializationExceptionMessages">If <see langword="true"/>, JSON deserialization exception messages will replaced by a generic message in model state.</param>
|
||||
/// <param name="allowInputFormatterExceptionMessages">
|
||||
/// If <see langword="false"/>, 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,
|
||||
|
|
@ -83,8 +85,8 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
ArrayPool<char> charPool,
|
||||
ObjectPoolProvider objectPoolProvider,
|
||||
bool suppressInputFormatterBuffering,
|
||||
bool suppressJsonDeserializationExceptionMessages)
|
||||
: base(logger, serializerSettings, charPool, objectPoolProvider, suppressInputFormatterBuffering, suppressJsonDeserializationExceptionMessages)
|
||||
bool allowInputFormatterExceptionMessages)
|
||||
: base(logger, serializerSettings, charPool, objectPoolProvider, suppressInputFormatterBuffering, allowInputFormatterExceptionMessages)
|
||||
{
|
||||
// Clear all values and only include json-patch+json value.
|
||||
SupportedMediaTypes.Clear();
|
||||
|
|
@ -104,13 +106,15 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
/// <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>
|
||||
/// <param name="jsonOptions">The <see cref="MvcJsonOptions"/>.</param>
|
||||
public JsonPatchInputFormatter(
|
||||
ILogger logger,
|
||||
JsonSerializerSettings serializerSettings,
|
||||
ArrayPool<char> charPool,
|
||||
ObjectPoolProvider objectPoolProvider,
|
||||
MvcOptions options)
|
||||
: base(logger, serializerSettings, charPool, objectPoolProvider, options)
|
||||
MvcOptions options,
|
||||
MvcJsonOptions jsonOptions)
|
||||
: base(logger, serializerSettings, charPool, objectPoolProvider, options, jsonOptions)
|
||||
{
|
||||
// Clear all values and only include json-patch+json value.
|
||||
SupportedMediaTypes.Clear();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc.Formatters;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
|
|
@ -9,12 +13,69 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
/// <summary>
|
||||
/// Provides programmatic configuration for JSON in the MVC framework.
|
||||
/// </summary>
|
||||
public class MvcJsonOptions
|
||||
public class MvcJsonOptions : IEnumerable<ICompatibilitySwitch>
|
||||
{
|
||||
private readonly CompatibilitySwitch<bool> _allowInputFormatterExceptionMessages;
|
||||
private readonly ICompatibilitySwitch[] _switches;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="MvcJsonOptions"/>.
|
||||
/// </summary>
|
||||
public MvcJsonOptions()
|
||||
{
|
||||
_allowInputFormatterExceptionMessages = new CompatibilitySwitch<bool>(nameof(AllowInputFormatterExceptionMessages));
|
||||
|
||||
_switches = new ICompatibilitySwitch[]
|
||||
{
|
||||
_allowInputFormatterExceptionMessages,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a flag to determine whether error messsages from JSON deserialization by the
|
||||
/// <see cref="JsonInputFormatter"/> will be added to the <see cref="ModelStateDictionary"/>. The default
|
||||
/// value is <c>false</c>, meaning that a generic error message will be used instead.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Error messages in the <see cref="ModelStateDictionary"/> are often communicated to clients, either in HTML
|
||||
/// or using <see cref="BadRequestObjectResult"/>. In effect, this setting controls whether clients can recieve
|
||||
/// detailed error messages about submitted JSON data.
|
||||
/// </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 of the value compatibility switch by calling this property's setter will take precedence
|
||||
/// over the value implied by the application's <see cref="CompatibilityVersion"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// If the application's compatibility version is set to <see cref="CompatibilityVersion.Version_2_0"/> then
|
||||
/// this setting will have value <c>false</c> if not explicitly configured.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// If the application's compatibility version is set to <see cref="CompatibilityVersion.Version_2_1"/> or
|
||||
/// higher then this setting will have value <c>true</c> if not explicitly configured.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public bool AllowInputFormatterExceptionMessages
|
||||
{
|
||||
get => _allowInputFormatterExceptionMessages.Value;
|
||||
set => _allowInputFormatterExceptionMessages.Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="JsonSerializerSettings"/> that are used by this application.
|
||||
/// </summary>
|
||||
public JsonSerializerSettings SerializerSettings { get; } =
|
||||
JsonSerializerSettingsProvider.CreateSerializerSettings();
|
||||
public JsonSerializerSettings SerializerSettings { get; } = JsonSerializerSettingsProvider.CreateSerializerSettings();
|
||||
|
||||
IEnumerator<ICompatibilitySwitch> IEnumerable<ICompatibilitySwitch>.GetEnumerator()
|
||||
{
|
||||
return ((IEnumerable<ICompatibilitySwitch>)_switches).GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => _switches.GetEnumerator();
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc
|
||||
{
|
||||
internal class MvcJsonOptionsConfigureCompatibilityOptions : ConfigureCompatibilityOptions<MvcJsonOptions>
|
||||
{
|
||||
public MvcJsonOptionsConfigureCompatibilityOptions(
|
||||
ILoggerFactory loggerFactory,
|
||||
IOptions<MvcCompatibilityOptions> compatibilityOptions)
|
||||
: base(loggerFactory, compatibilityOptions)
|
||||
{
|
||||
}
|
||||
|
||||
protected override IReadOnlyDictionary<string, object> DefaultValues
|
||||
{
|
||||
get
|
||||
{
|
||||
var values = new Dictionary<string, object>();
|
||||
|
||||
if (Version >= CompatibilityVersion.Version_2_1)
|
||||
{
|
||||
values[nameof(MvcJsonOptions.AllowInputFormatterExceptionMessages)] = true;
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -799,7 +799,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
|
|||
private readonly bool _throwNonInputFormatterException;
|
||||
|
||||
public TestableJsonInputFormatter(bool throwNonInputFormatterException)
|
||||
: base(GetLogger(), new JsonSerializerSettings(), ArrayPool<char>.Shared, new DefaultObjectPoolProvider(), new MvcOptions())
|
||||
: base(GetLogger(), new JsonSerializerSettings(), ArrayPool<char>.Shared, new DefaultObjectPoolProvider(), new MvcOptions(), new MvcJsonOptions()
|
||||
{
|
||||
// The tests that use this class rely on the 2.1 behavior of this formatter.
|
||||
AllowInputFormatterExceptionMessages = true,
|
||||
})
|
||||
{
|
||||
_throwNonInputFormatterException = throwNonInputFormatterException;
|
||||
}
|
||||
|
|
@ -865,7 +869,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
|
|||
private readonly bool _throwNonInputFormatterException;
|
||||
|
||||
public DerivedJsonInputFormatter(bool throwNonInputFormatterException)
|
||||
: base(GetLogger(), new JsonSerializerSettings(), ArrayPool<char>.Shared, new DefaultObjectPoolProvider(), new MvcOptions())
|
||||
: base(GetLogger(), new JsonSerializerSettings(), ArrayPool<char>.Shared, new DefaultObjectPoolProvider(), new MvcOptions(), new MvcJsonOptions()
|
||||
{
|
||||
// The tests that use this class rely on the 2.1 behavior of this formatter.
|
||||
AllowInputFormatterExceptionMessages = true,
|
||||
})
|
||||
{
|
||||
_throwNonInputFormatterException = throwNonInputFormatterException;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,36 +28,32 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
private static readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings();
|
||||
|
||||
[Fact]
|
||||
public async Task BuffersRequestBody_ByDefault()
|
||||
public async Task Version_2_0_Constructor_BuffersRequestBody_ByDefault()
|
||||
{
|
||||
// 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);
|
||||
var formatter = new JsonInputFormatter(
|
||||
GetLogger(),
|
||||
_serializerSettings,
|
||||
ArrayPool<char>.Shared,
|
||||
_objectPoolProvider);
|
||||
#pragma warning restore CS0618
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var content = "{name: 'Person Name', Age: '30'}";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
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);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(User), httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(context);
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
|
||||
var userModel = Assert.IsType<User>(result.Model);
|
||||
Assert.Equal("Person Name", userModel.Name);
|
||||
Assert.Equal(30, userModel.Age);
|
||||
|
|
@ -65,48 +61,43 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
Assert.True(httpContext.Request.Body.CanSeek);
|
||||
httpContext.Request.Body.Seek(0L, SeekOrigin.Begin);
|
||||
|
||||
result = await formatter.ReadAsync(context);
|
||||
result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// 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()
|
||||
public async Task Version_2_1_Constructor_BuffersRequestBody_UsingDefaultOptions()
|
||||
{
|
||||
// Arrange
|
||||
var content = "{name: 'Person Name', Age: '30'}";
|
||||
var logger = GetLogger();
|
||||
var formatter = new JsonInputFormatter(
|
||||
logger,
|
||||
GetLogger(),
|
||||
_serializerSettings,
|
||||
ArrayPool<char>.Shared,
|
||||
_objectPoolProvider,
|
||||
new MvcOptions());
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
new MvcOptions(),
|
||||
new MvcJsonOptions());
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var content = "{name: 'Person Name', Age: '30'}";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
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);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(User), httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(context);
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
|
||||
var userModel = Assert.IsType<User>(result.Model);
|
||||
Assert.Equal("Person Name", userModel.Name);
|
||||
Assert.Equal(30, userModel.Age);
|
||||
|
|
@ -114,99 +105,50 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
Assert.True(httpContext.Request.Body.CanSeek);
|
||||
httpContext.Request.Body.Seek(0L, SeekOrigin.Begin);
|
||||
|
||||
result = await formatter.ReadAsync(context);
|
||||
result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// 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 SuppressInputFormatterBufferingSetToTrue_DoesNotBufferRequestBody()
|
||||
public async Task Version_2_0_Constructor_SuppressInputFormatterBufferingSetToTrue_DoesNotBufferRequestBody()
|
||||
{
|
||||
// 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();
|
||||
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_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,
|
||||
GetLogger(),
|
||||
_serializerSettings,
|
||||
ArrayPool<char>.Shared,
|
||||
_objectPoolProvider,
|
||||
mvcOptions);
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
suppressInputFormatterBuffering: true);
|
||||
#pragma warning restore CS0618
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var content = "{name: 'Person Name', Age: '30'}";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
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);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(User), httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(context);
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// 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);
|
||||
result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
|
|
@ -214,44 +156,87 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SuppressInputFormatterBufferingSetToTrue_UsingMutatedOptions()
|
||||
public async Task Version_2_1_Constructor_SuppressInputFormatterBuffering_UsingMvcOptions_DoesNotBufferRequestBody()
|
||||
{
|
||||
// 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 mvcOptions = new MvcOptions()
|
||||
{
|
||||
SuppressInputFormatterBuffering = true,
|
||||
};
|
||||
var formatter = new JsonInputFormatter(
|
||||
GetLogger(),
|
||||
_serializerSettings,
|
||||
ArrayPool<char>.Shared,
|
||||
_objectPoolProvider,
|
||||
mvcOptions,
|
||||
new MvcJsonOptions());
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var content = "{name: 'Person Name', Age: '30'}";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
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);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(User), httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// 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(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
Assert.Null(result.Model);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Version_2_1_Constructor_SuppressInputFormatterBufferingSetToTrue_UsingMutatedOptions()
|
||||
{
|
||||
// Arrange
|
||||
var mvcOptions = new MvcOptions()
|
||||
{
|
||||
SuppressInputFormatterBuffering = false,
|
||||
};
|
||||
var formatter = new JsonInputFormatter(
|
||||
GetLogger(),
|
||||
_serializerSettings,
|
||||
ArrayPool<char>.Shared,
|
||||
_objectPoolProvider,
|
||||
mvcOptions,
|
||||
new MvcJsonOptions());
|
||||
|
||||
var content = "{name: 'Person Name', Age: '30'}";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var httpContext = new DefaultHttpContext();
|
||||
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
|
||||
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
|
||||
httpContext.Request.ContentType = "application/json";
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(User), httpContext);
|
||||
|
||||
// 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);
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// 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);
|
||||
result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
|
|
@ -277,21 +262,12 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
public void CanRead_ReturnsTrueForAnySupportedContentType(string requestContentType, bool expectedCanRead)
|
||||
{
|
||||
// Arrange
|
||||
var loggerMock = GetLogger();
|
||||
var formatter = CreateFormatter();
|
||||
|
||||
var formatter =
|
||||
new JsonInputFormatter(loggerMock, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var contentBytes = Encoding.UTF8.GetBytes("content");
|
||||
|
||||
var httpContext = GetHttpContext(contentBytes, contentType: requestContentType);
|
||||
var provider = new EmptyModelMetadataProvider();
|
||||
var metadata = provider.GetMetadataForType(typeof(string));
|
||||
var formatterContext = new InputFormatterContext(
|
||||
httpContext,
|
||||
modelName: string.Empty,
|
||||
modelState: new ModelStateDictionary(),
|
||||
metadata: metadata,
|
||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(string), httpContext);
|
||||
|
||||
// Act
|
||||
var result = formatter.CanRead(formatterContext);
|
||||
|
|
@ -304,9 +280,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
public void DefaultMediaType_ReturnsApplicationJson()
|
||||
{
|
||||
// Arrange
|
||||
var loggerMock = GetLogger();
|
||||
var formatter =
|
||||
new JsonInputFormatter(loggerMock, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var formatter = CreateFormatter();
|
||||
|
||||
// Act
|
||||
var mediaType = formatter.SupportedMediaTypes[0];
|
||||
|
|
@ -321,8 +295,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
{
|
||||
yield return new object[] { "100", typeof(int), 100 };
|
||||
yield return new object[] { "'abcd'", typeof(string), "abcd" };
|
||||
yield return new object[] { "'2012-02-01 12:45 AM'", typeof(DateTime),
|
||||
new DateTime(2012, 02, 01, 00, 45, 00) };
|
||||
yield return new object[] { "'2012-02-01 12:45 AM'", typeof(DateTime), new DateTime(2012, 02, 01, 00, 45, 00) };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -331,23 +304,15 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
public async Task JsonFormatterReadsSimpleTypes(string content, Type type, object expected)
|
||||
{
|
||||
// Arrange
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var formatter = CreateFormatter();
|
||||
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
var provider = new EmptyModelMetadataProvider();
|
||||
var metadata = provider.GetMetadataForType(type);
|
||||
var context = new InputFormatterContext(
|
||||
httpContext,
|
||||
modelName: string.Empty,
|
||||
modelState: new ModelStateDictionary(),
|
||||
metadata: metadata,
|
||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(type, httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(context);
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
|
|
@ -358,24 +323,16 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
public async Task JsonFormatterReadsComplexTypes()
|
||||
{
|
||||
// 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 formatter = CreateFormatter();
|
||||
|
||||
var content = "{name: 'Person Name', Age: '30'}";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
var provider = new EmptyModelMetadataProvider();
|
||||
var metadata = provider.GetMetadataForType(typeof(User));
|
||||
var context = new InputFormatterContext(
|
||||
httpContext,
|
||||
modelName: string.Empty,
|
||||
modelState: new ModelStateDictionary(),
|
||||
metadata: metadata,
|
||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(User), httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(context);
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
|
|
@ -388,25 +345,16 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
public async Task ReadAsync_ReadsValidArray()
|
||||
{
|
||||
// Arrange
|
||||
var content = "[0, 23, 300]";
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var formatter = CreateFormatter();
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var content = "[0, 23, 300]";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
var provider = new EmptyModelMetadataProvider();
|
||||
var metadata = provider.GetMetadataForType(typeof(int[]));
|
||||
var context = new InputFormatterContext(
|
||||
httpContext,
|
||||
modelName: string.Empty,
|
||||
modelState: modelState,
|
||||
metadata: metadata,
|
||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(int[]), httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(context);
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
|
|
@ -422,25 +370,16 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
public async Task ReadAsync_ReadsValidArray_AsList(Type requestedType)
|
||||
{
|
||||
// Arrange
|
||||
var content = "[0, 23, 300]";
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var formatter = CreateFormatter();
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var content = "[0, 23, 300]";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
var provider = new EmptyModelMetadataProvider();
|
||||
var metadata = provider.GetMetadataForType(requestedType);
|
||||
var context = new InputFormatterContext(
|
||||
httpContext,
|
||||
modelName: string.Empty,
|
||||
modelState: modelState,
|
||||
metadata: metadata,
|
||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(requestedType, httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(context);
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
|
|
@ -452,126 +391,90 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
public async Task ReadAsync_AddsModelValidationErrorsToModelState()
|
||||
{
|
||||
// Arrange
|
||||
var content = "{name: 'Person Name', Age: 'not-an-age'}";
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var formatter = CreateFormatter(allowInputFormatterExceptionMessages: true);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var content = "{name: 'Person Name', Age: 'not-an-age'}";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
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);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(User), httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(context);
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.HasError);
|
||||
Assert.Equal(
|
||||
"Could not convert string to decimal: not-an-age. Path 'Age', line 1, position 39.",
|
||||
modelState["Age"].Errors[0].ErrorMessage);
|
||||
formatterContext.ModelState["Age"].Errors[0].ErrorMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReadAsync_InvalidArray_AddsOverflowErrorsToModelState()
|
||||
{
|
||||
// Arrange
|
||||
var content = "[0, 23, 300]";
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var formatter = CreateFormatter(allowInputFormatterExceptionMessages: true);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var content = "[0, 23, 300]";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
var provider = new EmptyModelMetadataProvider();
|
||||
var metadata = provider.GetMetadataForType(typeof(byte[]));
|
||||
var context = new InputFormatterContext(
|
||||
httpContext,
|
||||
modelName: string.Empty,
|
||||
modelState: modelState,
|
||||
metadata: metadata,
|
||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(byte[]), httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(context);
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.HasError);
|
||||
Assert.Equal("The supplied value is invalid.", modelState["[2]"].Errors[0].ErrorMessage);
|
||||
Assert.Null(modelState["[2]"].Errors[0].Exception);
|
||||
Assert.Equal("The supplied value is invalid.", formatterContext.ModelState["[2]"].Errors[0].ErrorMessage);
|
||||
Assert.Null(formatterContext.ModelState["[2]"].Errors[0].Exception);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReadAsync_InvalidComplexArray_AddsOverflowErrorsToModelState()
|
||||
{
|
||||
// Arrange
|
||||
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 MvcOptions());
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var formatter = CreateFormatter(allowInputFormatterExceptionMessages: true);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var content = "[{name: 'Name One', Age: 30}, {name: 'Name Two', Small: 300}]";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
var provider = new EmptyModelMetadataProvider();
|
||||
var metadata = provider.GetMetadataForType(typeof(User[]));
|
||||
var context = new InputFormatterContext(
|
||||
httpContext,
|
||||
modelName: "names",
|
||||
modelState: modelState,
|
||||
metadata: metadata,
|
||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(User[]), httpContext, modelName: "names");
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(context);
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.HasError);
|
||||
Assert.Equal(
|
||||
"Error converting value 300 to type 'System.Byte'. Path '[1].Small', line 1, position 59.",
|
||||
modelState["names[1].Small"].Errors[0].ErrorMessage);
|
||||
formatterContext.ModelState["names[1].Small"].Errors[0].ErrorMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReadAsync_UsesTryAddModelValidationErrorsToModelState()
|
||||
{
|
||||
// Arrange
|
||||
var formatter = CreateFormatter();
|
||||
|
||||
var content = "{name: 'Person Name', Age: 'not-an-age'}";
|
||||
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();
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
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);
|
||||
|
||||
modelState.MaxAllowedErrors = 3;
|
||||
modelState.AddModelError("key1", "error1");
|
||||
modelState.AddModelError("key2", "error2");
|
||||
var formatterContext = CreateInputFormatterContext(typeof(User), httpContext);
|
||||
formatterContext.ModelState.MaxAllowedErrors = 3;
|
||||
formatterContext.ModelState.AddModelError("key1", "error1");
|
||||
formatterContext.ModelState.AddModelError("key2", "error2");
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(context);
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.HasError);
|
||||
Assert.False(modelState.ContainsKey("age"));
|
||||
var error = Assert.Single(modelState[""].Errors);
|
||||
|
||||
Assert.False(formatterContext.ModelState.ContainsKey("age"));
|
||||
var error = Assert.Single(formatterContext.ModelState[""].Errors);
|
||||
Assert.IsType<TooManyModelErrorsException>(error.Exception);
|
||||
}
|
||||
|
||||
|
|
@ -580,28 +483,24 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
[InlineData("null", false, false)]
|
||||
[InlineData(" ", true, true)]
|
||||
[InlineData(" ", false, false)]
|
||||
public async Task ReadAsync_WithInputThatDeserializesToNull_SetsModelOnlyIfAllowingEmptyInput(string content, bool allowEmptyInput, bool expectedIsModelSet)
|
||||
public async Task ReadAsync_WithInputThatDeserializesToNull_SetsModelOnlyIfAllowingEmptyInput(
|
||||
string content,
|
||||
bool treatEmptyInputAsDefaultValue,
|
||||
bool expectedIsModelSet)
|
||||
{
|
||||
// Arrange
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var formatter = CreateFormatter();
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
var provider = new EmptyModelMetadataProvider();
|
||||
var metadata = provider.GetMetadataForType(typeof(object));
|
||||
var context = new InputFormatterContext(
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(
|
||||
typeof(object),
|
||||
httpContext,
|
||||
modelName: string.Empty,
|
||||
modelState: modelState,
|
||||
metadata: metadata,
|
||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader,
|
||||
treatEmptyInputAsDefaultValue: allowEmptyInput);
|
||||
treatEmptyInputAsDefaultValue: treatEmptyInputAsDefaultValue);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(context);
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
|
|
@ -616,45 +515,35 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
var serializerSettings = new JsonSerializerSettings();
|
||||
|
||||
// Act
|
||||
var jsonFormatter = new TestableJsonInputFormatter(serializerSettings);
|
||||
var formatter = new TestableJsonInputFormatter(serializerSettings);
|
||||
|
||||
// Assert
|
||||
Assert.Same(serializerSettings, jsonFormatter.SerializerSettings);
|
||||
Assert.Same(serializerSettings, formatter.SerializerSettings);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CustomSerializerSettingsObject_TakesEffect()
|
||||
{
|
||||
// Arrange
|
||||
// missing password property here
|
||||
var contentBytes = Encoding.UTF8.GetBytes("{ \"UserName\" : \"John\"}");
|
||||
var logger = GetLogger();
|
||||
|
||||
// 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 MvcOptions());
|
||||
var formatter = CreateFormatter(serializerSettings, allowInputFormatterExceptionMessages: true);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
// missing password property here
|
||||
var contentBytes = Encoding.UTF8.GetBytes("{ \"UserName\" : \"John\"}");
|
||||
var httpContext = GetHttpContext(contentBytes, "application/json;charset=utf-8");
|
||||
var provider = new EmptyModelMetadataProvider();
|
||||
var metadata = provider.GetMetadataForType(typeof(UserLogin));
|
||||
var inputFormatterContext = new InputFormatterContext(
|
||||
httpContext,
|
||||
modelName: string.Empty,
|
||||
modelState: modelState,
|
||||
metadata: metadata,
|
||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(UserLogin), httpContext);
|
||||
|
||||
// Act
|
||||
var result = await jsonFormatter.ReadAsync(inputFormatterContext);
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.HasError);
|
||||
Assert.False(modelState.IsValid);
|
||||
Assert.False(formatterContext.ModelState.IsValid);
|
||||
|
||||
var modelErrorMessage = modelState.Values.First().Errors[0].ErrorMessage;
|
||||
Assert.Contains("Required property 'Password' not found in JSON", modelErrorMessage);
|
||||
var message = formatterContext.ModelState.Values.First().Errors[0].ErrorMessage;
|
||||
Assert.Contains("Required property 'Password' not found in JSON", message);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -684,86 +573,98 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
[InlineData("{\"age\":\"x\"}", "age", "Could not convert string to decimal: x. Path 'age', line 1, position 10.")]
|
||||
[InlineData("{\"login\":1}", "login", "Error converting value 1 to type 'Microsoft.AspNetCore.Mvc.Formatters.JsonInputFormatterTest+UserLogin'. Path 'login', line 1, position 10.")]
|
||||
[InlineData("{\"login\":{\"username\":\"somevalue\"}}", "login", "Required property 'Password' not found in JSON. Path 'login', line 1, position 33.")]
|
||||
public async Task ReadAsync_RegistersJsonInputExceptionsAsInputFormatterException(
|
||||
public async Task ReadAsync_WithAllowInputFormatterExceptionMessages_RegistersJsonInputExceptionsAsInputFormatterException(
|
||||
string content,
|
||||
string modelStateKey,
|
||||
string expectedMessage)
|
||||
{
|
||||
// Arrange
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var formatter = CreateFormatter(allowInputFormatterExceptionMessages: true);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
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);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(User), httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(context);
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.HasError);
|
||||
Assert.True(!modelState.IsValid);
|
||||
Assert.True(modelState.ContainsKey(modelStateKey));
|
||||
Assert.True(!formatterContext.ModelState.IsValid);
|
||||
Assert.True(formatterContext.ModelState.ContainsKey(modelStateKey));
|
||||
|
||||
var modelError = modelState[modelStateKey].Errors.Single();
|
||||
var modelError = formatterContext.ModelState[modelStateKey].Errors.Single();
|
||||
Assert.Equal(expectedMessage, modelError.ErrorMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReadAsync_WhenSuppressJsonDeserializationExceptionMessagesIsTrue_DoesNotWrapJsonInputExceptions()
|
||||
public async Task ReadAsync_DefaultOptions_DoesNotWrapJsonInputExceptions()
|
||||
{
|
||||
// Arrange
|
||||
var logger = GetLogger();
|
||||
var formatter = new JsonInputFormatter(
|
||||
logger,
|
||||
GetLogger(),
|
||||
_serializerSettings,
|
||||
ArrayPool<char>.Shared,
|
||||
_objectPoolProvider,
|
||||
new MvcOptions()
|
||||
{
|
||||
SuppressInputFormatterBuffering = false,
|
||||
SuppressJsonDeserializationExceptionMessagesInModelState = true
|
||||
});
|
||||
var contentBytes = Encoding.UTF8.GetBytes("{");
|
||||
var modelStateKey = string.Empty;
|
||||
new MvcOptions(),
|
||||
new MvcJsonOptions());
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var contentBytes = Encoding.UTF8.GetBytes("{");
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
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);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(User), httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(context);
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.HasError);
|
||||
Assert.True(!modelState.IsValid);
|
||||
Assert.True(modelState.ContainsKey(modelStateKey));
|
||||
Assert.True(!formatterContext.ModelState.IsValid);
|
||||
Assert.True(formatterContext.ModelState.ContainsKey(string.Empty));
|
||||
|
||||
var modelError = modelState[modelStateKey].Errors.Single();
|
||||
var modelError = formatterContext.ModelState[string.Empty].Errors.Single();
|
||||
Assert.IsNotType<InputFormatterException>(modelError.Exception);
|
||||
Assert.Empty(modelError.ErrorMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ReadAsync_AllowInputFormatterExceptionMessages_DoesNotWrapJsonInputExceptions()
|
||||
{
|
||||
// Arrange
|
||||
var formatter = new JsonInputFormatter(
|
||||
GetLogger(),
|
||||
_serializerSettings,
|
||||
ArrayPool<char>.Shared,
|
||||
_objectPoolProvider,
|
||||
new MvcOptions(),
|
||||
new MvcJsonOptions()
|
||||
{
|
||||
AllowInputFormatterExceptionMessages = true,
|
||||
});
|
||||
|
||||
var contentBytes = Encoding.UTF8.GetBytes("{");
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(User), httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.HasError);
|
||||
Assert.True(!formatterContext.ModelState.IsValid);
|
||||
Assert.True(formatterContext.ModelState.ContainsKey(string.Empty));
|
||||
|
||||
var modelError = formatterContext.ModelState[string.Empty].Errors.Single();
|
||||
Assert.Null(modelError.Exception);
|
||||
Assert.NotEmpty(modelError.ErrorMessage);
|
||||
}
|
||||
|
||||
private class TestableJsonInputFormatter : JsonInputFormatter
|
||||
{
|
||||
public TestableJsonInputFormatter(JsonSerializerSettings settings)
|
||||
: base(GetLogger(), settings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions())
|
||||
: base(GetLogger(), settings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions(), new MvcJsonOptions())
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -777,6 +678,20 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
return NullLogger.Instance;
|
||||
}
|
||||
|
||||
private JsonInputFormatter CreateFormatter(JsonSerializerSettings serializerSettings = null, bool allowInputFormatterExceptionMessages = false)
|
||||
{
|
||||
return new JsonInputFormatter(
|
||||
GetLogger(),
|
||||
serializerSettings ?? _serializerSettings,
|
||||
ArrayPool<char>.Shared,
|
||||
_objectPoolProvider,
|
||||
new MvcOptions(),
|
||||
new MvcJsonOptions()
|
||||
{
|
||||
AllowInputFormatterExceptionMessages = allowInputFormatterExceptionMessages,
|
||||
});
|
||||
}
|
||||
|
||||
private static HttpContext GetHttpContext(
|
||||
byte[] contentBytes,
|
||||
string contentType = "application/json")
|
||||
|
|
@ -800,6 +715,24 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
return httpContext.Object;
|
||||
}
|
||||
|
||||
private InputFormatterContext CreateInputFormatterContext(
|
||||
Type modelType,
|
||||
HttpContext httpContext,
|
||||
string modelName = null,
|
||||
bool treatEmptyInputAsDefaultValue = false)
|
||||
{
|
||||
var provider = new EmptyModelMetadataProvider();
|
||||
var metadata = provider.GetMetadataForType(modelType);
|
||||
|
||||
return new InputFormatterContext(
|
||||
httpContext,
|
||||
modelName: modelName ?? string.Empty,
|
||||
modelState: new ModelStateDictionary(),
|
||||
metadata: metadata,
|
||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader,
|
||||
treatEmptyInputAsDefaultValue: treatEmptyInputAsDefaultValue);
|
||||
}
|
||||
|
||||
private IEnumerable<string> GetModelStateErrorMessages(ModelStateDictionary modelStateDictionary)
|
||||
{
|
||||
var allErrorMessages = new List<string>();
|
||||
|
|
|
|||
|
|
@ -26,136 +26,128 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
private static readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings();
|
||||
|
||||
[Fact]
|
||||
public async Task BuffersRequestBody_ByDefault()
|
||||
public async Task Version_2_0_Constructor_BuffersRequestBody_ByDefault()
|
||||
{
|
||||
// Arrange
|
||||
var logger = GetLogger();
|
||||
#pragma warning disable CS0618
|
||||
var formatter =
|
||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
||||
var formatter = new JsonPatchInputFormatter(
|
||||
GetLogger(),
|
||||
_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);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(JsonPatchDocument<Customer>), httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(context);
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// 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);
|
||||
var patchDocument = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
||||
Assert.Equal("add", patchDocument.Operations[0].op);
|
||||
Assert.Equal("Customer/Name", patchDocument.Operations[0].path);
|
||||
Assert.Equal("John", patchDocument.Operations[0].value);
|
||||
|
||||
Assert.True(httpContext.Request.Body.CanSeek);
|
||||
httpContext.Request.Body.Seek(0L, SeekOrigin.Begin);
|
||||
|
||||
result = await formatter.ReadAsync(context);
|
||||
result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// 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);
|
||||
patchDocument = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
||||
Assert.Equal("add", patchDocument.Operations[0].op);
|
||||
Assert.Equal("Customer/Name", patchDocument.Operations[0].path);
|
||||
Assert.Equal("John", patchDocument.Operations[0].value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task BuffersRequestBody_UsingDefaultOptions()
|
||||
public async Task Version_2_1_Constructor_BuffersRequestBody_ByDefault()
|
||||
{
|
||||
// Arrange
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var formatter = new JsonPatchInputFormatter(
|
||||
GetLogger(),
|
||||
_serializerSettings,
|
||||
ArrayPool<char>.Shared,
|
||||
_objectPoolProvider,
|
||||
new MvcOptions(),
|
||||
new MvcJsonOptions());
|
||||
|
||||
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);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(JsonPatchDocument<Customer>), httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(context);
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// 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);
|
||||
var patchDocument = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
||||
Assert.Equal("add", patchDocument.Operations[0].op);
|
||||
Assert.Equal("Customer/Name", patchDocument.Operations[0].path);
|
||||
Assert.Equal("John", patchDocument.Operations[0].value);
|
||||
|
||||
Assert.True(httpContext.Request.Body.CanSeek);
|
||||
httpContext.Request.Body.Seek(0L, SeekOrigin.Begin);
|
||||
|
||||
result = await formatter.ReadAsync(context);
|
||||
result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// 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);
|
||||
patchDocument = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
||||
Assert.Equal("add", patchDocument.Operations[0].op);
|
||||
Assert.Equal("Customer/Name", patchDocument.Operations[0].path);
|
||||
Assert.Equal("John", patchDocument.Operations[0].value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SuppressInputFormatterBufferingSetToTrue_DoesNotBufferRequestBody()
|
||||
public async Task Version_2_0_Constructor_SuppressInputFormatterBuffering_DoesNotBufferRequestBody()
|
||||
{
|
||||
// Arrange
|
||||
var logger = GetLogger();
|
||||
#pragma warning disable CS0618
|
||||
var formatter =
|
||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, suppressInputFormatterBuffering: true);
|
||||
var formatter = new JsonPatchInputFormatter(
|
||||
GetLogger(),
|
||||
_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);
|
||||
|
||||
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);
|
||||
|
||||
var context = CreateInputFormatterContext(typeof(JsonPatchDocument<Customer>), httpContext);
|
||||
|
||||
// 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);
|
||||
|
||||
var patchDocument = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
||||
Assert.Equal("add", patchDocument.Operations[0].op);
|
||||
Assert.Equal("Customer/Name", patchDocument.Operations[0].path);
|
||||
Assert.Equal("John", patchDocument.Operations[0].value);
|
||||
|
||||
Assert.False(httpContext.Request.Body.CanSeek);
|
||||
result = await formatter.ReadAsync(context);
|
||||
|
|
@ -166,49 +158,46 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SuppressInputFormatterBufferingSetToTrue_UsingMutatedOptions_DoesNotBufferRequestBody()
|
||||
public async Task Version_2_1_Constructor_SuppressInputFormatterBuffering_DoesNotBufferRequestBody()
|
||||
{
|
||||
// Arrange
|
||||
var logger = GetLogger();
|
||||
var mvcOptions = new MvcOptions();
|
||||
mvcOptions.SuppressInputFormatterBuffering = false;
|
||||
var mvcOptions = new MvcOptions()
|
||||
{
|
||||
SuppressInputFormatterBuffering = false,
|
||||
};
|
||||
var formatter = new JsonPatchInputFormatter(
|
||||
logger,
|
||||
GetLogger(),
|
||||
_serializerSettings,
|
||||
ArrayPool<char>.Shared,
|
||||
_objectPoolProvider,
|
||||
mvcOptions);
|
||||
mvcOptions,
|
||||
new MvcJsonOptions());
|
||||
|
||||
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);
|
||||
|
||||
var formatterContext = CreateInputFormatterContext(typeof(JsonPatchDocument<Customer>), httpContext);
|
||||
|
||||
// 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);
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// 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);
|
||||
|
||||
var patchDocument = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
||||
Assert.Equal("add", patchDocument.Operations[0].op);
|
||||
Assert.Equal("Customer/Name", patchDocument.Operations[0].path);
|
||||
Assert.Equal("John", patchDocument.Operations[0].value);
|
||||
|
||||
Assert.False(httpContext.Request.Body.CanSeek);
|
||||
result = await formatter.ReadAsync(context);
|
||||
result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
|
|
@ -219,67 +208,49 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
public async Task JsonPatchInputFormatter_ReadsOneOperation_Successfully()
|
||||
{
|
||||
// Arrange
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var formatter = CreateFormatter();
|
||||
|
||||
var content = "[{\"op\":\"add\",\"path\":\"Customer/Name\",\"value\":\"John\"}]";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var httpContext = CreateHttpContext(contentBytes);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
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);
|
||||
var formatterContext = CreateInputFormatterContext(typeof(JsonPatchDocument<Customer>), httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(context);
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// 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);
|
||||
var patchDocument = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
||||
Assert.Equal("add", patchDocument.Operations[0].op);
|
||||
Assert.Equal("Customer/Name", patchDocument.Operations[0].path);
|
||||
Assert.Equal("John", patchDocument.Operations[0].value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task JsonPatchInputFormatter_ReadsMultipleOperations_Successfully()
|
||||
{
|
||||
// Arrange
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var formatter = CreateFormatter();
|
||||
|
||||
var content = "[{\"op\": \"add\", \"path\" : \"Customer/Name\", \"value\":\"John\"}," +
|
||||
"{\"op\": \"remove\", \"path\" : \"Customer/Name\"}]";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var httpContext = CreateHttpContext(contentBytes);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
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);
|
||||
var formatterContext = CreateInputFormatterContext(typeof(JsonPatchDocument<Customer>), httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(context);
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// 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.Equal("remove", patchDoc.Operations[1].op);
|
||||
Assert.Equal("Customer/Name", patchDoc.Operations[1].path);
|
||||
var patchDocument = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
||||
Assert.Equal("add", patchDocument.Operations[0].op);
|
||||
Assert.Equal("Customer/Name", patchDocument.Operations[0].path);
|
||||
Assert.Equal("John", patchDocument.Operations[0].value);
|
||||
Assert.Equal("remove", patchDocument.Operations[1].op);
|
||||
Assert.Equal("Customer/Name", patchDocument.Operations[1].path);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -290,22 +261,13 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
public void CanRead_ReturnsTrueOnlyForJsonPatchContentType(string requestContentType, bool expectedCanRead)
|
||||
{
|
||||
// Arrange
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var formatter = CreateFormatter();
|
||||
|
||||
var content = "[{\"op\": \"add\", \"path\" : \"Customer/Name\", \"value\":\"John\"}]";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var httpContext = CreateHttpContext(contentBytes, contentType: requestContentType);
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var httpContext = GetHttpContext(contentBytes, contentType: requestContentType);
|
||||
var provider = new EmptyModelMetadataProvider();
|
||||
var metadata = provider.GetMetadataForType(typeof(JsonPatchDocument<Customer>));
|
||||
var formatterContext = new InputFormatterContext(
|
||||
httpContext,
|
||||
modelName: string.Empty,
|
||||
modelState: modelState,
|
||||
metadata: metadata,
|
||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
||||
var formatterContext = CreateInputFormatterContext(typeof(JsonPatchDocument<Customer>), httpContext);
|
||||
|
||||
// Act
|
||||
var result = formatter.CanRead(formatterContext);
|
||||
|
|
@ -320,22 +282,15 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
public void CanRead_ReturnsFalse_NonJsonPatchContentType(Type modelType)
|
||||
{
|
||||
// Arrange
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
var formatter = CreateFormatter();
|
||||
|
||||
var content = "[{\"op\": \"add\", \"path\" : \"Customer/Name\", \"value\":\"John\"}]";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var httpContext = CreateHttpContext(contentBytes, contentType: "application/json-patch+json");
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var httpContext = GetHttpContext(contentBytes, contentType: "application/json-patch+json");
|
||||
var provider = new EmptyModelMetadataProvider();
|
||||
var metadata = provider.GetMetadataForType(modelType);
|
||||
var formatterContext = new InputFormatterContext(
|
||||
httpContext,
|
||||
modelName: string.Empty,
|
||||
modelState: modelState,
|
||||
metadata: metadata,
|
||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
||||
var formatterContext = CreateInputFormatterContext(modelType, httpContext);
|
||||
|
||||
// Act
|
||||
var result = formatter.CanRead(formatterContext);
|
||||
|
|
@ -351,29 +306,21 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
var exceptionMessage = "Cannot deserialize the current JSON array (e.g. [1,2,3]) into type " +
|
||||
$"'{typeof(Customer).FullName}' because the type requires a JSON object ";
|
||||
|
||||
var logger = GetLogger();
|
||||
var formatter =
|
||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
||||
// This test relies on 2.1 error message behavior
|
||||
var formatter = CreateFormatter(allowInputFormatterExceptionMessages: true);
|
||||
|
||||
var content = "[{\"op\": \"add\", \"path\" : \"Customer/Name\", \"value\":\"John\"}]";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var httpContext = CreateHttpContext(contentBytes, contentType: "application/json-patch+json");
|
||||
|
||||
var modelState = new ModelStateDictionary();
|
||||
var httpContext = GetHttpContext(contentBytes, contentType: "application/json-patch+json");
|
||||
var provider = new EmptyModelMetadataProvider();
|
||||
var metadata = provider.GetMetadataForType(typeof(Customer));
|
||||
var context = new InputFormatterContext(
|
||||
httpContext,
|
||||
modelName: string.Empty,
|
||||
modelState: modelState,
|
||||
metadata: metadata,
|
||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
||||
var formatterContext = CreateInputFormatterContext(typeof(Customer), httpContext);
|
||||
|
||||
// Act
|
||||
var result = await formatter.ReadAsync(context);
|
||||
var result = await formatter.ReadAsync(formatterContext);
|
||||
|
||||
// Assert
|
||||
Assert.True(result.HasError);
|
||||
Assert.Contains(exceptionMessage, modelState[""].Errors[0].ErrorMessage);
|
||||
Assert.Contains(exceptionMessage, formatterContext.ModelState[""].Errors[0].ErrorMessage);
|
||||
}
|
||||
|
||||
private static ILogger GetLogger()
|
||||
|
|
@ -381,7 +328,34 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
return NullLogger.Instance;
|
||||
}
|
||||
|
||||
private static HttpContext GetHttpContext(
|
||||
private JsonPatchInputFormatter CreateFormatter(bool allowInputFormatterExceptionMessages = false)
|
||||
{
|
||||
return new JsonPatchInputFormatter(
|
||||
NullLogger.Instance,
|
||||
_serializerSettings,
|
||||
ArrayPool<char>.Shared,
|
||||
_objectPoolProvider,
|
||||
new MvcOptions(),
|
||||
new MvcJsonOptions()
|
||||
{
|
||||
AllowInputFormatterExceptionMessages = allowInputFormatterExceptionMessages,
|
||||
});
|
||||
}
|
||||
|
||||
private InputFormatterContext CreateInputFormatterContext(Type modelType, HttpContext httpContext)
|
||||
{
|
||||
var provider = new EmptyModelMetadataProvider();
|
||||
var metadata = provider.GetMetadataForType(modelType);
|
||||
|
||||
return new InputFormatterContext(
|
||||
httpContext,
|
||||
modelName: string.Empty,
|
||||
modelState: new ModelStateDictionary(),
|
||||
metadata: metadata,
|
||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
||||
}
|
||||
|
||||
private static HttpContext CreateHttpContext(
|
||||
byte[] contentBytes,
|
||||
string contentType = "application/json-patch+json")
|
||||
{
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Fact] // This test covers the 2.0 behavior. JSON.Net error messages are not preserved.
|
||||
public async Task JsonInputFormatter_SuppliedJsonDeserializationErrorMessage()
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -109,7 +109,9 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
||||
Assert.Equal("{\"\":[\"Unexpected end when reading JSON. Path '', line 1, position 1.\"]}", responseBody);
|
||||
|
||||
// Update me in 3.0 xD
|
||||
Assert.Equal("{\"\":[\"The input was not valid.\"]}", responseBody);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ using System.Threading.Tasks;
|
|||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
||||
|
|
@ -448,7 +449,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
public int Address { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Fact] // This tests the 2.0 behavior. Error messages from JSON.NET are not preserved.
|
||||
public async Task FromBodyAndRequiredOnValueTypeProperty_EmptyBody_JsonFormatterAddsModelStateError()
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -485,11 +486,10 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
Assert.Null(entry.Value.AttemptedValue);
|
||||
Assert.Null(entry.Value.RawValue);
|
||||
var error = Assert.Single(entry.Value.Errors);
|
||||
Assert.Null(error.Exception);
|
||||
|
||||
// Json.NET currently throws an exception starting with "No JSON content found and type 'System.Int32' is
|
||||
// not nullable." but do not tie test to a particular Json.NET build.
|
||||
Assert.NotEmpty(error.ErrorMessage);
|
||||
|
||||
// Update me in 3.0 when MvcJsonOptions.AllowInputFormatterExceptionMessages is removed
|
||||
Assert.IsType<JsonSerializationException>(error.Exception);
|
||||
Assert.Empty(error.ErrorMessage);
|
||||
}
|
||||
|
||||
private class Person5
|
||||
|
|
@ -545,7 +545,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
Assert.Empty(modelState);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Fact] // This test covers the 2.0 behavior. Error messages from JSON.Net are preserved.
|
||||
public async Task FromBodyWithInvalidPropertyData_JsonFormatterAddsModelError()
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -586,18 +586,18 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
|||
Assert.Null(state.AttemptedValue);
|
||||
Assert.Null(state.RawValue);
|
||||
var error = Assert.Single(state.Errors);
|
||||
Assert.Null(error.Exception);
|
||||
|
||||
// Json.NET currently throws an Exception with a Message starting with "Could not convert string to
|
||||
// integer: not a number." but do not tie test to a particular Json.NET build.
|
||||
Assert.NotEmpty(error.ErrorMessage);
|
||||
// Update me in 3.0 when MvcJsonOptions.AllowInputFormatterExceptionMessages is removed
|
||||
Assert.IsType<JsonReaderException>(error.Exception);
|
||||
Assert.Empty(error.ErrorMessage);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(false, false)]
|
||||
[InlineData(true, true)]
|
||||
public async Task FromBodyWithEmptyBody_JsonFormatterAddsModelErrorWhenExpected(
|
||||
bool allowEmptyInputInBodyModelBindingSetting, bool expectedModelStateIsValid)
|
||||
bool allowEmptyInputInBodyModelBindingSetting,
|
||||
bool expectedModelStateIsValid)
|
||||
{
|
||||
// Arrange
|
||||
var parameter = new ParameterDescriptor
|
||||
|
|
|
|||
|
|
@ -28,11 +28,12 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTest
|
|||
|
||||
// Act
|
||||
var mvcOptions = services.GetRequiredService<IOptions<MvcOptions>>().Value;
|
||||
var jsonOptions = services.GetRequiredService<IOptions<MvcJsonOptions>>().Value;
|
||||
|
||||
// Assert
|
||||
Assert.False(mvcOptions.SuppressBindingUndefinedValueToEnumType);
|
||||
Assert.Equal(InputFormatterExceptionPolicy.AllExceptions, mvcOptions.InputFormatterExceptionPolicy);
|
||||
Assert.False(mvcOptions.SuppressJsonDeserializationExceptionMessagesInModelState); // This name needs to be inverted in #7157
|
||||
Assert.False(jsonOptions.AllowInputFormatterExceptionMessages);
|
||||
}
|
||||
|
||||
[Fact(Skip = "#7157 - some settings have the wrong values, this test should pass once #7157 is fixed")]
|
||||
|
|
@ -47,11 +48,12 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTest
|
|||
|
||||
// Act
|
||||
var mvcOptions = services.GetRequiredService<IOptions<MvcOptions>>().Value;
|
||||
var jsonOptions = services.GetRequiredService<IOptions<MvcJsonOptions>>().Value;
|
||||
|
||||
// Assert
|
||||
Assert.True(mvcOptions.SuppressBindingUndefinedValueToEnumType);
|
||||
Assert.Equal(InputFormatterExceptionPolicy.MalformedInputExceptions, mvcOptions.InputFormatterExceptionPolicy);
|
||||
Assert.True(mvcOptions.SuppressJsonDeserializationExceptionMessagesInModelState); // This name needs to be inverted in #7157
|
||||
Assert.True(jsonOptions.AllowInputFormatterExceptionMessages);
|
||||
}
|
||||
|
||||
[Fact(Skip = "#7157 - some settings have the wrong values, this test should pass once #7157 is fixed")]
|
||||
|
|
@ -66,11 +68,12 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTest
|
|||
|
||||
// Act
|
||||
var mvcOptions = services.GetRequiredService<IOptions<MvcOptions>>().Value;
|
||||
var jsonOptions = services.GetRequiredService<IOptions<MvcJsonOptions>>().Value;
|
||||
|
||||
// Assert
|
||||
Assert.True(mvcOptions.SuppressBindingUndefinedValueToEnumType);
|
||||
Assert.Equal(InputFormatterExceptionPolicy.MalformedInputExceptions, mvcOptions.InputFormatterExceptionPolicy);
|
||||
Assert.True(mvcOptions.SuppressJsonDeserializationExceptionMessagesInModelState); // This name needs to be inverted in #7157
|
||||
Assert.True(jsonOptions.AllowInputFormatterExceptionMessages);
|
||||
}
|
||||
|
||||
// This just does the minimum needed to be able to resolve these options.
|
||||
|
|
|
|||
|
|
@ -386,6 +386,13 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
typeof(RazorPagesOptions).Assembly.GetType("Microsoft.AspNetCore.Mvc.RazorPages.RazorPagesOptionsConfigureCompatibilityOptions", throwOnError: true),
|
||||
}
|
||||
},
|
||||
{
|
||||
typeof(IPostConfigureOptions<MvcJsonOptions>),
|
||||
new[]
|
||||
{
|
||||
typeof(MvcJsonOptions).Assembly.GetType("Microsoft.AspNetCore.Mvc.MvcJsonOptionsConfigureCompatibilityOptions", throwOnError: true),
|
||||
}
|
||||
},
|
||||
{
|
||||
typeof(IActionConstraintProvider),
|
||||
new Type[]
|
||||
|
|
|
|||
Loading…
Reference in New Issue