diff --git a/src/Mvc/Mvc.Core/ref/Microsoft.AspNetCore.Mvc.Core.netcoreapp3.0.cs b/src/Mvc/Mvc.Core/ref/Microsoft.AspNetCore.Mvc.Core.netcoreapp3.0.cs index f55eeb1ca2c8..de22f01d28aa 100644 --- a/src/Mvc/Mvc.Core/ref/Microsoft.AspNetCore.Mvc.Core.netcoreapp3.0.cs +++ b/src/Mvc/Mvc.Core/ref/Microsoft.AspNetCore.Mvc.Core.netcoreapp3.0.cs @@ -814,6 +814,11 @@ public partial interface IRequestFormLimitsPolicy : Microsoft.AspNetCore.Mvc.Fil 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 @@ public MvcOptions() { } 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 @@ public StringOutputFormatter() { } } 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 @@ public SystemTextJsonInputFormatter(Microsoft.AspNetCore.Mvc.MvcOptions options) } 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 @@ public static partial class MvcCoreMvcBuilderExtensions 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 setupAction) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IMvcBuilder AddJsonOptions(this Microsoft.Extensions.DependencyInjection.IMvcBuilder builder, System.Action configure) { throw null; } public static Microsoft.Extensions.DependencyInjection.IMvcBuilder AddMvcOptions(this Microsoft.Extensions.DependencyInjection.IMvcBuilder builder, System.Action setupAction) { throw null; } public static Microsoft.Extensions.DependencyInjection.IMvcBuilder ConfigureApiBehaviorOptions(this Microsoft.Extensions.DependencyInjection.IMvcBuilder builder, System.Action setupAction) { throw null; } public static Microsoft.Extensions.DependencyInjection.IMvcBuilder ConfigureApplicationPartManager(this Microsoft.Extensions.DependencyInjection.IMvcBuilder builder, System.Action setupAction) { throw null; } @@ -3039,6 +3044,7 @@ public static partial class MvcCoreMvcCoreBuilderExtensions 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 setupAction) { throw null; } + public static Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder AddJsonOptions(this Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder builder, System.Action configure) { throw null; } public static Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder AddMvcOptions(this Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder builder, System.Action setupAction) { throw null; } public static Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder ConfigureApiBehaviorOptions(this Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder builder, System.Action setupAction) { throw null; } public static Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder ConfigureApplicationPartManager(this Microsoft.Extensions.DependencyInjection.IMvcCoreBuilder builder, System.Action setupAction) { throw null; } diff --git a/src/Mvc/Mvc.Core/src/DependencyInjection/MvcCoreMvcBuilderExtensions.cs b/src/Mvc/Mvc.Core/src/DependencyInjection/MvcCoreMvcBuilderExtensions.cs index 74cbeadb21cc..3d0181a57161 100644 --- a/src/Mvc/Mvc.Core/src/DependencyInjection/MvcCoreMvcBuilderExtensions.cs +++ b/src/Mvc/Mvc.Core/src/DependencyInjection/MvcCoreMvcBuilderExtensions.cs @@ -42,6 +42,30 @@ public static IMvcBuilder AddMvcOptions( return builder; } + /// + /// Configures for the specified . + /// + /// The . + /// An to configure the . + /// The . + public static IMvcBuilder AddJsonOptions( + this IMvcBuilder builder, + Action configure) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + builder.Services.Configure(configure); + return builder; + } + /// /// Configures for the specified . /// diff --git a/src/Mvc/Mvc.Core/src/DependencyInjection/MvcCoreMvcCoreBuilderExtensions.cs b/src/Mvc/Mvc.Core/src/DependencyInjection/MvcCoreMvcCoreBuilderExtensions.cs index 7dd460b8c49f..76244cb0aa7f 100644 --- a/src/Mvc/Mvc.Core/src/DependencyInjection/MvcCoreMvcCoreBuilderExtensions.cs +++ b/src/Mvc/Mvc.Core/src/DependencyInjection/MvcCoreMvcCoreBuilderExtensions.cs @@ -44,6 +44,30 @@ public static IMvcCoreBuilder AddMvcOptions( return builder; } + /// + /// Configures for the specified . + /// + /// The . + /// An to configure the . + /// The . + public static IMvcCoreBuilder AddJsonOptions( + this IMvcCoreBuilder builder, + Action configure) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (configure == null) + { + throw new ArgumentNullException(nameof(configure)); + } + + builder.Services.Configure(configure); + return builder; + } + /// /// Adds services to support . /// diff --git a/src/Mvc/Mvc.Core/src/Formatters/SystemTextJsonInputFormatter.cs b/src/Mvc/Mvc.Core/src/Formatters/SystemTextJsonInputFormatter.cs index a99775da126f..e2b275624687 100644 --- a/src/Mvc/Mvc.Core/src/Formatters/SystemTextJsonInputFormatter.cs +++ b/src/Mvc/Mvc.Core/src/Formatters/SystemTextJsonInputFormatter.cs @@ -19,10 +19,10 @@ public class SystemTextJsonInputFormatter : TextInputFormatter, IInputFormatterE /// /// Initializes a new instance of . /// - /// The . - public SystemTextJsonInputFormatter(MvcOptions options) + /// The . + public SystemTextJsonInputFormatter(JsonOptions options) { - SerializerOptions = options.SerializerOptions; + SerializerOptions = options.JsonSerializerOptions; SupportedEncodings.Add(UTF8EncodingWithoutBOM); SupportedEncodings.Add(UTF16EncodingLittleEndian); diff --git a/src/Mvc/Mvc.Core/src/Formatters/SystemTextJsonOutputFormatter.cs b/src/Mvc/Mvc.Core/src/Formatters/SystemTextJsonOutputFormatter.cs index b39ab6ded60a..0ff0ce706592 100644 --- a/src/Mvc/Mvc.Core/src/Formatters/SystemTextJsonOutputFormatter.cs +++ b/src/Mvc/Mvc.Core/src/Formatters/SystemTextJsonOutputFormatter.cs @@ -20,10 +20,10 @@ public class SystemTextJsonOutputFormatter : TextOutputFormatter /// /// Initializes a new instance. /// - /// The . - public SystemTextJsonOutputFormatter(MvcOptions options) + /// The . + public SystemTextJsonOutputFormatter(JsonOptions options) { - SerializerOptions = options.SerializerOptions; + SerializerOptions = options.JsonSerializerOptions; SupportedEncodings.Add(Encoding.UTF8); SupportedEncodings.Add(Encoding.Unicode); diff --git a/src/Mvc/Mvc.Core/src/Infrastructure/MvcCoreMvcOptionsSetup.cs b/src/Mvc/Mvc.Core/src/Infrastructure/MvcCoreMvcOptionsSetup.cs index d856b684b8b7..b94ff3955787 100644 --- a/src/Mvc/Mvc.Core/src/Infrastructure/MvcCoreMvcOptionsSetup.cs +++ b/src/Mvc/Mvc.Core/src/Infrastructure/MvcCoreMvcOptionsSetup.cs @@ -25,21 +25,33 @@ internal class MvcCoreMvcOptionsSetup : IConfigureOptions, IPostConf { private readonly IHttpRequestStreamReaderFactory _readerFactory; private readonly ILoggerFactory _loggerFactory; + private readonly IOptions _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) { 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 @@ public void Configure(MvcOptions options) 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()); diff --git a/src/Mvc/Mvc.Core/src/Infrastructure/SystemTextJsonResultExecutor.cs b/src/Mvc/Mvc.Core/src/Infrastructure/SystemTextJsonResultExecutor.cs index c3e0e5f9ddb8..6706b30db092 100644 --- a/src/Mvc/Mvc.Core/src/Infrastructure/SystemTextJsonResultExecutor.cs +++ b/src/Mvc/Mvc.Core/src/Infrastructure/SystemTextJsonResultExecutor.cs @@ -23,14 +23,14 @@ internal sealed class SystemTextJsonResultExecutor : IActionResultExecutor _logger; public SystemTextJsonResultExecutor( - IOptions mvcOptions, + IOptions options, ILogger logger) { - _mvcOptions = mvcOptions.Value; + _options = options.Value; _logger = logger; } @@ -100,7 +100,7 @@ private JsonSerializerOptions GetSerializerOptions(JsonResult result) var serializerSettings = result.SerializerSettings; if (serializerSettings == null) { - return _mvcOptions.SerializerOptions; + return _options.JsonSerializerOptions; } else { diff --git a/src/Mvc/Mvc.Core/src/JsonOptions.cs b/src/Mvc/Mvc.Core/src/JsonOptions.cs new file mode 100644 index 000000000000..86768039e3bc --- /dev/null +++ b/src/Mvc/Mvc.Core/src/JsonOptions.cs @@ -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 + { + /// + /// Gets the used by and + /// . + /// + 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, + }; + } +} diff --git a/src/Mvc/Mvc.Core/src/MvcOptions.cs b/src/Mvc/Mvc.Core/src/MvcOptions.cs index 4933974b22f3..2cb5f9fa7216 100644 --- a/src/Mvc/Mvc.Core/src/MvcOptions.cs +++ b/src/Mvc/Mvc.Core/src/MvcOptions.cs @@ -5,8 +5,6 @@ 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 @@ public MvcOptions() /// /// Gets or sets a value that detemines if the inference of for - /// for properties and parameters of non-nullable reference types is suppressed. If false - /// (the default), then all non-nullable reference types will behave as-if [Required] has - /// been applied. If true, this behavior will be suppressed; nullable reference types and + /// for properties and parameters of non-nullable reference types is suppressed. If false + /// (the default), then all non-nullable reference types will behave as-if [Required] has + /// been applied. If true, this behavior will be suppressed; nullable reference types and /// non-nullable reference types will behave the same for the purposes of validation. /// /// /// - /// 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. /// /// - /// 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 /// [Required] has been applied, resulting in validation errors when no value was bound. /// /// @@ -361,21 +359,6 @@ public int MaxModelBindingRecursionDepth } } - /// - /// Gets the used by and - /// . - /// - 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 IEnumerable.GetEnumerator() => _switches.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _switches.GetEnumerator(); diff --git a/src/Mvc/Mvc.Core/test/CreatedAtActionResultTests.cs b/src/Mvc/Mvc.Core/test/CreatedAtActionResultTests.cs index 0088dadf499f..cad29a30437d 100644 --- a/src/Mvc/Mvc.Core/test/CreatedAtActionResultTests.cs +++ b/src/Mvc/Mvc.Core/test/CreatedAtActionResultTests.cs @@ -91,7 +91,7 @@ private static IServiceProvider CreateServices() { 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>(new ObjectResultExecutor( diff --git a/src/Mvc/Mvc.Core/test/CreatedAtRouteResultTests.cs b/src/Mvc/Mvc.Core/test/CreatedAtRouteResultTests.cs index 829992317681..65410977a06c 100644 --- a/src/Mvc/Mvc.Core/test/CreatedAtRouteResultTests.cs +++ b/src/Mvc/Mvc.Core/test/CreatedAtRouteResultTests.cs @@ -104,7 +104,7 @@ private static IServiceProvider CreateServices() { 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>(new ObjectResultExecutor( diff --git a/src/Mvc/Mvc.Core/test/CreatedResultTests.cs b/src/Mvc/Mvc.Core/test/CreatedResultTests.cs index 8d984ec7beaa..6d3eefc85bde 100644 --- a/src/Mvc/Mvc.Core/test/CreatedResultTests.cs +++ b/src/Mvc/Mvc.Core/test/CreatedResultTests.cs @@ -92,7 +92,7 @@ private static IServiceProvider CreateServices() { 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>(new ObjectResultExecutor( diff --git a/src/Mvc/Mvc.Core/test/Formatters/FormatFilterTest.cs b/src/Mvc/Mvc.Core/test/Formatters/FormatFilterTest.cs index bb8da19ae2a5..fea2967990ee 100644 --- a/src/Mvc/Mvc.Core/test/Formatters/FormatFilterTest.cs +++ b/src/Mvc/Mvc.Core/test/Formatters/FormatFilterTest.cs @@ -465,7 +465,7 @@ private void Initialize( // 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( diff --git a/src/Mvc/Mvc.Core/test/Formatters/SystemTextJsonInputFormatterTest.cs b/src/Mvc/Mvc.Core/test/Formatters/SystemTextJsonInputFormatterTest.cs index 020c35796116..8cea7ff8216b 100644 --- a/src/Mvc/Mvc.Core/test/Formatters/SystemTextJsonInputFormatterTest.cs +++ b/src/Mvc/Mvc.Core/test/Formatters/SystemTextJsonInputFormatterTest.cs @@ -34,7 +34,7 @@ public override Task ReadAsync_UsesTryAddModelValidationErrorsToModelState() protected override TextInputFormatter GetInputFormatter() { - return new SystemTextJsonInputFormatter(new MvcOptions()); + return new SystemTextJsonInputFormatter(new JsonOptions()); } } } diff --git a/src/Mvc/Mvc.Core/test/Formatters/SystemTextJsonOutputFormatterTest.cs b/src/Mvc/Mvc.Core/test/Formatters/SystemTextJsonOutputFormatterTest.cs index edd5d64f2c99..0a3d90e18b92 100644 --- a/src/Mvc/Mvc.Core/test/Formatters/SystemTextJsonOutputFormatterTest.cs +++ b/src/Mvc/Mvc.Core/test/Formatters/SystemTextJsonOutputFormatterTest.cs @@ -16,7 +16,7 @@ public class SystemTextJsonOutputFormatterTest : JsonOutputFormatterTestBase { protected override TextOutputFormatter GetOutputFormatter() { - return new SystemTextJsonOutputFormatter(new MvcOptions()); + return new SystemTextJsonOutputFormatter(new JsonOptions()); } [Theory] diff --git a/src/Mvc/Mvc.Core/test/HttpNotFoundObjectResultTest.cs b/src/Mvc/Mvc.Core/test/HttpNotFoundObjectResultTest.cs index 58d6abb3f17c..498a12c57ac4 100644 --- a/src/Mvc/Mvc.Core/test/HttpNotFoundObjectResultTest.cs +++ b/src/Mvc/Mvc.Core/test/HttpNotFoundObjectResultTest.cs @@ -69,7 +69,7 @@ private static IServiceProvider CreateServices() { 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>(new ObjectResultExecutor( diff --git a/src/Mvc/Mvc.Core/test/HttpOkObjectResultTest.cs b/src/Mvc/Mvc.Core/test/HttpOkObjectResultTest.cs index 0c29f4d541f1..13f5572c4e53 100644 --- a/src/Mvc/Mvc.Core/test/HttpOkObjectResultTest.cs +++ b/src/Mvc/Mvc.Core/test/HttpOkObjectResultTest.cs @@ -70,7 +70,7 @@ private static IServiceProvider CreateServices() { 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>(new ObjectResultExecutor( diff --git a/src/Mvc/Mvc.Core/test/Infrastructure/SystemTextJsonResultExecutorTest.cs b/src/Mvc/Mvc.Core/test/Infrastructure/SystemTextJsonResultExecutorTest.cs index 3467dc72a615..062d5d12272b 100644 --- a/src/Mvc/Mvc.Core/test/Infrastructure/SystemTextJsonResultExecutorTest.cs +++ b/src/Mvc/Mvc.Core/test/Infrastructure/SystemTextJsonResultExecutorTest.cs @@ -11,7 +11,7 @@ public class SystemTextJsonResultExecutorTest : JsonResultExecutorTestBase { protected override IActionResultExecutor CreateExecutor(ILoggerFactory loggerFactory) { - return new SystemTextJsonResultExecutor(Options.Create(new MvcOptions()), loggerFactory.CreateLogger()); + return new SystemTextJsonResultExecutor(Options.Create(new JsonOptions()), loggerFactory.CreateLogger()); } protected override object GetIndentedSettings() diff --git a/src/Mvc/Mvc.ViewFeatures/src/Rendering/SystemTextJsonHelper.cs b/src/Mvc/Mvc.ViewFeatures/src/Rendering/SystemTextJsonHelper.cs index cff4844c7755..8524da0f94ce 100644 --- a/src/Mvc/Mvc.ViewFeatures/src/Rendering/SystemTextJsonHelper.cs +++ b/src/Mvc/Mvc.ViewFeatures/src/Rendering/SystemTextJsonHelper.cs @@ -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) + public SystemTextJsonHelper(IOptions options) { - _mvcOptions = mvcOptions.Value; + _options = options.Value; } /// @@ -22,7 +22,7 @@ public IHtmlContent Serialize(object value) { // 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); } } diff --git a/src/Mvc/Mvc.ViewFeatures/test/Rendering/SystemTextJsonHelperTest.cs b/src/Mvc/Mvc.ViewFeatures/test/Rendering/SystemTextJsonHelperTest.cs index 4c07e933fd40..4550976a40a9 100644 --- a/src/Mvc/Mvc.ViewFeatures/test/Rendering/SystemTextJsonHelperTest.cs +++ b/src/Mvc/Mvc.ViewFeatures/test/Rendering/SystemTextJsonHelperTest.cs @@ -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 @@ public class SystemTextJsonHelperTest : JsonHelperTestBase { 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)); } } } diff --git a/src/Mvc/test/Mvc.FunctionalTests/JsonInputFormatterTestBase.cs b/src/Mvc/test/Mvc.FunctionalTests/JsonInputFormatterTestBase.cs index ceeb88fd4837..347bca0e2548 100644 --- a/src/Mvc/test/Mvc.FunctionalTests/JsonInputFormatterTestBase.cs +++ b/src/Mvc/test/Mvc.FunctionalTests/JsonInputFormatterTestBase.cs @@ -8,6 +8,7 @@ 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 @@ public async Task JsonInputFormatter_ReadsPrimitiveTypes() 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(); + + // 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() { diff --git a/src/Mvc/test/WebSites/BasicWebSite/Startup.cs b/src/Mvc/test/WebSites/BasicWebSite/Startup.cs index 7e5236d53bee..3d93826f2c91 100644 --- a/src/Mvc/test/WebSites/BasicWebSite/Startup.cs +++ b/src/Mvc/test/WebSites/BasicWebSite/Startup.cs @@ -13,8 +13,6 @@ public class Startup // Set up application services public void ConfigureServices(IServiceCollection services) { - services.AddRouting(); - services.AddMvc() .SetCompatibilityVersion(CompatibilityVersion.Latest) .AddNewtonsoftJson() diff --git a/src/Mvc/test/WebSites/FormatterWebSite/Controllers/JsonFormatterController.cs b/src/Mvc/test/WebSites/FormatterWebSite/Controllers/JsonFormatterController.cs index 0ebaf0c3242c..1e848590ae95 100644 --- a/src/Mvc/test/WebSites/FormatterWebSite/Controllers/JsonFormatterController.cs +++ b/src/Mvc/test/WebSites/FormatterWebSite/Controllers/JsonFormatterController.cs @@ -64,5 +64,20 @@ public IActionResult ValueTypeAsBody([FromBody] int value) return Content(value.ToString()); } + + [HttpPost] + public ActionResult RoundtripSimpleModel([FromBody] SimpleModel model) + { + return model; + } + + public class SimpleModel + { + public int Id { get; set; } + + public string Name { get; set; } + + public string StreetName { get; set; } + } } } \ No newline at end of file