Skip to content

Commit c1e82b8

Browse files
authored
Set default JSON type resolver if not set (#49393)
* Set default JSON type resolver if not set * Use JsonTypeInfoResolver.Combine and add tests * Add comment and fix test assertion * Tweak tests
1 parent 0826e5a commit c1e82b8

File tree

4 files changed

+73
-2
lines changed

4 files changed

+73
-2
lines changed

src/Http/Http.Extensions/test/RequestDelegateFactoryTests.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
using Microsoft.AspNetCore.Http.Metadata;
3030
using Microsoft.AspNetCore.Routing.Patterns;
3131
using Microsoft.AspNetCore.Testing;
32+
using Microsoft.DotNet.RemoteExecutor;
3233
using Microsoft.Extensions.DependencyInjection;
3334
using Microsoft.Extensions.Internal;
3435
using Microsoft.Extensions.Logging;
@@ -2903,6 +2904,44 @@ static void TestAction([AsParameters] ParameterListMixedRequiredStringsFromDiffe
29032904
}
29042905
#nullable enable
29052906

2907+
[ConditionalFact]
2908+
[RemoteExecutionSupported]
2909+
public void RequestDelegateFactory_WhenJsonIsReflectionEnabledByDefaultFalse()
2910+
{
2911+
var options = new RemoteInvokeOptions();
2912+
options.RuntimeConfigurationOptions.Add("System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault", false.ToString());
2913+
2914+
using var remoteHandle = RemoteExecutor.Invoke(static () =>
2915+
{
2916+
// Arrange
2917+
var @delegate = (string task) => new Todo();
2918+
2919+
// IsReflectionEnabledByDefault defaults to `false` when `PublishTrimmed=true`. For these scenarios, we
2920+
// expect users to configure JSON source generation as instructed in the `NotSupportedException` message.
2921+
var exception = Assert.Throws<NotSupportedException>(() => RequestDelegateFactory.Create(@delegate));
2922+
Assert.Contains("Microsoft.AspNetCore.Routing.Internal.RequestDelegateFactoryTests+Todo", exception.Message);
2923+
Assert.Contains("JsonSerializableAttribute", exception.Message);
2924+
}, options);
2925+
}
2926+
2927+
[ConditionalFact]
2928+
[RemoteExecutionSupported]
2929+
public void RequestDelegateFactory_WhenJsonIsReflectionEnabledByDefaultTrue()
2930+
{
2931+
var options = new RemoteInvokeOptions();
2932+
options.RuntimeConfigurationOptions.Add("System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault", true.ToString());
2933+
2934+
using var remoteHandle = RemoteExecutor.Invoke(static () =>
2935+
{
2936+
// Arrange
2937+
var @delegate = (string task) => new Todo();
2938+
2939+
// Assert
2940+
var exception = Record.Exception(() => RequestDelegateFactory.Create(@delegate));
2941+
Assert.Null(exception);
2942+
}, options);
2943+
}
2944+
29062945
private DefaultHttpContext CreateHttpContext()
29072946
{
29082947
var responseFeature = new TestHttpResponseFeature();

src/Mvc/Mvc.Core/src/Formatters/SystemTextJsonOutputFormatter.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ public SystemTextJsonOutputFormatter(JsonSerializerOptions jsonSerializerOptions
2323
{
2424
SerializerOptions = jsonSerializerOptions;
2525

26+
// Use JsonTypeInfoResolver.Combine() to produce an empty TypeInfoResolver
27+
jsonSerializerOptions.TypeInfoResolver ??= JsonTypeInfoResolver.Combine();
2628
jsonSerializerOptions.MakeReadOnly();
2729

2830
SupportedEncodings.Add(Encoding.UTF8);

src/Mvc/Mvc.Core/test/Formatters/SystemTextJsonOutputFormatterTest.cs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
using System.Text.Json;
77
using System.Text.Json.Serialization;
88
using System.Text.Json.Serialization.Metadata;
9+
using Microsoft.AspNetCore.Testing;
10+
using Microsoft.DotNet.RemoteExecutor;
911
using Microsoft.Extensions.Primitives;
1012
using Microsoft.Net.Http.Headers;
1113

@@ -254,13 +256,39 @@ private static async IAsyncEnumerable<JsonPerson> GetPeopleAsync()
254256
}
255257

256258
[Fact]
257-
public void WriteResponseBodyAsync_Throws_WhenTypeResolverIsNull()
259+
public void WriteResponseBodyAsync_Works_WhenTypeResolverIsNull()
258260
{
259261
// Arrange
260262
var jsonOptions = new JsonOptions();
261263
jsonOptions.JsonSerializerOptions.TypeInfoResolver = null;
262264

263-
Assert.Throws<InvalidOperationException>(() => SystemTextJsonOutputFormatter.CreateFormatter(jsonOptions));
265+
var stjOutputFormatter = SystemTextJsonOutputFormatter.CreateFormatter(jsonOptions);
266+
Assert.IsAssignableFrom<IJsonTypeInfoResolver>(stjOutputFormatter.SerializerOptions.TypeInfoResolver);
267+
}
268+
269+
[ConditionalTheory]
270+
[RemoteExecutionSupported]
271+
[InlineData(false)]
272+
[InlineData(true)]
273+
public void STJOutputFormatter_UsesEmptyResolver_WhenJsonIsReflectionEnabledByDefaultFalse(bool isReflectionEnabledByDefault)
274+
{
275+
var options = new RemoteInvokeOptions();
276+
options.RuntimeConfigurationOptions.Add("System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault", isReflectionEnabledByDefault.ToString());
277+
278+
using var remoteHandle = RemoteExecutor.Invoke(static () =>
279+
{
280+
// Arrange
281+
var jsonOptions = new JsonOptions();
282+
283+
// Assert
284+
var stjOutputFormatter = SystemTextJsonOutputFormatter.CreateFormatter(jsonOptions);
285+
Assert.IsAssignableFrom<IJsonTypeInfoResolver>(stjOutputFormatter.SerializerOptions.TypeInfoResolver);
286+
// Use default resolver if reflection is enabled instead of empty one
287+
if (JsonSerializer.IsReflectionEnabledByDefault)
288+
{
289+
Assert.IsType<DefaultJsonTypeInfoResolver>(stjOutputFormatter.SerializerOptions.TypeInfoResolver);
290+
}
291+
}, options);
264292
}
265293

266294
private class Person

src/Shared/Json/JsonSerializerExtensions.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ public static bool ShouldUseWith(this JsonTypeInfo jsonTypeInfo, [NotNullWhen(fa
1818

1919
public static JsonTypeInfo GetReadOnlyTypeInfo(this JsonSerializerOptions options, Type type)
2020
{
21+
// Use JsonTypeInfoResolver.Combine() to produce an empty TypeInfoResolver
22+
options.TypeInfoResolver ??= JsonTypeInfoResolver.Combine();
2123
options.MakeReadOnly();
2224
return options.GetTypeInfo(type);
2325
}

0 commit comments

Comments
 (0)