React to new System.Text.Json features (#9572)
This commit is contained in:
parent
19ce372887
commit
418f3d82dd
|
|
@ -329,7 +329,16 @@ namespace Microsoft.AspNetCore.Mvc
|
|||
/// Gets the <see cref="JsonSerializerOptions"/> used by <see cref="SystemTextJsonInputFormatter"/> and
|
||||
/// <see cref="SystemTextJsonOutputFormatter"/>.
|
||||
/// </summary>
|
||||
public JsonSerializerOptions SerializerOptions { get; } = new JsonSerializerOptions();
|
||||
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();
|
||||
|
||||
|
|
|
|||
|
|
@ -106,7 +106,8 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
public virtual async Task JsonFormatterReadsDateTimeValue()
|
||||
{
|
||||
// Arrange
|
||||
var content = "\"2012-02-01 12:45 AM\"";
|
||||
var expected = new DateTime(2012, 02, 01, 00, 45, 00);
|
||||
var content = $"\"{expected.ToString("O")}\"";
|
||||
var formatter = GetInputFormatter();
|
||||
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
|
|
@ -120,7 +121,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
var dateValue = Assert.IsType<DateTime>(result.Model);
|
||||
Assert.Equal(new DateTime(2012, 02, 01, 00, 45, 00), dateValue);
|
||||
Assert.Equal(expected, dateValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -129,7 +130,7 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
// Arrange
|
||||
var formatter = GetInputFormatter();
|
||||
|
||||
var content = "{\"Name\": \"Person Name\", \"Age\": 30}";
|
||||
var content = "{\"name\": \"Person Name\", \"age\": 30}";
|
||||
var contentBytes = Encoding.UTF8.GetBytes(content);
|
||||
var httpContext = GetHttpContext(contentBytes);
|
||||
|
||||
|
|
@ -194,8 +195,8 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
|
||||
// Assert
|
||||
Assert.False(result.HasError);
|
||||
var integers = Assert.IsType<List<int>>(result.Model);
|
||||
Assert.Equal(new int[] { 0, 23, 300 }, integers);
|
||||
Assert.IsAssignableFrom(requestedType, result.Model);
|
||||
Assert.Equal(new int[] { 0, 23, 300 }, (IEnumerable<int>)result.Model);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
|
|
@ -8,12 +8,6 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
{
|
||||
public class SystemTextJsonInputFormatterTest : JsonInputFormatterTestBase
|
||||
{
|
||||
[Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/8489")]
|
||||
public override Task JsonFormatterReadsDateTimeValue()
|
||||
{
|
||||
return base.JsonFormatterReadsDateTimeValue();
|
||||
}
|
||||
|
||||
[Fact(Skip = "https://github.com/aspnet/AspNetCore/issues/8474")]
|
||||
public override Task ReadAsync_AddsModelValidationErrorsToModelState()
|
||||
{
|
||||
|
|
@ -38,24 +32,6 @@ namespace Microsoft.AspNetCore.Mvc.Formatters
|
|||
return base.ReadAsync_UsesTryAddModelValidationErrorsToModelState();
|
||||
}
|
||||
|
||||
[Fact(Skip = "https://github.com/dotnet/corefx/issues/36026")]
|
||||
public override Task ReadAsync_ReadsValidArray_AsCollectionOfT()
|
||||
{
|
||||
return base.ReadAsync_ReadsValidArray_AsCollectionOfT();
|
||||
}
|
||||
|
||||
[Fact(Skip = "https://github.com/dotnet/corefx/issues/36026")]
|
||||
public override Task ReadAsync_ReadsValidArray_AsEnumerableOfT()
|
||||
{
|
||||
return base.ReadAsync_ReadsValidArray_AsEnumerableOfT();
|
||||
}
|
||||
|
||||
[Fact(Skip = "https://github.com/dotnet/corefx/issues/36026")]
|
||||
public override Task ReadAsync_ReadsValidArray_AsIListOfT()
|
||||
{
|
||||
return base.ReadAsync_ReadsValidArray_AsIListOfT();
|
||||
}
|
||||
|
||||
protected override TextInputFormatter GetInputFormatter()
|
||||
{
|
||||
return new SystemTextJsonInputFormatter(new MvcOptions());
|
||||
|
|
|
|||
|
|
@ -213,6 +213,28 @@ namespace Microsoft.AspNetCore.Mvc.NewtonsoftJson
|
|||
testProvider.EnsureObjectCanBeSerialized(value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public override void RoundTripTest_GuidToString()
|
||||
{
|
||||
// Documents the behavior of round-tripping a Guid value as a string
|
||||
// Arrange
|
||||
var key = "test-key";
|
||||
var testProvider = GetTempDataSerializer();
|
||||
var value = Guid.NewGuid();
|
||||
var input = new Dictionary<string, object>
|
||||
{
|
||||
{ key, value.ToString() }
|
||||
};
|
||||
|
||||
// Act
|
||||
var bytes = testProvider.Serialize(input);
|
||||
var values = testProvider.Deserialize(bytes);
|
||||
|
||||
// Assert
|
||||
var roundTripValue = Assert.IsType<string>(values[key]);
|
||||
Assert.Equal(value.ToString(), roundTripValue);
|
||||
}
|
||||
|
||||
private class TestItem
|
||||
{
|
||||
public int DummyInt { get; set; }
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure
|
||||
|
|
@ -46,19 +45,17 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure
|
|||
break;
|
||||
|
||||
case JsonValueType.String:
|
||||
var stringValue = item.Value.GetString();
|
||||
// BsonTempDataSerializer will parse certain types of string values. We'll attempt to imitiate it.
|
||||
if (DateTime.TryParseExact(stringValue, "r", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var dateTime))
|
||||
{
|
||||
deserializedValue = dateTime;
|
||||
}
|
||||
else if (Guid.TryParseExact(stringValue, "B", out var guid))
|
||||
if (item.Value.TryGetGuid(out var guid))
|
||||
{
|
||||
deserializedValue = guid;
|
||||
}
|
||||
else if (item.Value.TryGetDateTime(out var dateTime))
|
||||
{
|
||||
deserializedValue = dateTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
deserializedValue = stringValue;
|
||||
deserializedValue = item.Value.GetString();
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -123,11 +120,11 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure
|
|||
break;
|
||||
|
||||
case DateTime dateTime:
|
||||
writer.WriteString(key, dateTime.ToString("r", CultureInfo.InvariantCulture));
|
||||
writer.WriteString(key, dateTime);
|
||||
break;
|
||||
|
||||
case Guid guid:
|
||||
writer.WriteString(key, guid.ToString("B", CultureInfo.InvariantCulture));
|
||||
writer.WriteString(key, guid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,11 +12,11 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure
|
|||
protected override TempDataSerializer GetTempDataSerializer() => new DefaultTempDataSerializer();
|
||||
|
||||
[Fact]
|
||||
public void RoundTripTest_StringThatLooksLikeCompliantDateTime()
|
||||
public void RoundTripTest_NonStandardDateTimeStringFormat_RoundTripsAsString()
|
||||
{
|
||||
// This is an unintentional side-effect of trying to support a compat with JSON.NET.
|
||||
// Any string that looks like a compliant DateTime object will be parsed as a DateTime.
|
||||
// This test documents this behavior.
|
||||
// DateTime that do not match the format that System.Text.Json uses for round-tripping
|
||||
// should round-trip as strings.
|
||||
|
||||
// Arrange
|
||||
var key = "test-key";
|
||||
var testProvider = GetTempDataSerializer();
|
||||
|
|
@ -30,57 +30,9 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure
|
|||
var bytes = testProvider.Serialize(input);
|
||||
var values = testProvider.Deserialize(bytes);
|
||||
|
||||
// Assert
|
||||
var roundTripValue = Assert.IsType<DateTime>(values[key]);
|
||||
Assert.Equal(value, roundTripValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RoundTripTest_StringThatIsNotCompliantDateTime()
|
||||
{
|
||||
// This is an unintentional side-effect of trying to support a compat with JSON.NET.
|
||||
// Any string that looks like a compliant DateTime object will be parsed as a DateTime.
|
||||
// This test documents this behavior.
|
||||
// Arrange
|
||||
var key = "test-key";
|
||||
var testProvider = GetTempDataSerializer();
|
||||
var value = new DateTime(2009, 1, 1, 12, 37, 43);
|
||||
var input = new Dictionary<string, object>
|
||||
{
|
||||
{ key, value.ToString() }
|
||||
};
|
||||
|
||||
// Act
|
||||
var bytes = testProvider.Serialize(input);
|
||||
var values = testProvider.Deserialize(bytes);
|
||||
|
||||
// Assert
|
||||
var roundTripValue = Assert.IsType<string>(values[key]);
|
||||
Assert.Equal(value.ToString(), roundTripValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RoundTripTest_StringThatIsNotCompliantGuid()
|
||||
{
|
||||
// This is an unintentional side-effect of trying to support a compat with JSON.NET.
|
||||
// Any string that looks like a compliant DateTime object will be parsed as a DateTime.
|
||||
// This test documents this behavior.
|
||||
// Arrange
|
||||
var key = "test-key";
|
||||
var testProvider = GetTempDataSerializer();
|
||||
var value = Guid.NewGuid();
|
||||
var input = new Dictionary<string, object>
|
||||
{
|
||||
{ key, value.ToString() }
|
||||
};
|
||||
|
||||
// Act
|
||||
var bytes = testProvider.Serialize(input);
|
||||
var values = testProvider.Deserialize(bytes);
|
||||
|
||||
// Assert
|
||||
var roundTripValue = Assert.IsType<string>(values[key]);
|
||||
Assert.Equal(value.ToString(), roundTripValue);
|
||||
Assert.Equal(value.ToString("r"), roundTripValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -173,6 +173,72 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Infrastructure
|
|||
Assert.Equal(value, roundTripValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public virtual void RoundTripTest_DateTimeToString()
|
||||
{
|
||||
// Documents the behavior of round-tripping a DateTime value as a string
|
||||
// Arrange
|
||||
var key = "test-key";
|
||||
var testProvider = GetTempDataSerializer();
|
||||
var value = new DateTime(2009, 1, 1, 12, 37, 43);
|
||||
var input = new Dictionary<string, object>
|
||||
{
|
||||
{ key, value.ToString() }
|
||||
};
|
||||
|
||||
// Act
|
||||
var bytes = testProvider.Serialize(input);
|
||||
var values = testProvider.Deserialize(bytes);
|
||||
|
||||
// Assert
|
||||
var roundTripValue = Assert.IsType<string>(values[key]);
|
||||
Assert.Equal(value.ToString(), roundTripValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public virtual void RoundTripTest_StringThatIsNotCompliantGuid()
|
||||
{
|
||||
// Documents the behavior of round-tripping a Guid with a non-default format specifier
|
||||
// Arrange
|
||||
var key = "test-key";
|
||||
var testProvider = GetTempDataSerializer();
|
||||
var value = Guid.NewGuid();
|
||||
var input = new Dictionary<string, object>
|
||||
{
|
||||
{ key, value.ToString("N") }
|
||||
};
|
||||
|
||||
// Act
|
||||
var bytes = testProvider.Serialize(input);
|
||||
var values = testProvider.Deserialize(bytes);
|
||||
|
||||
// Assert
|
||||
var roundTripValue = Assert.IsType<string>(values[key]);
|
||||
Assert.Equal(value.ToString("N"), roundTripValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public virtual void RoundTripTest_GuidToString()
|
||||
{
|
||||
// Documents the behavior of round-tripping a Guid value as a string
|
||||
// Arrange
|
||||
var key = "test-key";
|
||||
var testProvider = GetTempDataSerializer();
|
||||
var value = Guid.NewGuid();
|
||||
var input = new Dictionary<string, object>
|
||||
{
|
||||
{ key, value.ToString() }
|
||||
};
|
||||
|
||||
// Act
|
||||
var bytes = testProvider.Serialize(input);
|
||||
var values = testProvider.Deserialize(bytes);
|
||||
|
||||
// Assert
|
||||
var roundTripValue = Assert.IsType<Guid>(values[key]);
|
||||
Assert.Equal(value, roundTripValue);
|
||||
}
|
||||
|
||||
protected abstract TempDataSerializer GetTempDataSerializer();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -507,7 +507,7 @@ Products: Book1, Book2 (1)";
|
|||
|
||||
// Act - 3
|
||||
// Trigger an expiration of the nested content.
|
||||
var content = @"[{ ""ProductName"": ""Music Systems"" },{ ""ProductName"": ""Televisions"" }]";
|
||||
var content = @"[{ ""productName"": ""Music Systems"" },{ ""productName"": ""Televisions"" }]";
|
||||
var requestMessage = new HttpRequestMessage(HttpMethod.Post, "/categories/Electronics");
|
||||
requestMessage.Content = new StringContent(content, Encoding.UTF8, "application/json");
|
||||
(await Client.SendAsync(requestMessage)).EnsureSuccessStatusCode();
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
// 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;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Xunit;
|
||||
|
|
@ -31,7 +33,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
{
|
||||
// Arrange
|
||||
var sampleInputInt = 10;
|
||||
var input = "{\"SampleInt\":10}";
|
||||
var input = "{\"sampleInt\":10}";
|
||||
var content = new StringContent(input, Encoding.UTF8, requestContentType);
|
||||
|
||||
// Act
|
||||
|
|
@ -43,7 +45,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("application/json", "{\"SampleInt\":10}", 10)]
|
||||
[InlineData("application/json", "{\"sampleInt\":10}", 10)]
|
||||
[InlineData("application/json", "{}", 0)]
|
||||
public async Task JsonInputFormatter_IsModelStateValid_ForValidContentType(
|
||||
string requestContentType,
|
||||
|
|
@ -100,7 +102,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
public async Task JsonInputFormatter_Returns415UnsupportedMediaType_ForEmptyContentType()
|
||||
{
|
||||
// Arrange
|
||||
var jsonInput = "{\"SampleInt\":10}";
|
||||
var jsonInput = "{\"sampleInt\":10}";
|
||||
var content = new StringContent(jsonInput, Encoding.UTF8, "application/json");
|
||||
content.Headers.Clear();
|
||||
|
||||
|
|
@ -112,7 +114,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("application/json", "{\"SampleInt\":10}", 10)]
|
||||
[InlineData("application/json", "{\"sampleInt\":10}", 10)]
|
||||
[InlineData("application/json", "{}", 0)]
|
||||
public async Task JsonInputFormatter_IsModelStateValid_ForTransferEncodingChunk(
|
||||
string requestContentType,
|
||||
|
|
@ -133,6 +135,5 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
Assert.Equal(expectedSampleIntValue.ToString(), responseBody);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
{
|
||||
}
|
||||
|
||||
[Fact(Skip = "Insert issue here")]
|
||||
[Fact(Skip = "Dictionary serialization does not correctly work.")]
|
||||
public override Task SerializableErrorIsReturnedInExpectedFormat() => base.SerializableErrorIsReturnedInExpectedFormat();
|
||||
|
||||
[Fact]
|
||||
|
|
@ -29,67 +29,13 @@ namespace Microsoft.AspNetCore.Mvc.FunctionalTests
|
|||
Assert.Equal("\"Hello Mr. \\ud83e\\udd8a\"", await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public override async Task Formatting_SimpleModel()
|
||||
{
|
||||
// Arrange
|
||||
var expected = "{\"Id\":10,\"Name\":\"Test\",\"StreetName\":\"Some street\"}";
|
||||
|
||||
// Act
|
||||
var response = await Client.GetAsync($"/JsonOutputFormatter/{nameof(JsonOutputFormatterController.SimpleModelResult)}");
|
||||
|
||||
// Assert
|
||||
await response.AssertStatusCodeAsync(HttpStatusCode.OK);
|
||||
Assert.Equal(expected, await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public override async Task Formatting_CollectionType()
|
||||
{
|
||||
// Arrange
|
||||
var expected = "[{\"Id\":10,\"Name\":\"TestName\",\"StreetName\":null},{\"Id\":11,\"Name\":\"TestName1\",\"StreetName\":\"Some street\"}]";
|
||||
|
||||
// Act
|
||||
var response = await Client.GetAsync($"/JsonOutputFormatter/{nameof(JsonOutputFormatterController.CollectionModelResult)}");
|
||||
|
||||
// Assert
|
||||
await response.AssertStatusCodeAsync(HttpStatusCode.OK);
|
||||
Assert.Equal(expected, await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
|
||||
[Fact(Skip = "Dictionary serialization does not correctly work.")]
|
||||
public override Task Formatting_DictionaryType() => base.Formatting_DictionaryType();
|
||||
|
||||
[Fact(Skip = "Dictionary serialization does not correctly work.")]
|
||||
public override Task Formatting_ProblemDetails() => base.Formatting_ProblemDetails();
|
||||
|
||||
[Fact]
|
||||
public override async Task Formatting_PolymorphicModel()
|
||||
{
|
||||
// Arrange
|
||||
var expected = "{\"Id\":10,\"Name\":\"test\",\"StreetName\":null}";
|
||||
|
||||
// Act
|
||||
var response = await Client.GetAsync($"/JsonOutputFormatter/{nameof(JsonOutputFormatterController.PolymorphicResult)}");
|
||||
|
||||
// Assert
|
||||
await response.AssertStatusCodeAsync(HttpStatusCode.OK);
|
||||
Assert.Equal(expected, await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public override async Task Formatting_LargeObject()
|
||||
{
|
||||
// Arrange
|
||||
var expectedName = "This is long so we can test large objects " + new string('a', 1024 * 65);
|
||||
var expected = $"{{\"Id\":10,\"Name\":\"{expectedName}\",\"StreetName\":null}}";
|
||||
|
||||
// Act
|
||||
var response = await Client.GetAsync($"/JsonOutputFormatter/{nameof(JsonOutputFormatterController.LargeObjectResult)}");
|
||||
|
||||
// Assert
|
||||
await response.AssertStatusCodeAsync(HttpStatusCode.OK);
|
||||
Assert.Equal(expected, await response.Content.ReadAsStringAsync());
|
||||
}
|
||||
[Fact(Skip = "https://github.com/dotnet/corefx/issues/36166")]
|
||||
public override Task Formatting_PolymorphicModel() => base.Formatting_PolymorphicModel();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue