Просмотр исходного кода

Use request culture to deserialize JSON body (#31331)

* Use request culture to deserialize JSON body

Add new ReadJsonWithRequestCulture option to support
deserializing JSON using the current HTTP request's culture
when using the Newtonsoft.Json input formatter with MVC.

Fixes #9994.
Martin Costello 4 лет назад
Родитель
Сommit
44bcd96cc9

+ 10 - 0
src/Mvc/Mvc.NewtonsoftJson/src/MvcNewtonsoftJsonOptions.cs

@@ -4,6 +4,7 @@
 using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.Globalization;
 using Microsoft.AspNetCore.Mvc.Formatters;
 using Microsoft.AspNetCore.Mvc.Infrastructure;
 using Microsoft.AspNetCore.Mvc.ModelBinding;
@@ -49,6 +50,15 @@ namespace Microsoft.AspNetCore.Mvc
         /// <value>Defaults to 30Kb.</value>
         public int InputFormatterMemoryBufferThreshold { get; set; } = 1024 * 30;
 
+        /// <summary>
+        /// Gets or sets a flag to determine whether the value of <see cref="CultureInfo.CurrentCulture"/>
+        /// for the current HTTP request is used for JSON deserialization by <see cref="NewtonsoftJsonInputFormatter"/>.
+        /// </summary>
+        /// <value>
+        /// The default value is <see langword="false"/>.
+        /// </value>
+        public bool ReadJsonWithRequestCulture { get; set; }
+
         /// <summary>
         /// Gets the maximum size to buffer in memory when <see cref="MvcOptions.SuppressOutputFormatterBuffering"/> is not set.
         /// <para>

+ 7 - 0
src/Mvc/Mvc.NewtonsoftJson/src/NewtonsoftJsonInputFormatter.cs

@@ -3,6 +3,7 @@
 
 using System;
 using System.Buffers;
+using System.Globalization;
 using System.IO;
 using System.Runtime.ExceptionServices;
 using System.Text;
@@ -175,6 +176,12 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
                 var type = context.ModelType;
                 var jsonSerializer = CreateJsonSerializer(context);
                 jsonSerializer.Error += ErrorHandler;
+
+                if (_jsonOptions.ReadJsonWithRequestCulture)
+                {
+                    jsonSerializer.Culture = CultureInfo.CurrentCulture;
+                }
+
                 try
                 {
                     model = jsonSerializer.Deserialize(jsonReader, type);

+ 2 - 0
src/Mvc/Mvc.NewtonsoftJson/src/PublicAPI.Unshipped.txt

@@ -66,3 +66,5 @@ virtual Microsoft.AspNetCore.Mvc.Formatters.NewtonsoftJsonInputFormatter.Release
 virtual Microsoft.AspNetCore.Mvc.Formatters.NewtonsoftJsonOutputFormatter.CreateJsonSerializer() -> Newtonsoft.Json.JsonSerializer!
 virtual Microsoft.AspNetCore.Mvc.Formatters.NewtonsoftJsonOutputFormatter.CreateJsonSerializer(Microsoft.AspNetCore.Mvc.Formatters.OutputFormatterWriteContext! context) -> Newtonsoft.Json.JsonSerializer!
 virtual Microsoft.AspNetCore.Mvc.Formatters.NewtonsoftJsonOutputFormatter.CreateJsonWriter(System.IO.TextWriter! writer) -> Newtonsoft.Json.JsonWriter!
+Microsoft.AspNetCore.Mvc.MvcNewtonsoftJsonOptions.ReadJsonWithRequestCulture.get -> bool
+Microsoft.AspNetCore.Mvc.MvcNewtonsoftJsonOptions.ReadJsonWithRequestCulture.set -> void

+ 53 - 0
src/Mvc/Mvc.NewtonsoftJson/test/NewtonsoftJsonInputFormatterTest.cs

@@ -434,6 +434,59 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
             httpContext.Verify();
         }
 
+        [Theory]
+        [InlineData("2019-05-15", "")]
+        [InlineData("2019-05-15", "en-CA")]
+        [InlineData("15/05/2019", "en-GB")]
+        [InlineData("5/15/2019", "en-US")]
+        [InlineData("15/05/2019", "es-ES")]
+        [InlineData("15/5/2019", "es-MX")]
+        [InlineData("2019-05-15", "fr-CA")]
+        [InlineData("15/05/2019", "fr-FR")]
+        [InlineData("15.05.2019", "ru-RU")]
+        [InlineData("2019/5/15", "zh-CN")]
+        public async Task ReadAsync_WithReadJsonWithRequestCulture_DeserializesUsingRequestCulture(
+            string dateString,
+            string culture)
+        {
+            // Arrange
+            var formatter = new NewtonsoftJsonInputFormatter(
+                GetLogger(),
+                _serializerSettings,
+                ArrayPool<char>.Shared,
+                _objectPoolProvider,
+                new MvcOptions(),
+                new MvcNewtonsoftJsonOptions() { ReadJsonWithRequestCulture = true });
+
+            var originalCulture = CultureInfo.CurrentCulture;
+            CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo(culture);
+
+            try
+            {
+                var content = $"{{'DateValue': '{dateString}'}}";
+                var contentBytes = Encoding.UTF8.GetBytes(content);
+                var httpContext = new DefaultHttpContext();
+                httpContext.Features.Set<IHttpResponseFeature>(new TestResponseFeature());
+                httpContext.Request.Body = new NonSeekableReadStream(contentBytes, allowSyncReads: false);
+                httpContext.Request.ContentType = "application/json";
+
+                var formatterContext = CreateInputFormatterContext(typeof(TypeWithPrimitives), httpContext);
+
+                // Act
+                var result = await formatter.ReadAsync(formatterContext);
+
+                // Assert
+                Assert.False(result.HasError);
+
+                var userModel = Assert.IsType<TypeWithPrimitives>(result.Model);
+                Assert.Equal(new DateTime(2019, 05, 15, 00, 00, 00, DateTimeKind.Unspecified), userModel.DateValue);
+            }
+            finally
+            {
+                CultureInfo.CurrentCulture = originalCulture;
+            }
+        }
+
         private class TestableJsonInputFormatter : NewtonsoftJsonInputFormatter
         {
             public TestableJsonInputFormatter(JsonSerializerSettings settings, ObjectPoolProvider objectPoolProvider)