React to new System.Text.Json features (#9572)

This commit is contained in:
Pranav K 2019-04-21 20:48:41 -07:00 committed by GitHub
parent 19ce372887
commit 418f3d82dd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 127 additions and 157 deletions

View File

@ -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();

View File

@ -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]

View File

@ -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());

View File

@ -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; }

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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();
}
}

View File

@ -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();

View File

@ -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);
}
}
}

View File

@ -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();
}
}