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>
|
/// <remarks>
|
||||||
/// ASP.NET Core MVC 2.1 introduces compatibility switches for the following:
|
/// ASP.NET Core MVC 2.1 introduces compatibility switches for the following:
|
||||||
/// <list type="bullet">
|
/// <list type="bullet">
|
||||||
|
/// <item><description><see cref="MvcOptions.InputFormatterExceptionPolicy"/></description></item>
|
||||||
/// <item><description><see cref="MvcOptions.SuppressBindingUndefinedValueToEnumType"/></description></item>
|
/// <item><description><see cref="MvcOptions.SuppressBindingUndefinedValueToEnumType"/></description></item>
|
||||||
|
/// <item><description><c>MvcJsonOptions.AllowInputFormatterExceptionMessages</c></description></item>
|
||||||
/// <item><description><c>RazorPagesOptions.AllowAreas</c></description></item>
|
/// <item><description><c>RazorPagesOptions.AllowAreas</c></description></item>
|
||||||
/// </list>
|
/// </list>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,11 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
// See CompatibilitySwitch.cs for guide on how to implement these.
|
// See CompatibilitySwitch.cs for guide on how to implement these.
|
||||||
private readonly CompatibilitySwitch<InputFormatterExceptionPolicy> _inputFormatterExceptionPolicy;
|
private readonly CompatibilitySwitch<InputFormatterExceptionPolicy> _inputFormatterExceptionPolicy;
|
||||||
private readonly CompatibilitySwitch<bool> _suppressBindingUndefinedValueToEnumType;
|
private readonly CompatibilitySwitch<bool> _suppressBindingUndefinedValueToEnumType;
|
||||||
private readonly CompatibilitySwitch<bool> _suppressJsonDeserializationExceptionMessagesInModelState;
|
|
||||||
private readonly ICompatibilitySwitch[] _switches;
|
private readonly ICompatibilitySwitch[] _switches;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of <see cref="MvcOptions"/>.
|
||||||
|
/// </summary>
|
||||||
public MvcOptions()
|
public MvcOptions()
|
||||||
{
|
{
|
||||||
CacheProfiles = new Dictionary<string, CacheProfile>(StringComparer.OrdinalIgnoreCase);
|
CacheProfiles = new Dictionary<string, CacheProfile>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
@ -43,12 +45,11 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
|
|
||||||
_inputFormatterExceptionPolicy = new CompatibilitySwitch<InputFormatterExceptionPolicy>(nameof(InputFormatterExceptionPolicy), InputFormatterExceptionPolicy.AllExceptions);
|
_inputFormatterExceptionPolicy = new CompatibilitySwitch<InputFormatterExceptionPolicy>(nameof(InputFormatterExceptionPolicy), InputFormatterExceptionPolicy.AllExceptions);
|
||||||
_suppressBindingUndefinedValueToEnumType = new CompatibilitySwitch<bool>(nameof(SuppressBindingUndefinedValueToEnumType));
|
_suppressBindingUndefinedValueToEnumType = new CompatibilitySwitch<bool>(nameof(SuppressBindingUndefinedValueToEnumType));
|
||||||
_suppressJsonDeserializationExceptionMessagesInModelState = new CompatibilitySwitch<bool>(nameof(SuppressJsonDeserializationExceptionMessagesInModelState));
|
|
||||||
_switches = new ICompatibilitySwitch[]
|
_switches = new ICompatibilitySwitch[]
|
||||||
{
|
{
|
||||||
_inputFormatterExceptionPolicy,
|
_inputFormatterExceptionPolicy,
|
||||||
_suppressBindingUndefinedValueToEnumType,
|
_suppressBindingUndefinedValueToEnumType,
|
||||||
_suppressJsonDeserializationExceptionMessagesInModelState,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -241,20 +242,6 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool RequireHttpsPermanent { get; set; }
|
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()
|
IEnumerator<ICompatibilitySwitch> IEnumerable<ICompatibilitySwitch>.GetEnumerator()
|
||||||
{
|
{
|
||||||
return ((IEnumerable<ICompatibilitySwitch>)_switches).GetEnumerator();
|
return ((IEnumerable<ICompatibilitySwitch>)_switches).GetEnumerator();
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
{
|
{
|
||||||
services.TryAddEnumerable(
|
services.TryAddEnumerable(
|
||||||
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, MvcJsonMvcOptionsSetup>());
|
ServiceDescriptor.Transient<IConfigureOptions<MvcOptions>, MvcJsonMvcOptionsSetup>());
|
||||||
|
services.TryAddEnumerable(
|
||||||
|
ServiceDescriptor.Transient<IPostConfigureOptions<MvcJsonOptions>, MvcJsonOptionsConfigureCompatibilityOptions>());
|
||||||
services.TryAddEnumerable(
|
services.TryAddEnumerable(
|
||||||
ServiceDescriptor.Transient<IApiDescriptionProvider, JsonPatchOperationsArrayProvider>());
|
ServiceDescriptor.Transient<IApiDescriptionProvider, JsonPatchOperationsArrayProvider>());
|
||||||
services.TryAddSingleton<JsonResultExecutor>();
|
services.TryAddSingleton<JsonResultExecutor>();
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Extensions.ObjectPool;
|
using Microsoft.Extensions.ObjectPool;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.Formatters.Json.Internal
|
namespace Microsoft.AspNetCore.Mvc.Formatters.Json.Internal
|
||||||
|
|
@ -20,7 +19,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Json.Internal
|
||||||
public class MvcJsonMvcOptionsSetup : IConfigureOptions<MvcOptions>
|
public class MvcJsonMvcOptionsSetup : IConfigureOptions<MvcOptions>
|
||||||
{
|
{
|
||||||
private readonly ILoggerFactory _loggerFactory;
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
private readonly JsonSerializerSettings _jsonSerializerSettings;
|
private readonly MvcJsonOptions _jsonOptions;
|
||||||
private readonly ArrayPool<char> _charPool;
|
private readonly ArrayPool<char> _charPool;
|
||||||
private readonly ObjectPoolProvider _objectPoolProvider;
|
private readonly ObjectPoolProvider _objectPoolProvider;
|
||||||
|
|
||||||
|
|
@ -51,14 +50,14 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Json.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
_loggerFactory = loggerFactory;
|
_loggerFactory = loggerFactory;
|
||||||
_jsonSerializerSettings = jsonOptions.Value.SerializerSettings;
|
_jsonOptions = jsonOptions.Value;
|
||||||
_charPool = charPool;
|
_charPool = charPool;
|
||||||
_objectPoolProvider = objectPoolProvider;
|
_objectPoolProvider = objectPoolProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Configure(MvcOptions options)
|
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
|
// Register JsonPatchInputFormatter before JsonInputFormatter, otherwise
|
||||||
// JsonInputFormatter would consume "application/json-patch+json" requests
|
// JsonInputFormatter would consume "application/json-patch+json" requests
|
||||||
|
|
@ -66,18 +65,20 @@ namespace Microsoft.AspNetCore.Mvc.Formatters.Json.Internal
|
||||||
var jsonInputPatchLogger = _loggerFactory.CreateLogger<JsonPatchInputFormatter>();
|
var jsonInputPatchLogger = _loggerFactory.CreateLogger<JsonPatchInputFormatter>();
|
||||||
options.InputFormatters.Add(new JsonPatchInputFormatter(
|
options.InputFormatters.Add(new JsonPatchInputFormatter(
|
||||||
jsonInputPatchLogger,
|
jsonInputPatchLogger,
|
||||||
_jsonSerializerSettings,
|
_jsonOptions.SerializerSettings,
|
||||||
_charPool,
|
_charPool,
|
||||||
_objectPoolProvider,
|
_objectPoolProvider,
|
||||||
options));
|
options,
|
||||||
|
_jsonOptions));
|
||||||
|
|
||||||
var jsonInputLogger = _loggerFactory.CreateLogger<JsonInputFormatter>();
|
var jsonInputLogger = _loggerFactory.CreateLogger<JsonInputFormatter>();
|
||||||
options.InputFormatters.Add(new JsonInputFormatter(
|
options.InputFormatters.Add(new JsonInputFormatter(
|
||||||
jsonInputLogger,
|
jsonInputLogger,
|
||||||
_jsonSerializerSettings,
|
_jsonOptions.SerializerSettings,
|
||||||
_charPool,
|
_charPool,
|
||||||
_objectPoolProvider,
|
_objectPoolProvider,
|
||||||
options));
|
options,
|
||||||
|
_jsonOptions));
|
||||||
|
|
||||||
options.FormatterMappings.SetMediaTypeMappingForFormat("json", MediaTypeHeaderValue.Parse("application/json"));
|
options.FormatterMappings.SetMediaTypeMappingForFormat("json", MediaTypeHeaderValue.Parse("application/json"));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,8 +28,12 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly ObjectPoolProvider _objectPoolProvider;
|
private readonly ObjectPoolProvider _objectPoolProvider;
|
||||||
private readonly MvcOptions _options;
|
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 _suppressInputFormatterBuffering;
|
||||||
private readonly bool _suppressJsonDeserializationExceptionMessages;
|
private readonly bool _allowInputFormatterExceptionMessages;
|
||||||
|
|
||||||
private ObjectPool<JsonSerializer> _jsonSerializerPool;
|
private ObjectPool<JsonSerializer> _jsonSerializerPool;
|
||||||
|
|
||||||
|
|
@ -74,10 +78,10 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
ArrayPool<char> charPool,
|
ArrayPool<char> charPool,
|
||||||
ObjectPoolProvider objectPoolProvider,
|
ObjectPoolProvider objectPoolProvider,
|
||||||
bool suppressInputFormatterBuffering)
|
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
|
// This constructor by default treats JSON deserialization exceptions as unsafe
|
||||||
// because this is the default for applications generally
|
// because this is the default in 2.0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -92,7 +96,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
/// <param name="charPool">The <see cref="ArrayPool{Char}"/>.</param>
|
/// <param name="charPool">The <see cref="ArrayPool{Char}"/>.</param>
|
||||||
/// <param name="objectPoolProvider">The <see cref="ObjectPoolProvider"/>.</param>
|
/// <param name="objectPoolProvider">The <see cref="ObjectPoolProvider"/>.</param>
|
||||||
/// <param name="suppressInputFormatterBuffering">Flag to buffer entire request body before deserializing it.</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.")]
|
[Obsolete("This constructor is obsolete and will be removed in a future version.")]
|
||||||
public JsonInputFormatter(
|
public JsonInputFormatter(
|
||||||
ILogger logger,
|
ILogger logger,
|
||||||
|
|
@ -100,7 +104,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
ArrayPool<char> charPool,
|
ArrayPool<char> charPool,
|
||||||
ObjectPoolProvider objectPoolProvider,
|
ObjectPoolProvider objectPoolProvider,
|
||||||
bool suppressInputFormatterBuffering,
|
bool suppressInputFormatterBuffering,
|
||||||
bool suppressJsonDeserializationExceptionMessages)
|
bool allowInputFormatterExceptionMessages)
|
||||||
{
|
{
|
||||||
if (logger == null)
|
if (logger == null)
|
||||||
{
|
{
|
||||||
|
|
@ -127,7 +131,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
_charPool = new JsonArrayPool<char>(charPool);
|
_charPool = new JsonArrayPool<char>(charPool);
|
||||||
_objectPoolProvider = objectPoolProvider;
|
_objectPoolProvider = objectPoolProvider;
|
||||||
_suppressInputFormatterBuffering = suppressInputFormatterBuffering;
|
_suppressInputFormatterBuffering = suppressInputFormatterBuffering;
|
||||||
_suppressJsonDeserializationExceptionMessages = suppressJsonDeserializationExceptionMessages;
|
_allowInputFormatterExceptionMessages = allowInputFormatterExceptionMessages;
|
||||||
|
|
||||||
SupportedEncodings.Add(UTF8EncodingWithoutBOM);
|
SupportedEncodings.Add(UTF8EncodingWithoutBOM);
|
||||||
SupportedEncodings.Add(UTF16EncodingLittleEndian);
|
SupportedEncodings.Add(UTF16EncodingLittleEndian);
|
||||||
|
|
@ -149,12 +153,14 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
/// <param name="charPool">The <see cref="ArrayPool{Char}"/>.</param>
|
/// <param name="charPool">The <see cref="ArrayPool{Char}"/>.</param>
|
||||||
/// <param name="objectPoolProvider">The <see cref="ObjectPoolProvider"/>.</param>
|
/// <param name="objectPoolProvider">The <see cref="ObjectPoolProvider"/>.</param>
|
||||||
/// <param name="options">The <see cref="MvcOptions"/>.</param>
|
/// <param name="options">The <see cref="MvcOptions"/>.</param>
|
||||||
|
/// <param name="jsonOptions">The <see cref="MvcJsonOptions"/>.</param>
|
||||||
public JsonInputFormatter(
|
public JsonInputFormatter(
|
||||||
ILogger logger,
|
ILogger logger,
|
||||||
JsonSerializerSettings serializerSettings,
|
JsonSerializerSettings serializerSettings,
|
||||||
ArrayPool<char> charPool,
|
ArrayPool<char> charPool,
|
||||||
ObjectPoolProvider objectPoolProvider,
|
ObjectPoolProvider objectPoolProvider,
|
||||||
MvcOptions options)
|
MvcOptions options,
|
||||||
|
MvcJsonOptions jsonOptions)
|
||||||
{
|
{
|
||||||
if (logger == null)
|
if (logger == null)
|
||||||
{
|
{
|
||||||
|
|
@ -181,6 +187,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
_charPool = new JsonArrayPool<char>(charPool);
|
_charPool = new JsonArrayPool<char>(charPool);
|
||||||
_objectPoolProvider = objectPoolProvider;
|
_objectPoolProvider = objectPoolProvider;
|
||||||
_options = options;
|
_options = options;
|
||||||
|
_jsonOptions = jsonOptions;
|
||||||
|
|
||||||
SupportedEncodings.Add(UTF8EncodingWithoutBOM);
|
SupportedEncodings.Add(UTF8EncodingWithoutBOM);
|
||||||
SupportedEncodings.Add(UTF16EncodingLittleEndian);
|
SupportedEncodings.Add(UTF16EncodingLittleEndian);
|
||||||
|
|
@ -406,17 +413,26 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
|
|
||||||
private Exception WrapExceptionForModelState(Exception exception)
|
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
|
// 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
|
// other than these two types, but we're being conservative and limiting which ones
|
||||||
// we regard as having safe messages to expose to clients
|
// we regard as having safe messages to expose to clients
|
||||||
var isJsonExceptionType =
|
if (exception is JsonReaderException || exception is JsonSerializationException)
|
||||||
exception is JsonReaderException || exception is JsonSerializationException;
|
{
|
||||||
var suppressJsonDeserializationExceptionMessages = _options?.SuppressJsonDeserializationExceptionMessagesInModelState ?? _suppressJsonDeserializationExceptionMessages;
|
// InputFormatterException specifies that the message is safe to return to a client, it will
|
||||||
var suppressOriginalMessage =
|
// be added to model state.
|
||||||
suppressJsonDeserializationExceptionMessages || !isJsonExceptionType;
|
return new InputFormatterException(exception.Message, exception);
|
||||||
return suppressOriginalMessage
|
}
|
||||||
? exception
|
|
||||||
: 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,
|
ArrayPool<char> charPool,
|
||||||
ObjectPoolProvider objectPoolProvider,
|
ObjectPoolProvider objectPoolProvider,
|
||||||
bool suppressInputFormatterBuffering)
|
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="charPool">The <see cref="ArrayPool{Char}"/>.</param>
|
||||||
/// <param name="objectPoolProvider">The <see cref="ObjectPoolProvider"/>.</param>
|
/// <param name="objectPoolProvider">The <see cref="ObjectPoolProvider"/>.</param>
|
||||||
/// <param name="suppressInputFormatterBuffering">Flag to buffer entire request body before deserializing it.</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.")]
|
[Obsolete("This constructor is obsolete and will be removed in a future version.")]
|
||||||
public JsonPatchInputFormatter(
|
public JsonPatchInputFormatter(
|
||||||
ILogger logger,
|
ILogger logger,
|
||||||
|
|
@ -83,8 +85,8 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
ArrayPool<char> charPool,
|
ArrayPool<char> charPool,
|
||||||
ObjectPoolProvider objectPoolProvider,
|
ObjectPoolProvider objectPoolProvider,
|
||||||
bool suppressInputFormatterBuffering,
|
bool suppressInputFormatterBuffering,
|
||||||
bool suppressJsonDeserializationExceptionMessages)
|
bool allowInputFormatterExceptionMessages)
|
||||||
: base(logger, serializerSettings, charPool, objectPoolProvider, suppressInputFormatterBuffering, suppressJsonDeserializationExceptionMessages)
|
: base(logger, serializerSettings, charPool, objectPoolProvider, suppressInputFormatterBuffering, allowInputFormatterExceptionMessages)
|
||||||
{
|
{
|
||||||
// Clear all values and only include json-patch+json value.
|
// Clear all values and only include json-patch+json value.
|
||||||
SupportedMediaTypes.Clear();
|
SupportedMediaTypes.Clear();
|
||||||
|
|
@ -104,13 +106,15 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
/// <param name="charPool">The <see cref="ArrayPool{Char}"/>.</param>
|
/// <param name="charPool">The <see cref="ArrayPool{Char}"/>.</param>
|
||||||
/// <param name="objectPoolProvider">The <see cref="ObjectPoolProvider"/>.</param>
|
/// <param name="objectPoolProvider">The <see cref="ObjectPoolProvider"/>.</param>
|
||||||
/// <param name="options">The <see cref="MvcOptions"/>.</param>
|
/// <param name="options">The <see cref="MvcOptions"/>.</param>
|
||||||
|
/// <param name="jsonOptions">The <see cref="MvcJsonOptions"/>.</param>
|
||||||
public JsonPatchInputFormatter(
|
public JsonPatchInputFormatter(
|
||||||
ILogger logger,
|
ILogger logger,
|
||||||
JsonSerializerSettings serializerSettings,
|
JsonSerializerSettings serializerSettings,
|
||||||
ArrayPool<char> charPool,
|
ArrayPool<char> charPool,
|
||||||
ObjectPoolProvider objectPoolProvider,
|
ObjectPoolProvider objectPoolProvider,
|
||||||
MvcOptions options)
|
MvcOptions options,
|
||||||
: base(logger, serializerSettings, charPool, objectPoolProvider, options)
|
MvcJsonOptions jsonOptions)
|
||||||
|
: base(logger, serializerSettings, charPool, objectPoolProvider, options, jsonOptions)
|
||||||
{
|
{
|
||||||
// Clear all values and only include json-patch+json value.
|
// Clear all values and only include json-patch+json value.
|
||||||
SupportedMediaTypes.Clear();
|
SupportedMediaTypes.Clear();
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Mvc.Formatters;
|
using Microsoft.AspNetCore.Mvc.Formatters;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc
|
namespace Microsoft.AspNetCore.Mvc
|
||||||
|
|
@ -9,12 +13,69 @@ namespace Microsoft.AspNetCore.Mvc
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides programmatic configuration for JSON in the MVC framework.
|
/// Provides programmatic configuration for JSON in the MVC framework.
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// Gets the <see cref="JsonSerializerSettings"/> that are used by this application.
|
/// Gets the <see cref="JsonSerializerSettings"/> that are used by this application.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public JsonSerializerSettings SerializerSettings { get; } =
|
public JsonSerializerSettings SerializerSettings { get; } = JsonSerializerSettingsProvider.CreateSerializerSettings();
|
||||||
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;
|
private readonly bool _throwNonInputFormatterException;
|
||||||
|
|
||||||
public TestableJsonInputFormatter(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;
|
_throwNonInputFormatterException = throwNonInputFormatterException;
|
||||||
}
|
}
|
||||||
|
|
@ -865,7 +869,11 @@ namespace Microsoft.AspNetCore.Mvc.ModelBinding.Binders
|
||||||
private readonly bool _throwNonInputFormatterException;
|
private readonly bool _throwNonInputFormatterException;
|
||||||
|
|
||||||
public DerivedJsonInputFormatter(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;
|
_throwNonInputFormatterException = throwNonInputFormatterException;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,36 +28,32 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
private static readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings();
|
private static readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings();
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task BuffersRequestBody_ByDefault()
|
public async Task Version_2_0_Constructor_BuffersRequestBody_ByDefault()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var content = "{name: 'Person Name', Age: '30'}";
|
|
||||||
var logger = GetLogger();
|
|
||||||
#pragma warning disable CS0618
|
#pragma warning disable CS0618
|
||||||
var formatter =
|
var formatter = new JsonInputFormatter(
|
||||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
GetLogger(),
|
||||||
|
_serializerSettings,
|
||||||
|
ArrayPool<char>.Shared,
|
||||||
|
_objectPoolProvider);
|
||||||
#pragma warning restore CS0618
|
#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();
|
var httpContext = new DefaultHttpContext();
|
||||||
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
|
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
|
||||||
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
|
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
|
||||||
httpContext.Request.ContentType = "application/json";
|
httpContext.Request.ContentType = "application/json";
|
||||||
var provider = new EmptyModelMetadataProvider();
|
|
||||||
var metadata = provider.GetMetadataForType(typeof(User));
|
var formatterContext = CreateInputFormatterContext(typeof(User), httpContext);
|
||||||
var context = new InputFormatterContext(
|
|
||||||
httpContext,
|
|
||||||
modelName: string.Empty,
|
|
||||||
modelState: modelState,
|
|
||||||
metadata: metadata,
|
|
||||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await formatter.ReadAsync(context);
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.HasError);
|
Assert.False(result.HasError);
|
||||||
|
|
||||||
var userModel = Assert.IsType<User>(result.Model);
|
var userModel = Assert.IsType<User>(result.Model);
|
||||||
Assert.Equal("Person Name", userModel.Name);
|
Assert.Equal("Person Name", userModel.Name);
|
||||||
Assert.Equal(30, userModel.Age);
|
Assert.Equal(30, userModel.Age);
|
||||||
|
|
@ -65,48 +61,43 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
Assert.True(httpContext.Request.Body.CanSeek);
|
Assert.True(httpContext.Request.Body.CanSeek);
|
||||||
httpContext.Request.Body.Seek(0L, SeekOrigin.Begin);
|
httpContext.Request.Body.Seek(0L, SeekOrigin.Begin);
|
||||||
|
|
||||||
result = await formatter.ReadAsync(context);
|
result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.HasError);
|
Assert.False(result.HasError);
|
||||||
|
|
||||||
userModel = Assert.IsType<User>(result.Model);
|
userModel = Assert.IsType<User>(result.Model);
|
||||||
Assert.Equal("Person Name", userModel.Name);
|
Assert.Equal("Person Name", userModel.Name);
|
||||||
Assert.Equal(30, userModel.Age);
|
Assert.Equal(30, userModel.Age);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task BuffersRequestBody_UsingDefaultOptions()
|
public async Task Version_2_1_Constructor_BuffersRequestBody_UsingDefaultOptions()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var content = "{name: 'Person Name', Age: '30'}";
|
|
||||||
var logger = GetLogger();
|
|
||||||
var formatter = new JsonInputFormatter(
|
var formatter = new JsonInputFormatter(
|
||||||
logger,
|
GetLogger(),
|
||||||
_serializerSettings,
|
_serializerSettings,
|
||||||
ArrayPool<char>.Shared,
|
ArrayPool<char>.Shared,
|
||||||
_objectPoolProvider,
|
_objectPoolProvider,
|
||||||
new MvcOptions());
|
new MvcOptions(),
|
||||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
new MvcJsonOptions());
|
||||||
|
|
||||||
var modelState = new ModelStateDictionary();
|
var content = "{name: 'Person Name', Age: '30'}";
|
||||||
|
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||||
var httpContext = new DefaultHttpContext();
|
var httpContext = new DefaultHttpContext();
|
||||||
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
|
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
|
||||||
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
|
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
|
||||||
httpContext.Request.ContentType = "application/json";
|
httpContext.Request.ContentType = "application/json";
|
||||||
var provider = new EmptyModelMetadataProvider();
|
|
||||||
var metadata = provider.GetMetadataForType(typeof(User));
|
var formatterContext = CreateInputFormatterContext(typeof(User), httpContext);
|
||||||
var context = new InputFormatterContext(
|
|
||||||
httpContext,
|
|
||||||
modelName: string.Empty,
|
|
||||||
modelState: modelState,
|
|
||||||
metadata: metadata,
|
|
||||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await formatter.ReadAsync(context);
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.HasError);
|
Assert.False(result.HasError);
|
||||||
|
|
||||||
var userModel = Assert.IsType<User>(result.Model);
|
var userModel = Assert.IsType<User>(result.Model);
|
||||||
Assert.Equal("Person Name", userModel.Name);
|
Assert.Equal("Person Name", userModel.Name);
|
||||||
Assert.Equal(30, userModel.Age);
|
Assert.Equal(30, userModel.Age);
|
||||||
|
|
@ -114,99 +105,50 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
Assert.True(httpContext.Request.Body.CanSeek);
|
Assert.True(httpContext.Request.Body.CanSeek);
|
||||||
httpContext.Request.Body.Seek(0L, SeekOrigin.Begin);
|
httpContext.Request.Body.Seek(0L, SeekOrigin.Begin);
|
||||||
|
|
||||||
result = await formatter.ReadAsync(context);
|
result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.HasError);
|
Assert.False(result.HasError);
|
||||||
|
|
||||||
userModel = Assert.IsType<User>(result.Model);
|
userModel = Assert.IsType<User>(result.Model);
|
||||||
Assert.Equal("Person Name", userModel.Name);
|
Assert.Equal("Person Name", userModel.Name);
|
||||||
Assert.Equal(30, userModel.Age);
|
Assert.Equal(30, userModel.Age);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task SuppressInputFormatterBufferingSetToTrue_DoesNotBufferRequestBody()
|
public async Task Version_2_0_Constructor_SuppressInputFormatterBufferingSetToTrue_DoesNotBufferRequestBody()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var content = "{name: 'Person Name', Age: '30'}";
|
|
||||||
var logger = GetLogger();
|
|
||||||
#pragma warning disable CS0618
|
#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(
|
var formatter = new JsonInputFormatter(
|
||||||
logger,
|
GetLogger(),
|
||||||
_serializerSettings,
|
_serializerSettings,
|
||||||
ArrayPool<char>.Shared,
|
ArrayPool<char>.Shared,
|
||||||
_objectPoolProvider,
|
_objectPoolProvider,
|
||||||
mvcOptions);
|
suppressInputFormatterBuffering: true);
|
||||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
#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();
|
var httpContext = new DefaultHttpContext();
|
||||||
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
|
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
|
||||||
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
|
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
|
||||||
httpContext.Request.ContentType = "application/json";
|
httpContext.Request.ContentType = "application/json";
|
||||||
var provider = new EmptyModelMetadataProvider();
|
|
||||||
var metadata = provider.GetMetadataForType(typeof(User));
|
var formatterContext = CreateInputFormatterContext(typeof(User), httpContext);
|
||||||
var context = new InputFormatterContext(
|
|
||||||
httpContext,
|
|
||||||
modelName: string.Empty,
|
|
||||||
modelState: modelState,
|
|
||||||
metadata: metadata,
|
|
||||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await formatter.ReadAsync(context);
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.HasError);
|
Assert.False(result.HasError);
|
||||||
|
|
||||||
var userModel = Assert.IsType<User>(result.Model);
|
var userModel = Assert.IsType<User>(result.Model);
|
||||||
Assert.Equal("Person Name", userModel.Name);
|
Assert.Equal("Person Name", userModel.Name);
|
||||||
Assert.Equal(30, userModel.Age);
|
Assert.Equal(30, userModel.Age);
|
||||||
|
|
||||||
Assert.False(httpContext.Request.Body.CanSeek);
|
Assert.False(httpContext.Request.Body.CanSeek);
|
||||||
result = await formatter.ReadAsync(context);
|
result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.HasError);
|
Assert.False(result.HasError);
|
||||||
|
|
@ -214,44 +156,87 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task SuppressInputFormatterBufferingSetToTrue_UsingMutatedOptions()
|
public async Task Version_2_1_Constructor_SuppressInputFormatterBuffering_UsingMvcOptions_DoesNotBufferRequestBody()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var content = "{name: 'Person Name', Age: '30'}";
|
var mvcOptions = new MvcOptions()
|
||||||
var logger = GetLogger();
|
{
|
||||||
var mvcOptions = new MvcOptions();
|
SuppressInputFormatterBuffering = true,
|
||||||
mvcOptions.SuppressInputFormatterBuffering = false;
|
};
|
||||||
var formatter =
|
var formatter = new JsonInputFormatter(
|
||||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, mvcOptions);
|
GetLogger(),
|
||||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
_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();
|
var httpContext = new DefaultHttpContext();
|
||||||
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
|
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
|
||||||
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
|
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
|
||||||
httpContext.Request.ContentType = "application/json";
|
httpContext.Request.ContentType = "application/json";
|
||||||
var provider = new EmptyModelMetadataProvider();
|
|
||||||
var metadata = provider.GetMetadataForType(typeof(User));
|
var formatterContext = CreateInputFormatterContext(typeof(User), httpContext);
|
||||||
var context = new InputFormatterContext(
|
|
||||||
httpContext,
|
// Act
|
||||||
modelName: string.Empty,
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
modelState: modelState,
|
|
||||||
metadata: metadata,
|
// Assert
|
||||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
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
|
// Act
|
||||||
// Mutate options after passing into the constructor to make sure that the value type is not store in the constructor
|
// Mutate options after passing into the constructor to make sure that the value type is not store in the constructor
|
||||||
mvcOptions.SuppressInputFormatterBuffering = true;
|
mvcOptions.SuppressInputFormatterBuffering = true;
|
||||||
var result = await formatter.ReadAsync(context);
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.HasError);
|
Assert.False(result.HasError);
|
||||||
|
|
||||||
var userModel = Assert.IsType<User>(result.Model);
|
var userModel = Assert.IsType<User>(result.Model);
|
||||||
Assert.Equal("Person Name", userModel.Name);
|
Assert.Equal("Person Name", userModel.Name);
|
||||||
Assert.Equal(30, userModel.Age);
|
Assert.Equal(30, userModel.Age);
|
||||||
|
|
||||||
Assert.False(httpContext.Request.Body.CanSeek);
|
Assert.False(httpContext.Request.Body.CanSeek);
|
||||||
result = await formatter.ReadAsync(context);
|
result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.HasError);
|
Assert.False(result.HasError);
|
||||||
|
|
@ -277,21 +262,12 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
public void CanRead_ReturnsTrueForAnySupportedContentType(string requestContentType, bool expectedCanRead)
|
public void CanRead_ReturnsTrueForAnySupportedContentType(string requestContentType, bool expectedCanRead)
|
||||||
{
|
{
|
||||||
// Arrange
|
// 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 contentBytes = Encoding.UTF8.GetBytes("content");
|
||||||
|
|
||||||
var httpContext = GetHttpContext(contentBytes, contentType: requestContentType);
|
var httpContext = GetHttpContext(contentBytes, contentType: requestContentType);
|
||||||
var provider = new EmptyModelMetadataProvider();
|
|
||||||
var metadata = provider.GetMetadataForType(typeof(string));
|
var formatterContext = CreateInputFormatterContext(typeof(string), httpContext);
|
||||||
var formatterContext = new InputFormatterContext(
|
|
||||||
httpContext,
|
|
||||||
modelName: string.Empty,
|
|
||||||
modelState: new ModelStateDictionary(),
|
|
||||||
metadata: metadata,
|
|
||||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = formatter.CanRead(formatterContext);
|
var result = formatter.CanRead(formatterContext);
|
||||||
|
|
@ -304,9 +280,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
public void DefaultMediaType_ReturnsApplicationJson()
|
public void DefaultMediaType_ReturnsApplicationJson()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var loggerMock = GetLogger();
|
var formatter = CreateFormatter();
|
||||||
var formatter =
|
|
||||||
new JsonInputFormatter(loggerMock, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var mediaType = formatter.SupportedMediaTypes[0];
|
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[] { "100", typeof(int), 100 };
|
||||||
yield return new object[] { "'abcd'", typeof(string), "abcd" };
|
yield return new object[] { "'abcd'", typeof(string), "abcd" };
|
||||||
yield return new object[] { "'2012-02-01 12:45 AM'", typeof(DateTime),
|
yield return new object[] { "'2012-02-01 12:45 AM'", typeof(DateTime), new DateTime(2012, 02, 01, 00, 45, 00) };
|
||||||
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)
|
public async Task JsonFormatterReadsSimpleTypes(string content, Type type, object expected)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var logger = GetLogger();
|
var formatter = CreateFormatter();
|
||||||
var formatter =
|
|
||||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
|
||||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
|
||||||
|
|
||||||
|
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||||
var httpContext = GetHttpContext(contentBytes);
|
var httpContext = GetHttpContext(contentBytes);
|
||||||
var provider = new EmptyModelMetadataProvider();
|
|
||||||
var metadata = provider.GetMetadataForType(type);
|
var formatterContext = CreateInputFormatterContext(type, httpContext);
|
||||||
var context = new InputFormatterContext(
|
|
||||||
httpContext,
|
|
||||||
modelName: string.Empty,
|
|
||||||
modelState: new ModelStateDictionary(),
|
|
||||||
metadata: metadata,
|
|
||||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await formatter.ReadAsync(context);
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.HasError);
|
Assert.False(result.HasError);
|
||||||
|
|
@ -358,24 +323,16 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
public async Task JsonFormatterReadsComplexTypes()
|
public async Task JsonFormatterReadsComplexTypes()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var content = "{name: 'Person Name', Age: '30'}";
|
var formatter = CreateFormatter();
|
||||||
var logger = GetLogger();
|
|
||||||
var formatter =
|
|
||||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
|
||||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
|
||||||
|
|
||||||
|
var content = "{name: 'Person Name', Age: '30'}";
|
||||||
|
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||||
var httpContext = GetHttpContext(contentBytes);
|
var httpContext = GetHttpContext(contentBytes);
|
||||||
var provider = new EmptyModelMetadataProvider();
|
|
||||||
var metadata = provider.GetMetadataForType(typeof(User));
|
var formatterContext = CreateInputFormatterContext(typeof(User), httpContext);
|
||||||
var context = new InputFormatterContext(
|
|
||||||
httpContext,
|
|
||||||
modelName: string.Empty,
|
|
||||||
modelState: new ModelStateDictionary(),
|
|
||||||
metadata: metadata,
|
|
||||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await formatter.ReadAsync(context);
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.HasError);
|
Assert.False(result.HasError);
|
||||||
|
|
@ -388,25 +345,16 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
public async Task ReadAsync_ReadsValidArray()
|
public async Task ReadAsync_ReadsValidArray()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var content = "[0, 23, 300]";
|
var formatter = CreateFormatter();
|
||||||
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 content = "[0, 23, 300]";
|
||||||
|
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||||
var httpContext = GetHttpContext(contentBytes);
|
var httpContext = GetHttpContext(contentBytes);
|
||||||
var provider = new EmptyModelMetadataProvider();
|
|
||||||
var metadata = provider.GetMetadataForType(typeof(int[]));
|
var formatterContext = CreateInputFormatterContext(typeof(int[]), httpContext);
|
||||||
var context = new InputFormatterContext(
|
|
||||||
httpContext,
|
|
||||||
modelName: string.Empty,
|
|
||||||
modelState: modelState,
|
|
||||||
metadata: metadata,
|
|
||||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await formatter.ReadAsync(context);
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.HasError);
|
Assert.False(result.HasError);
|
||||||
|
|
@ -422,25 +370,16 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
public async Task ReadAsync_ReadsValidArray_AsList(Type requestedType)
|
public async Task ReadAsync_ReadsValidArray_AsList(Type requestedType)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var content = "[0, 23, 300]";
|
var formatter = CreateFormatter();
|
||||||
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 content = "[0, 23, 300]";
|
||||||
|
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||||
var httpContext = GetHttpContext(contentBytes);
|
var httpContext = GetHttpContext(contentBytes);
|
||||||
var provider = new EmptyModelMetadataProvider();
|
|
||||||
var metadata = provider.GetMetadataForType(requestedType);
|
var formatterContext = CreateInputFormatterContext(requestedType, httpContext);
|
||||||
var context = new InputFormatterContext(
|
|
||||||
httpContext,
|
|
||||||
modelName: string.Empty,
|
|
||||||
modelState: modelState,
|
|
||||||
metadata: metadata,
|
|
||||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await formatter.ReadAsync(context);
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.HasError);
|
Assert.False(result.HasError);
|
||||||
|
|
@ -452,126 +391,90 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
public async Task ReadAsync_AddsModelValidationErrorsToModelState()
|
public async Task ReadAsync_AddsModelValidationErrorsToModelState()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var content = "{name: 'Person Name', Age: 'not-an-age'}";
|
var formatter = CreateFormatter(allowInputFormatterExceptionMessages: true);
|
||||||
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 content = "{name: 'Person Name', Age: 'not-an-age'}";
|
||||||
|
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||||
var httpContext = GetHttpContext(contentBytes);
|
var httpContext = GetHttpContext(contentBytes);
|
||||||
var provider = new EmptyModelMetadataProvider();
|
|
||||||
var metadata = provider.GetMetadataForType(typeof(User));
|
var formatterContext = CreateInputFormatterContext(typeof(User), httpContext);
|
||||||
var context = new InputFormatterContext(
|
|
||||||
httpContext,
|
|
||||||
modelName: string.Empty,
|
|
||||||
modelState: modelState,
|
|
||||||
metadata: metadata,
|
|
||||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await formatter.ReadAsync(context);
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(result.HasError);
|
Assert.True(result.HasError);
|
||||||
Assert.Equal(
|
Assert.Equal(
|
||||||
"Could not convert string to decimal: not-an-age. Path 'Age', line 1, position 39.",
|
"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]
|
[Fact]
|
||||||
public async Task ReadAsync_InvalidArray_AddsOverflowErrorsToModelState()
|
public async Task ReadAsync_InvalidArray_AddsOverflowErrorsToModelState()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var content = "[0, 23, 300]";
|
var formatter = CreateFormatter(allowInputFormatterExceptionMessages: true);
|
||||||
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 content = "[0, 23, 300]";
|
||||||
|
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||||
var httpContext = GetHttpContext(contentBytes);
|
var httpContext = GetHttpContext(contentBytes);
|
||||||
var provider = new EmptyModelMetadataProvider();
|
|
||||||
var metadata = provider.GetMetadataForType(typeof(byte[]));
|
var formatterContext = CreateInputFormatterContext(typeof(byte[]), httpContext);
|
||||||
var context = new InputFormatterContext(
|
|
||||||
httpContext,
|
|
||||||
modelName: string.Empty,
|
|
||||||
modelState: modelState,
|
|
||||||
metadata: metadata,
|
|
||||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await formatter.ReadAsync(context);
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(result.HasError);
|
Assert.True(result.HasError);
|
||||||
Assert.Equal("The supplied value is invalid.", modelState["[2]"].Errors[0].ErrorMessage);
|
Assert.Equal("The supplied value is invalid.", formatterContext.ModelState["[2]"].Errors[0].ErrorMessage);
|
||||||
Assert.Null(modelState["[2]"].Errors[0].Exception);
|
Assert.Null(formatterContext.ModelState["[2]"].Errors[0].Exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ReadAsync_InvalidComplexArray_AddsOverflowErrorsToModelState()
|
public async Task ReadAsync_InvalidComplexArray_AddsOverflowErrorsToModelState()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var content = "[{name: 'Name One', Age: 30}, {name: 'Name Two', Small: 300}]";
|
var formatter = CreateFormatter(allowInputFormatterExceptionMessages: true);
|
||||||
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 content = "[{name: 'Name One', Age: 30}, {name: 'Name Two', Small: 300}]";
|
||||||
|
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||||
var httpContext = GetHttpContext(contentBytes);
|
var httpContext = GetHttpContext(contentBytes);
|
||||||
var provider = new EmptyModelMetadataProvider();
|
|
||||||
var metadata = provider.GetMetadataForType(typeof(User[]));
|
var formatterContext = CreateInputFormatterContext(typeof(User[]), httpContext, modelName: "names");
|
||||||
var context = new InputFormatterContext(
|
|
||||||
httpContext,
|
|
||||||
modelName: "names",
|
|
||||||
modelState: modelState,
|
|
||||||
metadata: metadata,
|
|
||||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await formatter.ReadAsync(context);
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(result.HasError);
|
Assert.True(result.HasError);
|
||||||
Assert.Equal(
|
Assert.Equal(
|
||||||
"Error converting value 300 to type 'System.Byte'. Path '[1].Small', line 1, position 59.",
|
"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]
|
[Fact]
|
||||||
public async Task ReadAsync_UsesTryAddModelValidationErrorsToModelState()
|
public async Task ReadAsync_UsesTryAddModelValidationErrorsToModelState()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var formatter = CreateFormatter();
|
||||||
|
|
||||||
var content = "{name: 'Person Name', Age: 'not-an-age'}";
|
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 contentBytes = Encoding.UTF8.GetBytes(content);
|
||||||
|
|
||||||
var modelState = new ModelStateDictionary();
|
|
||||||
var httpContext = GetHttpContext(contentBytes);
|
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;
|
var formatterContext = CreateInputFormatterContext(typeof(User), httpContext);
|
||||||
modelState.AddModelError("key1", "error1");
|
formatterContext.ModelState.MaxAllowedErrors = 3;
|
||||||
modelState.AddModelError("key2", "error2");
|
formatterContext.ModelState.AddModelError("key1", "error1");
|
||||||
|
formatterContext.ModelState.AddModelError("key2", "error2");
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await formatter.ReadAsync(context);
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(result.HasError);
|
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);
|
Assert.IsType<TooManyModelErrorsException>(error.Exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -580,28 +483,24 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
[InlineData("null", false, false)]
|
[InlineData("null", false, false)]
|
||||||
[InlineData(" ", true, true)]
|
[InlineData(" ", true, true)]
|
||||||
[InlineData(" ", false, false)]
|
[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
|
// Arrange
|
||||||
var logger = GetLogger();
|
var formatter = CreateFormatter();
|
||||||
var formatter =
|
|
||||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
|
||||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
|
||||||
|
|
||||||
var modelState = new ModelStateDictionary();
|
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||||
var httpContext = GetHttpContext(contentBytes);
|
var httpContext = GetHttpContext(contentBytes);
|
||||||
var provider = new EmptyModelMetadataProvider();
|
|
||||||
var metadata = provider.GetMetadataForType(typeof(object));
|
var formatterContext = CreateInputFormatterContext(
|
||||||
var context = new InputFormatterContext(
|
typeof(object),
|
||||||
httpContext,
|
httpContext,
|
||||||
modelName: string.Empty,
|
treatEmptyInputAsDefaultValue: treatEmptyInputAsDefaultValue);
|
||||||
modelState: modelState,
|
|
||||||
metadata: metadata,
|
|
||||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader,
|
|
||||||
treatEmptyInputAsDefaultValue: allowEmptyInput);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await formatter.ReadAsync(context);
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.HasError);
|
Assert.False(result.HasError);
|
||||||
|
|
@ -616,45 +515,35 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
var serializerSettings = new JsonSerializerSettings();
|
var serializerSettings = new JsonSerializerSettings();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var jsonFormatter = new TestableJsonInputFormatter(serializerSettings);
|
var formatter = new TestableJsonInputFormatter(serializerSettings);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Same(serializerSettings, jsonFormatter.SerializerSettings);
|
Assert.Same(serializerSettings, formatter.SerializerSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CustomSerializerSettingsObject_TakesEffect()
|
public async Task CustomSerializerSettingsObject_TakesEffect()
|
||||||
{
|
{
|
||||||
// Arrange
|
// 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
|
// by default we ignore missing members, so here explicitly changing it
|
||||||
var serializerSettings = new JsonSerializerSettings { MissingMemberHandling = MissingMemberHandling.Error };
|
var serializerSettings = new JsonSerializerSettings { MissingMemberHandling = MissingMemberHandling.Error };
|
||||||
var jsonFormatter =
|
var formatter = CreateFormatter(serializerSettings, allowInputFormatterExceptionMessages: true);
|
||||||
new JsonInputFormatter(logger, serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
|
||||||
|
|
||||||
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 httpContext = GetHttpContext(contentBytes, "application/json;charset=utf-8");
|
||||||
var provider = new EmptyModelMetadataProvider();
|
|
||||||
var metadata = provider.GetMetadataForType(typeof(UserLogin));
|
var formatterContext = CreateInputFormatterContext(typeof(UserLogin), httpContext);
|
||||||
var inputFormatterContext = new InputFormatterContext(
|
|
||||||
httpContext,
|
|
||||||
modelName: string.Empty,
|
|
||||||
modelState: modelState,
|
|
||||||
metadata: metadata,
|
|
||||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await jsonFormatter.ReadAsync(inputFormatterContext);
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(result.HasError);
|
Assert.True(result.HasError);
|
||||||
Assert.False(modelState.IsValid);
|
Assert.False(formatterContext.ModelState.IsValid);
|
||||||
|
|
||||||
var modelErrorMessage = modelState.Values.First().Errors[0].ErrorMessage;
|
var message = formatterContext.ModelState.Values.First().Errors[0].ErrorMessage;
|
||||||
Assert.Contains("Required property 'Password' not found in JSON", modelErrorMessage);
|
Assert.Contains("Required property 'Password' not found in JSON", message);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[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("{\"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\":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.")]
|
[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 content,
|
||||||
string modelStateKey,
|
string modelStateKey,
|
||||||
string expectedMessage)
|
string expectedMessage)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var logger = GetLogger();
|
var formatter = CreateFormatter(allowInputFormatterExceptionMessages: true);
|
||||||
var formatter =
|
|
||||||
new JsonInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
|
||||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
|
||||||
|
|
||||||
var modelState = new ModelStateDictionary();
|
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||||
var httpContext = GetHttpContext(contentBytes);
|
var httpContext = GetHttpContext(contentBytes);
|
||||||
var provider = new EmptyModelMetadataProvider();
|
|
||||||
var metadata = provider.GetMetadataForType(typeof(User));
|
var formatterContext = CreateInputFormatterContext(typeof(User), httpContext);
|
||||||
var context = new InputFormatterContext(
|
|
||||||
httpContext,
|
|
||||||
modelName: string.Empty,
|
|
||||||
modelState: modelState,
|
|
||||||
metadata: metadata,
|
|
||||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await formatter.ReadAsync(context);
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(result.HasError);
|
Assert.True(result.HasError);
|
||||||
Assert.True(!modelState.IsValid);
|
Assert.True(!formatterContext.ModelState.IsValid);
|
||||||
Assert.True(modelState.ContainsKey(modelStateKey));
|
Assert.True(formatterContext.ModelState.ContainsKey(modelStateKey));
|
||||||
|
|
||||||
var modelError = modelState[modelStateKey].Errors.Single();
|
var modelError = formatterContext.ModelState[modelStateKey].Errors.Single();
|
||||||
Assert.Equal(expectedMessage, modelError.ErrorMessage);
|
Assert.Equal(expectedMessage, modelError.ErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ReadAsync_WhenSuppressJsonDeserializationExceptionMessagesIsTrue_DoesNotWrapJsonInputExceptions()
|
public async Task ReadAsync_DefaultOptions_DoesNotWrapJsonInputExceptions()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var logger = GetLogger();
|
|
||||||
var formatter = new JsonInputFormatter(
|
var formatter = new JsonInputFormatter(
|
||||||
logger,
|
GetLogger(),
|
||||||
_serializerSettings,
|
_serializerSettings,
|
||||||
ArrayPool<char>.Shared,
|
ArrayPool<char>.Shared,
|
||||||
_objectPoolProvider,
|
_objectPoolProvider,
|
||||||
new MvcOptions()
|
new MvcOptions(),
|
||||||
{
|
new MvcJsonOptions());
|
||||||
SuppressInputFormatterBuffering = false,
|
|
||||||
SuppressJsonDeserializationExceptionMessagesInModelState = true
|
|
||||||
});
|
|
||||||
var contentBytes = Encoding.UTF8.GetBytes("{");
|
|
||||||
var modelStateKey = string.Empty;
|
|
||||||
|
|
||||||
var modelState = new ModelStateDictionary();
|
var contentBytes = Encoding.UTF8.GetBytes("{");
|
||||||
var httpContext = GetHttpContext(contentBytes);
|
var httpContext = GetHttpContext(contentBytes);
|
||||||
var provider = new EmptyModelMetadataProvider();
|
|
||||||
var metadata = provider.GetMetadataForType(typeof(User));
|
var formatterContext = CreateInputFormatterContext(typeof(User), httpContext);
|
||||||
var context = new InputFormatterContext(
|
|
||||||
httpContext,
|
|
||||||
modelName: string.Empty,
|
|
||||||
modelState: modelState,
|
|
||||||
metadata: metadata,
|
|
||||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await formatter.ReadAsync(context);
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(result.HasError);
|
Assert.True(result.HasError);
|
||||||
Assert.True(!modelState.IsValid);
|
Assert.True(!formatterContext.ModelState.IsValid);
|
||||||
Assert.True(modelState.ContainsKey(modelStateKey));
|
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.IsNotType<InputFormatterException>(modelError.Exception);
|
||||||
Assert.Empty(modelError.ErrorMessage);
|
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
|
private class TestableJsonInputFormatter : JsonInputFormatter
|
||||||
{
|
{
|
||||||
public TestableJsonInputFormatter(JsonSerializerSettings settings)
|
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;
|
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(
|
private static HttpContext GetHttpContext(
|
||||||
byte[] contentBytes,
|
byte[] contentBytes,
|
||||||
string contentType = "application/json")
|
string contentType = "application/json")
|
||||||
|
|
@ -800,6 +715,24 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
return httpContext.Object;
|
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)
|
private IEnumerable<string> GetModelStateErrorMessages(ModelStateDictionary modelStateDictionary)
|
||||||
{
|
{
|
||||||
var allErrorMessages = new List<string>();
|
var allErrorMessages = new List<string>();
|
||||||
|
|
|
||||||
|
|
@ -26,136 +26,128 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
private static readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings();
|
private static readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings();
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task BuffersRequestBody_ByDefault()
|
public async Task Version_2_0_Constructor_BuffersRequestBody_ByDefault()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var logger = GetLogger();
|
|
||||||
#pragma warning disable CS0618
|
#pragma warning disable CS0618
|
||||||
var formatter =
|
var formatter = new JsonPatchInputFormatter(
|
||||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider);
|
GetLogger(),
|
||||||
|
_serializerSettings,
|
||||||
|
ArrayPool<char>.Shared,
|
||||||
|
_objectPoolProvider);
|
||||||
#pragma warning restore CS0618
|
#pragma warning restore CS0618
|
||||||
|
|
||||||
var content = "[{\"op\":\"add\",\"path\":\"Customer/Name\",\"value\":\"John\"}]";
|
var content = "[{\"op\":\"add\",\"path\":\"Customer/Name\",\"value\":\"John\"}]";
|
||||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||||
|
|
||||||
var modelState = new ModelStateDictionary();
|
|
||||||
var httpContext = new DefaultHttpContext();
|
var httpContext = new DefaultHttpContext();
|
||||||
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
|
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
|
||||||
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
|
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
|
||||||
httpContext.Request.ContentType = "application/json";
|
httpContext.Request.ContentType = "application/json";
|
||||||
var provider = new EmptyModelMetadataProvider();
|
|
||||||
var metadata = provider.GetMetadataForType(typeof(JsonPatchDocument<Customer>));
|
var formatterContext = CreateInputFormatterContext(typeof(JsonPatchDocument<Customer>), httpContext);
|
||||||
var context = new InputFormatterContext(
|
|
||||||
httpContext,
|
|
||||||
modelName: string.Empty,
|
|
||||||
modelState: modelState,
|
|
||||||
metadata: metadata,
|
|
||||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await formatter.ReadAsync(context);
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.HasError);
|
Assert.False(result.HasError);
|
||||||
var patchDoc = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
var patchDocument = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
||||||
Assert.Equal("add", patchDoc.Operations[0].op);
|
Assert.Equal("add", patchDocument.Operations[0].op);
|
||||||
Assert.Equal("Customer/Name", patchDoc.Operations[0].path);
|
Assert.Equal("Customer/Name", patchDocument.Operations[0].path);
|
||||||
Assert.Equal("John", patchDoc.Operations[0].value);
|
Assert.Equal("John", patchDocument.Operations[0].value);
|
||||||
|
|
||||||
Assert.True(httpContext.Request.Body.CanSeek);
|
Assert.True(httpContext.Request.Body.CanSeek);
|
||||||
httpContext.Request.Body.Seek(0L, SeekOrigin.Begin);
|
httpContext.Request.Body.Seek(0L, SeekOrigin.Begin);
|
||||||
|
|
||||||
result = await formatter.ReadAsync(context);
|
result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.HasError);
|
Assert.False(result.HasError);
|
||||||
patchDoc = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
patchDocument = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
||||||
Assert.Equal("add", patchDoc.Operations[0].op);
|
Assert.Equal("add", patchDocument.Operations[0].op);
|
||||||
Assert.Equal("Customer/Name", patchDoc.Operations[0].path);
|
Assert.Equal("Customer/Name", patchDocument.Operations[0].path);
|
||||||
Assert.Equal("John", patchDoc.Operations[0].value);
|
Assert.Equal("John", patchDocument.Operations[0].value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task BuffersRequestBody_UsingDefaultOptions()
|
public async Task Version_2_1_Constructor_BuffersRequestBody_ByDefault()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var logger = GetLogger();
|
var formatter = new JsonPatchInputFormatter(
|
||||||
var formatter =
|
GetLogger(),
|
||||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
_serializerSettings,
|
||||||
|
ArrayPool<char>.Shared,
|
||||||
|
_objectPoolProvider,
|
||||||
|
new MvcOptions(),
|
||||||
|
new MvcJsonOptions());
|
||||||
|
|
||||||
var content = "[{\"op\":\"add\",\"path\":\"Customer/Name\",\"value\":\"John\"}]";
|
var content = "[{\"op\":\"add\",\"path\":\"Customer/Name\",\"value\":\"John\"}]";
|
||||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||||
|
|
||||||
var modelState = new ModelStateDictionary();
|
|
||||||
var httpContext = new DefaultHttpContext();
|
var httpContext = new DefaultHttpContext();
|
||||||
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
|
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
|
||||||
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
|
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
|
||||||
httpContext.Request.ContentType = "application/json";
|
httpContext.Request.ContentType = "application/json";
|
||||||
var provider = new EmptyModelMetadataProvider();
|
|
||||||
var metadata = provider.GetMetadataForType(typeof(JsonPatchDocument<Customer>));
|
var formatterContext = CreateInputFormatterContext(typeof(JsonPatchDocument<Customer>), httpContext);
|
||||||
var context = new InputFormatterContext(
|
|
||||||
httpContext,
|
|
||||||
modelName: string.Empty,
|
|
||||||
modelState: modelState,
|
|
||||||
metadata: metadata,
|
|
||||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await formatter.ReadAsync(context);
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.HasError);
|
Assert.False(result.HasError);
|
||||||
var patchDoc = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
var patchDocument = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
||||||
Assert.Equal("add", patchDoc.Operations[0].op);
|
Assert.Equal("add", patchDocument.Operations[0].op);
|
||||||
Assert.Equal("Customer/Name", patchDoc.Operations[0].path);
|
Assert.Equal("Customer/Name", patchDocument.Operations[0].path);
|
||||||
Assert.Equal("John", patchDoc.Operations[0].value);
|
Assert.Equal("John", patchDocument.Operations[0].value);
|
||||||
|
|
||||||
Assert.True(httpContext.Request.Body.CanSeek);
|
Assert.True(httpContext.Request.Body.CanSeek);
|
||||||
httpContext.Request.Body.Seek(0L, SeekOrigin.Begin);
|
httpContext.Request.Body.Seek(0L, SeekOrigin.Begin);
|
||||||
|
|
||||||
result = await formatter.ReadAsync(context);
|
result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.HasError);
|
Assert.False(result.HasError);
|
||||||
patchDoc = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
patchDocument = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
||||||
Assert.Equal("add", patchDoc.Operations[0].op);
|
Assert.Equal("add", patchDocument.Operations[0].op);
|
||||||
Assert.Equal("Customer/Name", patchDoc.Operations[0].path);
|
Assert.Equal("Customer/Name", patchDocument.Operations[0].path);
|
||||||
Assert.Equal("John", patchDoc.Operations[0].value);
|
Assert.Equal("John", patchDocument.Operations[0].value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task SuppressInputFormatterBufferingSetToTrue_DoesNotBufferRequestBody()
|
public async Task Version_2_0_Constructor_SuppressInputFormatterBuffering_DoesNotBufferRequestBody()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var logger = GetLogger();
|
|
||||||
#pragma warning disable CS0618
|
#pragma warning disable CS0618
|
||||||
var formatter =
|
var formatter = new JsonPatchInputFormatter(
|
||||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, suppressInputFormatterBuffering: true);
|
GetLogger(),
|
||||||
|
_serializerSettings,
|
||||||
|
ArrayPool<char>.Shared,
|
||||||
|
_objectPoolProvider,
|
||||||
|
suppressInputFormatterBuffering: true);
|
||||||
#pragma warning restore CS0618
|
#pragma warning restore CS0618
|
||||||
|
|
||||||
var content = "[{\"op\":\"add\",\"path\":\"Customer/Name\",\"value\":\"John\"}]";
|
var content = "[{\"op\":\"add\",\"path\":\"Customer/Name\",\"value\":\"John\"}]";
|
||||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||||
|
|
||||||
var modelState = new ModelStateDictionary();
|
|
||||||
var httpContext = new DefaultHttpContext();
|
var httpContext = new DefaultHttpContext();
|
||||||
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
|
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
|
||||||
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
|
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
|
||||||
httpContext.Request.ContentType = "application/json";
|
httpContext.Request.ContentType = "application/json";
|
||||||
var provider = new EmptyModelMetadataProvider();
|
|
||||||
var metadata = provider.GetMetadataForType(typeof(JsonPatchDocument<Customer>));
|
var context = CreateInputFormatterContext(typeof(JsonPatchDocument<Customer>), httpContext);
|
||||||
var context = new InputFormatterContext(
|
|
||||||
httpContext,
|
|
||||||
modelName: string.Empty,
|
|
||||||
modelState: modelState,
|
|
||||||
metadata: metadata,
|
|
||||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await formatter.ReadAsync(context);
|
var result = await formatter.ReadAsync(context);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.HasError);
|
Assert.False(result.HasError);
|
||||||
var patchDoc = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
|
||||||
Assert.Equal("add", patchDoc.Operations[0].op);
|
var patchDocument = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
||||||
Assert.Equal("Customer/Name", patchDoc.Operations[0].path);
|
Assert.Equal("add", patchDocument.Operations[0].op);
|
||||||
Assert.Equal("John", patchDoc.Operations[0].value);
|
Assert.Equal("Customer/Name", patchDocument.Operations[0].path);
|
||||||
|
Assert.Equal("John", patchDocument.Operations[0].value);
|
||||||
|
|
||||||
Assert.False(httpContext.Request.Body.CanSeek);
|
Assert.False(httpContext.Request.Body.CanSeek);
|
||||||
result = await formatter.ReadAsync(context);
|
result = await formatter.ReadAsync(context);
|
||||||
|
|
@ -166,49 +158,46 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task SuppressInputFormatterBufferingSetToTrue_UsingMutatedOptions_DoesNotBufferRequestBody()
|
public async Task Version_2_1_Constructor_SuppressInputFormatterBuffering_DoesNotBufferRequestBody()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var logger = GetLogger();
|
var mvcOptions = new MvcOptions()
|
||||||
var mvcOptions = new MvcOptions();
|
{
|
||||||
mvcOptions.SuppressInputFormatterBuffering = false;
|
SuppressInputFormatterBuffering = false,
|
||||||
|
};
|
||||||
var formatter = new JsonPatchInputFormatter(
|
var formatter = new JsonPatchInputFormatter(
|
||||||
logger,
|
GetLogger(),
|
||||||
_serializerSettings,
|
_serializerSettings,
|
||||||
ArrayPool<char>.Shared,
|
ArrayPool<char>.Shared,
|
||||||
_objectPoolProvider,
|
_objectPoolProvider,
|
||||||
mvcOptions);
|
mvcOptions,
|
||||||
|
new MvcJsonOptions());
|
||||||
|
|
||||||
var content = "[{\"op\":\"add\",\"path\":\"Customer/Name\",\"value\":\"John\"}]";
|
var content = "[{\"op\":\"add\",\"path\":\"Customer/Name\",\"value\":\"John\"}]";
|
||||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||||
|
|
||||||
var modelState = new ModelStateDictionary();
|
|
||||||
var httpContext = new DefaultHttpContext();
|
var httpContext = new DefaultHttpContext();
|
||||||
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
|
httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
|
||||||
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
|
httpContext.Request.Body = new NonSeekableReadStream(contentBytes);
|
||||||
httpContext.Request.ContentType = "application/json";
|
httpContext.Request.ContentType = "application/json";
|
||||||
var provider = new EmptyModelMetadataProvider();
|
|
||||||
var metadata = provider.GetMetadataForType(typeof(JsonPatchDocument<Customer>));
|
var formatterContext = CreateInputFormatterContext(typeof(JsonPatchDocument<Customer>), httpContext);
|
||||||
var context = new InputFormatterContext(
|
|
||||||
httpContext,
|
|
||||||
modelName: string.Empty,
|
|
||||||
modelState: modelState,
|
|
||||||
metadata: metadata,
|
|
||||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
// Mutate options after passing into the constructor to make sure that the value type is not store in the constructor
|
// Mutate options after passing into the constructor to make sure that the value type is not store in the constructor
|
||||||
mvcOptions.SuppressInputFormatterBuffering = true;
|
mvcOptions.SuppressInputFormatterBuffering = true;
|
||||||
var result = await formatter.ReadAsync(context);
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.HasError);
|
Assert.False(result.HasError);
|
||||||
var patchDoc = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
|
||||||
Assert.Equal("add", patchDoc.Operations[0].op);
|
var patchDocument = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
||||||
Assert.Equal("Customer/Name", patchDoc.Operations[0].path);
|
Assert.Equal("add", patchDocument.Operations[0].op);
|
||||||
Assert.Equal("John", patchDoc.Operations[0].value);
|
Assert.Equal("Customer/Name", patchDocument.Operations[0].path);
|
||||||
|
Assert.Equal("John", patchDocument.Operations[0].value);
|
||||||
|
|
||||||
Assert.False(httpContext.Request.Body.CanSeek);
|
Assert.False(httpContext.Request.Body.CanSeek);
|
||||||
result = await formatter.ReadAsync(context);
|
result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.HasError);
|
Assert.False(result.HasError);
|
||||||
|
|
@ -219,67 +208,49 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
public async Task JsonPatchInputFormatter_ReadsOneOperation_Successfully()
|
public async Task JsonPatchInputFormatter_ReadsOneOperation_Successfully()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var logger = GetLogger();
|
var formatter = CreateFormatter();
|
||||||
var formatter =
|
|
||||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
|
||||||
var content = "[{\"op\":\"add\",\"path\":\"Customer/Name\",\"value\":\"John\"}]";
|
var content = "[{\"op\":\"add\",\"path\":\"Customer/Name\",\"value\":\"John\"}]";
|
||||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||||
|
var httpContext = CreateHttpContext(contentBytes);
|
||||||
|
|
||||||
var modelState = new ModelStateDictionary();
|
var formatterContext = CreateInputFormatterContext(typeof(JsonPatchDocument<Customer>), httpContext);
|
||||||
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);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await formatter.ReadAsync(context);
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.HasError);
|
Assert.False(result.HasError);
|
||||||
var patchDoc = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
var patchDocument = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
||||||
Assert.Equal("add", patchDoc.Operations[0].op);
|
Assert.Equal("add", patchDocument.Operations[0].op);
|
||||||
Assert.Equal("Customer/Name", patchDoc.Operations[0].path);
|
Assert.Equal("Customer/Name", patchDocument.Operations[0].path);
|
||||||
Assert.Equal("John", patchDoc.Operations[0].value);
|
Assert.Equal("John", patchDocument.Operations[0].value);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task JsonPatchInputFormatter_ReadsMultipleOperations_Successfully()
|
public async Task JsonPatchInputFormatter_ReadsMultipleOperations_Successfully()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var logger = GetLogger();
|
var formatter = CreateFormatter();
|
||||||
var formatter =
|
|
||||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
|
||||||
var content = "[{\"op\": \"add\", \"path\" : \"Customer/Name\", \"value\":\"John\"}," +
|
var content = "[{\"op\": \"add\", \"path\" : \"Customer/Name\", \"value\":\"John\"}," +
|
||||||
"{\"op\": \"remove\", \"path\" : \"Customer/Name\"}]";
|
"{\"op\": \"remove\", \"path\" : \"Customer/Name\"}]";
|
||||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||||
|
var httpContext = CreateHttpContext(contentBytes);
|
||||||
|
|
||||||
var modelState = new ModelStateDictionary();
|
var formatterContext = CreateInputFormatterContext(typeof(JsonPatchDocument<Customer>), httpContext);
|
||||||
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);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await formatter.ReadAsync(context);
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(result.HasError);
|
Assert.False(result.HasError);
|
||||||
var patchDoc = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
var patchDocument = Assert.IsType<JsonPatchDocument<Customer>>(result.Model);
|
||||||
Assert.Equal("add", patchDoc.Operations[0].op);
|
Assert.Equal("add", patchDocument.Operations[0].op);
|
||||||
Assert.Equal("Customer/Name", patchDoc.Operations[0].path);
|
Assert.Equal("Customer/Name", patchDocument.Operations[0].path);
|
||||||
Assert.Equal("John", patchDoc.Operations[0].value);
|
Assert.Equal("John", patchDocument.Operations[0].value);
|
||||||
Assert.Equal("remove", patchDoc.Operations[1].op);
|
Assert.Equal("remove", patchDocument.Operations[1].op);
|
||||||
Assert.Equal("Customer/Name", patchDoc.Operations[1].path);
|
Assert.Equal("Customer/Name", patchDocument.Operations[1].path);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
@ -290,22 +261,13 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
public void CanRead_ReturnsTrueOnlyForJsonPatchContentType(string requestContentType, bool expectedCanRead)
|
public void CanRead_ReturnsTrueOnlyForJsonPatchContentType(string requestContentType, bool expectedCanRead)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var logger = GetLogger();
|
var formatter = CreateFormatter();
|
||||||
var formatter =
|
|
||||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
|
||||||
var content = "[{\"op\": \"add\", \"path\" : \"Customer/Name\", \"value\":\"John\"}]";
|
var content = "[{\"op\": \"add\", \"path\" : \"Customer/Name\", \"value\":\"John\"}]";
|
||||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||||
|
var httpContext = CreateHttpContext(contentBytes, contentType: requestContentType);
|
||||||
|
|
||||||
var modelState = new ModelStateDictionary();
|
var formatterContext = CreateInputFormatterContext(typeof(JsonPatchDocument<Customer>), httpContext);
|
||||||
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);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = formatter.CanRead(formatterContext);
|
var result = formatter.CanRead(formatterContext);
|
||||||
|
|
@ -320,22 +282,15 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
public void CanRead_ReturnsFalse_NonJsonPatchContentType(Type modelType)
|
public void CanRead_ReturnsFalse_NonJsonPatchContentType(Type modelType)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var logger = GetLogger();
|
var formatter = CreateFormatter();
|
||||||
var formatter =
|
|
||||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
|
||||||
var content = "[{\"op\": \"add\", \"path\" : \"Customer/Name\", \"value\":\"John\"}]";
|
var content = "[{\"op\": \"add\", \"path\" : \"Customer/Name\", \"value\":\"John\"}]";
|
||||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
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 provider = new EmptyModelMetadataProvider();
|
||||||
var metadata = provider.GetMetadataForType(modelType);
|
var metadata = provider.GetMetadataForType(modelType);
|
||||||
var formatterContext = new InputFormatterContext(
|
var formatterContext = CreateInputFormatterContext(modelType, httpContext);
|
||||||
httpContext,
|
|
||||||
modelName: string.Empty,
|
|
||||||
modelState: modelState,
|
|
||||||
metadata: metadata,
|
|
||||||
readerFactory: new TestHttpRequestStreamReaderFactory().CreateReader);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = formatter.CanRead(formatterContext);
|
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 " +
|
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 ";
|
$"'{typeof(Customer).FullName}' because the type requires a JSON object ";
|
||||||
|
|
||||||
var logger = GetLogger();
|
// This test relies on 2.1 error message behavior
|
||||||
var formatter =
|
var formatter = CreateFormatter(allowInputFormatterExceptionMessages: true);
|
||||||
new JsonPatchInputFormatter(logger, _serializerSettings, ArrayPool<char>.Shared, _objectPoolProvider, new MvcOptions());
|
|
||||||
var content = "[{\"op\": \"add\", \"path\" : \"Customer/Name\", \"value\":\"John\"}]";
|
var content = "[{\"op\": \"add\", \"path\" : \"Customer/Name\", \"value\":\"John\"}]";
|
||||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||||
|
var httpContext = CreateHttpContext(contentBytes, contentType: "application/json-patch+json");
|
||||||
|
|
||||||
var modelState = new ModelStateDictionary();
|
var formatterContext = CreateInputFormatterContext(typeof(Customer), httpContext);
|
||||||
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);
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = await formatter.ReadAsync(context);
|
var result = await formatter.ReadAsync(formatterContext);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(result.HasError);
|
Assert.True(result.HasError);
|
||||||
Assert.Contains(exceptionMessage, modelState[""].Errors[0].ErrorMessage);
|
Assert.Contains(exceptionMessage, formatterContext.ModelState[""].Errors[0].ErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ILogger GetLogger()
|
private static ILogger GetLogger()
|
||||||
|
|
@ -381,7 +328,34 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
||||||
return NullLogger.Instance;
|
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,
|
byte[] contentBytes,
|
||||||
string contentType = "application/json-patch+json")
|
string contentType = "application/json-patch+json")
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
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()
|
public async Task JsonInputFormatter_SuppliedJsonDeserializationErrorMessage()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
@ -109,7 +109,9 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
|
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]
|
[Theory]
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
||||||
|
|
@ -448,7 +449,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
||||||
public int Address { get; set; }
|
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()
|
public async Task FromBodyAndRequiredOnValueTypeProperty_EmptyBody_JsonFormatterAddsModelStateError()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
@ -485,11 +486,10 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
||||||
Assert.Null(entry.Value.AttemptedValue);
|
Assert.Null(entry.Value.AttemptedValue);
|
||||||
Assert.Null(entry.Value.RawValue);
|
Assert.Null(entry.Value.RawValue);
|
||||||
var error = Assert.Single(entry.Value.Errors);
|
var error = Assert.Single(entry.Value.Errors);
|
||||||
Assert.Null(error.Exception);
|
|
||||||
|
// Update me in 3.0 when MvcJsonOptions.AllowInputFormatterExceptionMessages is removed
|
||||||
// Json.NET currently throws an exception starting with "No JSON content found and type 'System.Int32' is
|
Assert.IsType<JsonSerializationException>(error.Exception);
|
||||||
// not nullable." but do not tie test to a particular Json.NET build.
|
Assert.Empty(error.ErrorMessage);
|
||||||
Assert.NotEmpty(error.ErrorMessage);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class Person5
|
private class Person5
|
||||||
|
|
@ -545,7 +545,7 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
||||||
Assert.Empty(modelState);
|
Assert.Empty(modelState);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact] // This test covers the 2.0 behavior. Error messages from JSON.Net are preserved.
|
||||||
public async Task FromBodyWithInvalidPropertyData_JsonFormatterAddsModelError()
|
public async Task FromBodyWithInvalidPropertyData_JsonFormatterAddsModelError()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
@ -586,18 +586,18 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTests
|
||||||
Assert.Null(state.AttemptedValue);
|
Assert.Null(state.AttemptedValue);
|
||||||
Assert.Null(state.RawValue);
|
Assert.Null(state.RawValue);
|
||||||
var error = Assert.Single(state.Errors);
|
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
|
// Update me in 3.0 when MvcJsonOptions.AllowInputFormatterExceptionMessages is removed
|
||||||
// integer: not a number." but do not tie test to a particular Json.NET build.
|
Assert.IsType<JsonReaderException>(error.Exception);
|
||||||
Assert.NotEmpty(error.ErrorMessage);
|
Assert.Empty(error.ErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(false, false)]
|
[InlineData(false, false)]
|
||||||
[InlineData(true, true)]
|
[InlineData(true, true)]
|
||||||
public async Task FromBodyWithEmptyBody_JsonFormatterAddsModelErrorWhenExpected(
|
public async Task FromBodyWithEmptyBody_JsonFormatterAddsModelErrorWhenExpected(
|
||||||
bool allowEmptyInputInBodyModelBindingSetting, bool expectedModelStateIsValid)
|
bool allowEmptyInputInBodyModelBindingSetting,
|
||||||
|
bool expectedModelStateIsValid)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var parameter = new ParameterDescriptor
|
var parameter = new ParameterDescriptor
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,12 @@ namespace Microsoft.AspNetCore.Mvc.IntegrationTest
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var mvcOptions = services.GetRequiredService<IOptions<MvcOptions>>().Value;
|
var mvcOptions = services.GetRequiredService<IOptions<MvcOptions>>().Value;
|
||||||
|
var jsonOptions = services.GetRequiredService<IOptions<MvcJsonOptions>>().Value;
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.False(mvcOptions.SuppressBindingUndefinedValueToEnumType);
|
Assert.False(mvcOptions.SuppressBindingUndefinedValueToEnumType);
|
||||||
Assert.Equal(InputFormatterExceptionPolicy.AllExceptions, mvcOptions.InputFormatterExceptionPolicy);
|
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")]
|
[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
|
// Act
|
||||||
var mvcOptions = services.GetRequiredService<IOptions<MvcOptions>>().Value;
|
var mvcOptions = services.GetRequiredService<IOptions<MvcOptions>>().Value;
|
||||||
|
var jsonOptions = services.GetRequiredService<IOptions<MvcJsonOptions>>().Value;
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(mvcOptions.SuppressBindingUndefinedValueToEnumType);
|
Assert.True(mvcOptions.SuppressBindingUndefinedValueToEnumType);
|
||||||
Assert.Equal(InputFormatterExceptionPolicy.MalformedInputExceptions, mvcOptions.InputFormatterExceptionPolicy);
|
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")]
|
[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
|
// Act
|
||||||
var mvcOptions = services.GetRequiredService<IOptions<MvcOptions>>().Value;
|
var mvcOptions = services.GetRequiredService<IOptions<MvcOptions>>().Value;
|
||||||
|
var jsonOptions = services.GetRequiredService<IOptions<MvcJsonOptions>>().Value;
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.True(mvcOptions.SuppressBindingUndefinedValueToEnumType);
|
Assert.True(mvcOptions.SuppressBindingUndefinedValueToEnumType);
|
||||||
Assert.Equal(InputFormatterExceptionPolicy.MalformedInputExceptions, mvcOptions.InputFormatterExceptionPolicy);
|
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.
|
// 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(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),
|
typeof(IActionConstraintProvider),
|
||||||
new Type[]
|
new Type[]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue