Make JSON case-insensitive (#10727)

* Make JSON case-insensitive

Fixes: #10724

The rationale for this change is that existing .NET client code for the
most part uses JSON.NET with its default settings (preserve property
casing). This includes the WebAPI client - which we're encouraging
everyone to use. It's not really reasonable for us to break everyone
using webapi client.

* Make separate options and add extension method

* fixit

* fix build

* fix text
This commit is contained in:
Ryan Nowak 2019-06-03 09:16:58 -07:00 committed by GitHub
parent 1ed5febf93
commit 670865d2a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 174 additions and 60 deletions

View File

@ -814,6 +814,11 @@ namespace Microsoft.AspNetCore.Mvc
public partial interface IRequestSizePolicy : Microsoft.AspNetCore.Mvc.Filters.IFilterMetadata
{
}
public partial class JsonOptions
{
public JsonOptions() { }
public System.Text.Json.Serialization.JsonSerializerOptions JsonSerializerOptions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
}
public partial class JsonResult : Microsoft.AspNetCore.Mvc.ActionResult, Microsoft.AspNetCore.Mvc.IActionResult, Microsoft.AspNetCore.Mvc.Infrastructure.IStatusCodeActionResult
{
public JsonResult(object value) { }
@ -881,7 +886,6 @@ namespace Microsoft.AspNetCore.Mvc
public bool RequireHttpsPermanent { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public bool RespectBrowserAcceptHeader { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public bool ReturnHttpNotAcceptable { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public System.Text.Json.Serialization.JsonSerializerOptions SerializerOptions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
public int? SslPort { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public bool SuppressAsyncSuffixInActionNames { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public bool SuppressImplicitRequiredAttributeForNonNullableReferenceTypes { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
@ -1872,7 +1876,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
}
public partial class SystemTextJsonInputFormatter : Microsoft.AspNetCore.Mvc.Formatters.TextInputFormatter, Microsoft.AspNetCore.Mvc.Formatters.IInputFormatterExceptionPolicy
{
public SystemTextJsonInputFormatter(Microsoft.AspNetCore.Mvc.MvcOptions options) { }
public SystemTextJsonInputFormatter(Microsoft.AspNetCore.Mvc.JsonOptions options) { }
Microsoft.AspNetCore.Mvc.Formatters.InputFormatterExceptionPolicy Microsoft.AspNetCore.Mvc.Formatters.IInputFormatterExceptionPolicy.ExceptionPolicy { get { throw null; } }
public System.Text.Json.Serialization.JsonSerializerOptions SerializerOptions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
[System.Diagnostics.DebuggerStepThroughAttribute]
@ -1880,7 +1884,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
}
public partial class SystemTextJsonOutputFormatter : Microsoft.AspNetCore.Mvc.Formatters.TextOutputFormatter
{
public SystemTextJsonOutputFormatter(Microsoft.AspNetCore.Mvc.MvcOptions options) { }
public SystemTextJsonOutputFormatter(Microsoft.AspNetCore.Mvc.JsonOptions options) { }
public System.Text.Json.Serialization.JsonSerializerOptions SerializerOptions { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
[System.Diagnostics.DebuggerStepThroughAttribute]
public sealed override System.Threading.Tasks.Task WriteResponseBodyAsync(Microsoft.AspNetCore.Mvc.Formatters.OutputFormatterWriteContext context, System.Text.Encoding selectedEncoding) { throw null; }
@ -3026,6 +3030,7 @@ namespace Microsoft.Extensions.DependencyInjection
public static Microsoft.Extensions.DependencyInjection.IMvcBuilder AddApplicationPart(this Microsoft.Extensions.DependencyInjection.IMvcBuilder builder, System.Reflection.Assembly assembly) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IMvcBuilder AddControllersAsServices(this Microsoft.Extensions.DependencyInjection.IMvcBuilder builder) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IMvcBuilder AddFormatterMappings(this Microsoft.Extensions.DependencyInjection.IMvcBuilder builder, System.Action<Microsoft.AspNetCore.Mvc.Formatters.FormatterMappings> setupAction) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IMvcBuilder AddJsonOptions(this Microsoft.Extensions.DependencyInjection.IMvcBuilder builder, System.Action<Microsoft.AspNetCore.Mvc.JsonOptions> configure) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IMvcBuilder AddMvcOptions(this Microsoft.Extensions.DependencyInjection.IMvcBuilder builder, System.Action<Microsoft.AspNetCore.Mvc.MvcOptions> setupAction) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IMvcBuilder ConfigureApiBehaviorOptions(this Microsoft.Extensions.DependencyInjection.IMvcBuilder builder, System.Action<Microsoft.AspNetCore.Mvc.ApiBehaviorOptions> setupAction) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IMvcBuilder ConfigureApplicationPartManager(this Microsoft.Extensions.DependencyInjection.IMvcBuilder builder, System.Action<Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartManager> setupAction) { throw null; }
@ -3039,6 +3044,7 @@ namespace Microsoft.Extensions.DependencyInjection
public static Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder AddControllersAsServices(this Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder builder) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder AddFormatterMappings(this Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder builder) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder AddFormatterMappings(this Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder builder, System.Action<Microsoft.AspNetCore.Mvc.Formatters.FormatterMappings> setupAction) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder AddJsonOptions(this Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder builder, System.Action<Microsoft.AspNetCore.Mvc.JsonOptions> configure) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder AddMvcOptions(this Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder builder, System.Action<Microsoft.AspNetCore.Mvc.MvcOptions> setupAction) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder ConfigureApiBehaviorOptions(this Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder builder, System.Action<Microsoft.AspNetCore.Mvc.ApiBehaviorOptions> setupAction) { throw null; }
public static Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder ConfigureApplicationPartManager(this Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder builder, System.Action<Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartManager> setupAction) { throw null; }

View File

@ -42,6 +42,30 @@ namespace Microsoft.Extensions.DependencyInjection
return builder;
}
/// <summary>
/// Configures <see cref="JsonOptions"/> for the specified <paramref name="builder"/>.
/// </summary>
/// <param name="builder">The <see cref="IMvcBuilder"/>.</param>
/// <param name="configure">An <see cref="Action"/> to configure the <see cref="JsonOptions"/>.</param>
/// <returns>The <see cref="IMvcBuilder"/>.</returns>
public static IMvcBuilder AddJsonOptions(
this IMvcBuilder builder,
Action<JsonOptions> configure)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}
builder.Services.Configure(configure);
return builder;
}
/// <summary>
/// Configures <see cref="FormatterMappings"/> for the specified <paramref name="builder"/>.
/// </summary>

View File

@ -44,6 +44,30 @@ namespace Microsoft.Extensions.DependencyInjection
return builder;
}
/// <summary>
/// Configures <see cref="JsonOptions"/> for the specified <paramref name="builder"/>.
/// </summary>
/// <param name="builder">The <see cref="IMvcBuilder"/>.</param>
/// <param name="configure">An <see cref="Action"/> to configure the <see cref="JsonOptions"/>.</param>
/// <returns>The <see cref="IMvcBuilder"/>.</returns>
public static IMvcCoreBuilder AddJsonOptions(
this IMvcCoreBuilder builder,
Action<JsonOptions> configure)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}
builder.Services.Configure(configure);
return builder;
}
/// <summary>
/// Adds services to support <see cref="FormatterMappings"/>.
/// </summary>

View File

@ -19,10 +19,10 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
/// <summary>
/// Initializes a new instance of <see cref="SystemTextJsonInputFormatter"/>.
/// </summary>
/// <param name="options">The <see cref="MvcOptions"/>.</param>
public SystemTextJsonInputFormatter(MvcOptions options)
/// <param name="options">The <see cref="JsonOptions"/>.</param>
public SystemTextJsonInputFormatter(JsonOptions options)
{
SerializerOptions = options.SerializerOptions;
SerializerOptions = options.JsonSerializerOptions;
SupportedEncodings.Add(UTF8EncodingWithoutBOM);
SupportedEncodings.Add(UTF16EncodingLittleEndian);

View File

@ -20,10 +20,10 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
/// <summary>
/// Initializes a new <see cref="SystemTextJsonOutputFormatter"/> instance.
/// </summary>
/// <param name="options">The <see cref="MvcOptions"/>.</param>
public SystemTextJsonOutputFormatter(MvcOptions options)
/// <param name="options">The <see cref="JsonOptions"/>.</param>
public SystemTextJsonOutputFormatter(JsonOptions options)
{
SerializerOptions = options.SerializerOptions;
SerializerOptions = options.JsonSerializerOptions;
SupportedEncodings.Add(Encoding.UTF8);
SupportedEncodings.Add(Encoding.Unicode);

View File

@ -25,21 +25,33 @@ namespace Microsoft.AspNetCore.Mvc
{
private readonly IHttpRequestStreamReaderFactory _readerFactory;
private readonly ILoggerFactory _loggerFactory;
private readonly IOptions<JsonOptions> _jsonOptions;
public MvcCoreMvcOptionsSetup(IHttpRequestStreamReaderFactory readerFactory)
: this(readerFactory, NullLoggerFactory.Instance)
: this(readerFactory, NullLoggerFactory.Instance, Options.Create(new JsonOptions()))
{
}
public MvcCoreMvcOptionsSetup(IHttpRequestStreamReaderFactory readerFactory, ILoggerFactory loggerFactory)
public MvcCoreMvcOptionsSetup(IHttpRequestStreamReaderFactory readerFactory, ILoggerFactory loggerFactory, IOptions<JsonOptions> jsonOptions)
{
if (readerFactory == null)
{
throw new ArgumentNullException(nameof(readerFactory));
}
if (loggerFactory == null)
{
throw new ArgumentNullException(nameof(loggerFactory));
}
if (jsonOptions == null)
{
throw new ArgumentNullException(nameof(jsonOptions));
}
_readerFactory = readerFactory;
_loggerFactory = loggerFactory;
_jsonOptions = jsonOptions;
}
public void Configure(MvcOptions options)
@ -66,13 +78,13 @@ namespace Microsoft.AspNetCore.Mvc
options.Filters.Add(new UnsupportedContentTypeFilter());
// Set up default input formatters.
options.InputFormatters.Add(new SystemTextJsonInputFormatter(options));
options.InputFormatters.Add(new SystemTextJsonInputFormatter(_jsonOptions.Value));
// Set up default output formatters.
options.OutputFormatters.Add(new HttpNoContentOutputFormatter());
options.OutputFormatters.Add(new StringOutputFormatter());
options.OutputFormatters.Add(new StreamOutputFormatter());
options.OutputFormatters.Add(new SystemTextJsonOutputFormatter(options));
options.OutputFormatters.Add(new SystemTextJsonOutputFormatter(_jsonOptions.Value));
// Set up ValueProviders
options.ValueProviderFactories.Add(new FormValueProviderFactory());

View File

@ -23,14 +23,14 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
Encoding = Encoding.UTF8
}.ToString();
private readonly MvcOptions _mvcOptions;
private readonly JsonOptions _options;
private readonly ILogger<SystemTextJsonResultExecutor> _logger;
public SystemTextJsonResultExecutor(
IOptions<MvcOptions> mvcOptions,
IOptions<JsonOptions> options,
ILogger<SystemTextJsonResultExecutor> logger)
{
_mvcOptions = mvcOptions.Value;
_options = options.Value;
_logger = logger;
}
@ -100,7 +100,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
var serializerSettings = result.SerializerSettings;
if (serializerSettings == null)
{
return _mvcOptions.SerializerOptions;
return _options.JsonSerializerOptions;
}
else
{

View File

@ -0,0 +1,31 @@
// 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.Text.Json.Serialization;
using Microsoft.AspNetCore.Mvc.Formatters;
namespace Microsoft.AspNetCore.Mvc
{
public class JsonOptions
{
/// <summary>
/// Gets the <see cref="System.Text.Json.Serialization.JsonSerializerOptions"/> used by <see cref="SystemTextJsonInputFormatter"/> and
/// <see cref="SystemTextJsonOutputFormatter"/>.
/// </summary>
public JsonSerializerOptions JsonSerializerOptions { get; } = new JsonSerializerOptions
{
// Limit the object graph we'll consume to a fixed depth. This prevents stackoverflow exceptions
// from deserialization errors that might occur from deeply nested objects.
// This value is the same for model binding and Json.Net's serialization.
MaxDepth = MvcOptions.DefaultMaxModelBindingRecursionDepth,
// We're using case-insensitive because there's a TON of code that there that does uses JSON.NET's default
// settings (preserve case) - including the WebAPIClient. This worked when we were using JSON.NET + camel casing
// because JSON.NET is case-insensitive by default.
PropertyNameCaseInsensitive = true,
// Use camel casing for properties
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};
}
}

View File

@ -5,8 +5,6 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
@ -105,18 +103,18 @@ namespace Microsoft.AspNetCore.Mvc
/// <summary>
/// Gets or sets a value that detemines if the inference of <see cref="RequiredAttribute"/> for
/// for properties and parameters of non-nullable reference types is suppressed. If <c>false</c>
/// (the default), then all non-nullable reference types will behave as-if <c>[Required]</c> has
/// been applied. If <c>true</c>, this behavior will be suppressed; nullable reference types and
/// for properties and parameters of non-nullable reference types is suppressed. If <c>false</c>
/// (the default), then all non-nullable reference types will behave as-if <c>[Required]</c> has
/// been applied. If <c>true</c>, this behavior will be suppressed; nullable reference types and
/// non-nullable reference types will behave the same for the purposes of validation.
/// </summary>
/// <remarks>
/// <para>
/// This option controls whether MVC model binding and validation treats nullable and non-nullable
/// reference types differently.
/// This option controls whether MVC model binding and validation treats nullable and non-nullable
/// reference types differently.
/// </para>
/// <para>
/// By default, MVC will treat a non-nullable reference type parameters and properties as-if
/// By default, MVC will treat a non-nullable reference type parameters and properties as-if
/// <c>[Required]</c> has been applied, resulting in validation errors when no value was bound.
/// </para>
/// <para>
@ -361,21 +359,6 @@ namespace Microsoft.AspNetCore.Mvc
}
}
/// <summary>
/// Gets the <see cref="JsonSerializerOptions"/> used by <see cref="SystemTextJsonInputFormatter"/> and
/// <see cref="SystemTextJsonOutputFormatter"/>.
/// </summary>
public JsonSerializerOptions SerializerOptions { get; } = new JsonSerializerOptions
{
// Limit the object graph we'll consume to a fixed depth. This prevents stackoverflow exceptions
// from deserialization errors that might occur from deeply nested objects.
// This value is the same for model binding and Json.Net's serialization.
MaxDepth = DefaultMaxModelBindingRecursionDepth,
// Use camel casing for properties
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
};
IEnumerator<ICompatibilitySwitch> IEnumerable<ICompatibilitySwitch>.GetEnumerator() => _switches.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => _switches.GetEnumerator();

View File

@ -91,7 +91,7 @@ namespace Microsoft.AspNetCore.Mvc
{
var options = Options.Create(new MvcOptions());
options.Value.OutputFormatters.Add(new StringOutputFormatter());
options.Value.OutputFormatters.Add(new SystemTextJsonOutputFormatter(new MvcOptions()));
options.Value.OutputFormatters.Add(new SystemTextJsonOutputFormatter(new JsonOptions()));
var services = new ServiceCollection();
services.AddSingleton<IActionResultExecutor<ObjectResult>>(new ObjectResultExecutor(

View File

@ -104,7 +104,7 @@ namespace Microsoft.AspNetCore.Mvc
{
var options = Options.Create(new MvcOptions());
options.Value.OutputFormatters.Add(new StringOutputFormatter());
options.Value.OutputFormatters.Add(new SystemTextJsonOutputFormatter(new MvcOptions()));
options.Value.OutputFormatters.Add(new SystemTextJsonOutputFormatter(new JsonOptions()));
var services = new ServiceCollection();
services.AddSingleton<IActionResultExecutor<ObjectResult>>(new ObjectResultExecutor(

View File

@ -92,7 +92,7 @@ namespace Microsoft.AspNetCore.Mvc
{
var options = Options.Create(new MvcOptions());
options.Value.OutputFormatters.Add(new StringOutputFormatter());
options.Value.OutputFormatters.Add(new SystemTextJsonOutputFormatter(new MvcOptions()));
options.Value.OutputFormatters.Add(new SystemTextJsonOutputFormatter(new JsonOptions()));
var services = new ServiceCollection();
services.AddSingleton<IActionResultExecutor<ObjectResult>>(new ObjectResultExecutor(

View File

@ -465,7 +465,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
// Set up default output formatters.
MvcOptions.OutputFormatters.Add(new HttpNoContentOutputFormatter());
MvcOptions.OutputFormatters.Add(new StringOutputFormatter());
MvcOptions.OutputFormatters.Add(new SystemTextJsonOutputFormatter(new MvcOptions()));
MvcOptions.OutputFormatters.Add(new SystemTextJsonOutputFormatter(new JsonOptions()));
// Set up default mapping for json extensions to content type
MvcOptions.FormatterMappings.SetMediaTypeMappingForFormat(

View File

@ -34,7 +34,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
protected override TextInputFormatter GetInputFormatter()
{
return new SystemTextJsonInputFormatter(new MvcOptions());
return new SystemTextJsonInputFormatter(new JsonOptions());
}
}
}

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
{
protected override TextOutputFormatter GetOutputFormatter()
{
return new SystemTextJsonOutputFormatter(new MvcOptions());
return new SystemTextJsonOutputFormatter(new JsonOptions());
}
[Theory]

View File

@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Mvc
{
var options = Options.Create(new MvcOptions());
options.Value.OutputFormatters.Add(new StringOutputFormatter());
options.Value.OutputFormatters.Add(new SystemTextJsonOutputFormatter(new MvcOptions()));
options.Value.OutputFormatters.Add(new SystemTextJsonOutputFormatter(new JsonOptions()));
var services = new ServiceCollection();
services.AddSingleton<IActionResultExecutor<ObjectResult>>(new ObjectResultExecutor(

View File

@ -70,7 +70,7 @@ namespace Microsoft.AspNetCore.Mvc
{
var options = Options.Create(new MvcOptions());
options.Value.OutputFormatters.Add(new StringOutputFormatter());
options.Value.OutputFormatters.Add(new SystemTextJsonOutputFormatter(new MvcOptions()));
options.Value.OutputFormatters.Add(new SystemTextJsonOutputFormatter(new JsonOptions()));
var services = new ServiceCollection();
services.AddSingleton<IActionResultExecutor<ObjectResult>>(new ObjectResultExecutor(

View File

@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Mvc.Infrastructure
{
protected override IActionResultExecutor<JsonResult> CreateExecutor(ILoggerFactory loggerFactory)
{
return new SystemTextJsonResultExecutor(Options.Create(new MvcOptions()), loggerFactory.CreateLogger<SystemTextJsonResultExecutor>());
return new SystemTextJsonResultExecutor(Options.Create(new JsonOptions()), loggerFactory.CreateLogger<SystemTextJsonResultExecutor>());
}
protected override object GetIndentedSettings()

View File

@ -10,11 +10,11 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
{
internal class SystemTextJsonHelper : IJsonHelper
{
private readonly MvcOptions _mvcOptions;
private readonly JsonOptions _options;
public SystemTextJsonHelper(IOptions<MvcOptions> mvcOptions)
public SystemTextJsonHelper(IOptions<JsonOptions> options)
{
_mvcOptions = mvcOptions.Value;
_options = options.Value;
}
/// <inheritdoc />
@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
{
// JsonSerializer always encodes non-ASCII chars, so we do not need
// to do anything special with the SerializerOptions
var json = JsonSerializer.ToString(value, _mvcOptions.SerializerOptions);
var json = JsonSerializer.ToString(value, _options.JsonSerializerOptions);
return new HtmlString(json);
}
}

View File

@ -1,8 +1,6 @@

// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. All rights reserved
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Text.Json.Serialization;
using Microsoft.Extensions.Options;
@ -12,8 +10,8 @@ namespace Microsoft.AspNetCore.Mvc.Rendering
{
protected override IJsonHelper GetJsonHelper()
{
var mvcOptions = new MvcOptions { SerializerOptions = { PropertyNamingPolicy = JsonNamingPolicy.CamelCase } };
return new SystemTextJsonHelper(Options.Create(mvcOptions));
var options = new JsonOptions() { JsonSerializerOptions = { PropertyNamingPolicy = JsonNamingPolicy.CamelCase } };
return new SystemTextJsonHelper(Options.Create(options));
}
}
}

View File

@ -8,6 +8,7 @@ using System.Net.Http;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using FormatterWebSite.Controllers;
using Microsoft.AspNetCore.Hosting;
using Xunit;
@ -98,6 +99,28 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
Assert.Equal(expected, responseBody);
}
[Fact]
public async Task JsonInputFormatter_RoundtripsPocoModel()
{
// Arrange
var expected = new JsonFormatterController.SimpleModel()
{
Id = 18,
Name = "James",
StreetName = "JnK",
};
// Act
var response = await Client.PostAsJsonAsync("http://localhost/JsonFormatter/RoundtripSimpleModel/", expected);
var actual = await response.Content.ReadAsAsync<JsonFormatterController.SimpleModel>();
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(expected.Id, actual.Id);
Assert.Equal(expected.Name, actual.Name);
Assert.Equal(expected.StreetName, actual.StreetName);
}
[Fact]
public async Task JsonInputFormatter_Returns415UnsupportedMediaType_ForEmptyContentType()
{

View File

@ -13,8 +13,6 @@ namespace BasicWebSite
// Set up application services
public void ConfigureServices(IServiceCollection services)
{
services.AddRouting();
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Latest)
.AddNewtonsoftJson()

View File

@ -64,5 +64,20 @@ namespace FormatterWebSite.Controllers
return Content(value.ToString());
}
[HttpPost]
public ActionResult<SimpleModel> RoundtripSimpleModel([FromBody] SimpleModel model)
{
return model;
}
public class SimpleModel
{
public int Id { get; set; }
public string Name { get; set; }
public string StreetName { get; set; }
}
}
}