Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changing reflection-based json configuration #46303

Closed
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
c2945c2
Mvc JsonOptions updates
brunolins16 Jan 26, 2023
26be191
Http JsonOptions updates
brunolins16 Jan 26, 2023
5754198
Merge remote-tracking branch 'upstream/main' into brunolins16/aot/jso…
brunolins16 Jan 26, 2023
b6fadd9
Bad merge
brunolins16 Jan 26, 2023
d31a8e2
fix end line
brunolins16 Jan 26, 2023
cc71ed2
Updating RUC/RDC message
brunolins16 Jan 26, 2023
f47a076
Add call to AddRouting
brunolins16 Jan 27, 2023
d227b30
Fix suppressions
brunolins16 Jan 27, 2023
df207a0
Remove empty spaces
brunolins16 Jan 27, 2023
9e97941
Moving to Routing
brunolins16 Jan 28, 2023
eb64558
Revert "Moving to Routing"
brunolins16 Jan 30, 2023
5b175ed
Adding AddDefaultJsonOptions API
brunolins16 Jan 30, 2023
45b2282
Fixing unit tests
brunolins16 Jan 31, 2023
bb507c7
Trying a different approach
brunolins16 Jan 31, 2023
f52c535
Changing MVC
brunolins16 Jan 31, 2023
4298afc
Clean up
brunolins16 Feb 1, 2023
785d91e
Merge remote-tracking branch 'upstream/main' into brunolins16/aot/jso…
brunolins16 Feb 1, 2023
60f9ade
Clean up & Fix/Add unit tests
brunolins16 Feb 1, 2023
df95bd3
Merge remote-tracking branch 'upstream/main' into brunolins16/aot/jso…
brunolins16 Feb 1, 2023
b9ec639
Merge remote-tracking branch 'upstream/main' into brunolins16/aot/jso…
brunolins16 Feb 1, 2023
e186cc4
Include TrimmingAppContextSwitches
brunolins16 Feb 1, 2023
de59eb6
Create Microsoft.AspNetCore.Http.Results.WarningSuppressions.xml
brunolins16 Feb 2, 2023
2f2869a
Clean up
brunolins16 Feb 2, 2023
c45a8f3
Merging changes from #46225
brunolins16 Feb 3, 2023
de450ae
Locking JsonOptions :(
brunolins16 Feb 3, 2023
b3b7cef
Fixing linker suppressions
brunolins16 Feb 3, 2023
5715427
Simplify and using PostConfigureOptions
brunolins16 Feb 6, 2023
67faf3f
Clean up
brunolins16 Feb 6, 2023
ede0a7e
Fixing unit tests
brunolins16 Feb 7, 2023
a7ae048
Fix unit test
brunolins16 Feb 8, 2023
422cb93
Trying fix SignalR
brunolins16 Feb 8, 2023
8edd3b7
Revert "Trying fix SignalR"
brunolins16 Feb 8, 2023
cde347e
Update Startup.cs
brunolins16 Feb 8, 2023
a18c01b
Changing to a IOptionsFactory
brunolins16 Feb 8, 2023
d45eea5
Move to singleton
brunolins16 Feb 8, 2023
298f9e5
Trying another approach
brunolins16 Feb 10, 2023
fe7434a
Clean up
brunolins16 Feb 10, 2023
580d227
More clean up
brunolins16 Feb 10, 2023
6dfe6c1
More clean up
brunolins16 Feb 10, 2023
e7e44a5
Merge branch 'brunolins16/aot/json/reflection-json-options' of https:…
brunolins16 Feb 10, 2023
afd6762
More clean up
brunolins16 Feb 10, 2023
c251205
Fix mvc
brunolins16 Feb 10, 2023
19c9465
Merge remote-tracking branch 'upstream/main' into brunolins16/aot/jso…
brunolins16 Feb 15, 2023
c01dc5f
More clean up
brunolins16 Feb 15, 2023
473e2eb
Merge remote-tracking branch 'upstream/main' into brunolins16/aot/jso…
brunolins16 Feb 15, 2023
9ceb581
More clean up
brunolins16 Feb 15, 2023
5bfb9e1
Cleaning up JsonHttpResult
brunolins16 Feb 15, 2023
aaed7dc
More clean up
brunolins16 Feb 16, 2023
79d5117
More clean up
brunolins16 Feb 16, 2023
7637bdd
Moving changes to #46716
brunolins16 Feb 16, 2023
14a5aac
Fix comment
brunolins16 Feb 17, 2023
35556da
Removing public api
brunolins16 Feb 24, 2023
08e1545
Merge remote-tracking branch 'upstream/main' into brunolins16/aot/jso…
brunolins16 Feb 24, 2023
29fd8cd
PR feedback updates
brunolins16 Feb 24, 2023
8c830d1
Removing extra usings
brunolins16 Feb 24, 2023
2c5caff
Fix tests
brunolins16 Feb 28, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,18 @@
using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization.Metadata;
using Microsoft.AspNetCore.Http.Json;
using Microsoft.AspNetCore.Mvc;

namespace Microsoft.AspNetCore.Http.Abstractions.Tests;

public class ProblemDetailsJsonConverterTest
{
private static JsonSerializerOptions JsonSerializerOptions => new JsonOptions().SerializerOptions;
private static JsonSerializerOptions JsonSerializerOptions => new JsonSerializerOptions(new JsonOptions().SerializerOptions)
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
};

[Fact]
public void Read_ThrowsIfJsonIsIncomplete()
Expand Down
4 changes: 3 additions & 1 deletion src/Http/Http.Extensions/src/HttpJsonServiceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.AspNetCore.Http.Json;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;

namespace Microsoft.Extensions.DependencyInjection;

Expand All @@ -22,7 +24,7 @@ public static class HttpJsonServiceExtensions
/// <returns>The modified <see cref="IServiceCollection"/>.</returns>
public static IServiceCollection ConfigureHttpJsonOptions(this IServiceCollection services, Action<JsonOptions> configureOptions)
{
services.Configure<JsonOptions>(configureOptions);
services.Configure(configureOptions);
return services;
}
}
104 changes: 28 additions & 76 deletions src/Http/Http.Extensions/src/HttpRequestJsonExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ namespace Microsoft.AspNetCore.Http;
/// </summary>
public static class HttpRequestJsonExtensions
{
private const string RequiresUnreferencedCodeMessage = "JSON serialization and deserialization might require types that cannot be statically analyzed. " +
"Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved.";
private const string RequiresDynamicCodeMessage = "JSON serialization and deserialization might require types that cannot be statically analyzed and need runtime code generation. " +
"Use the overload that takes a JsonTypeInfo or JsonSerializerContext for native AOT applications.";

/// <summary>
/// Read JSON from the request and deserialize to the specified type.
Expand All @@ -36,10 +32,7 @@ public static class HttpRequestJsonExtensions
this HttpRequest request,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);

var options = ResolveSerializerOptions(request.HttpContext);
return request.ReadFromJsonAsync(jsonTypeInfo: (JsonTypeInfo<TValue>)options.GetTypeInfo(typeof(TValue)), cancellationToken);
return request.ReadFromJsonAsync<TValue>(options: null, cancellationToken);
}

/// <summary>
Expand All @@ -51,36 +44,17 @@ public static class HttpRequestJsonExtensions
/// <param name="options">The serializer options to use when deserializing the content.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> used to cancel the operation.</param>
/// <returns>The task object representing the asynchronous operation.</returns>
[RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)]
Copy link
Member

@eiriktsarpalis eiriktsarpalis Feb 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recall @eerhardt mentioning that public ASP.NET APIs should preserve RUC/RDC annotations even if any reflection code is hidden behind a feature flag. Presumably library authors would still need to be warned if calling into these APIs.

[RequiresDynamicCode(RequiresDynamicCodeMessage)]
public static async ValueTask<TValue?> ReadFromJsonAsync<TValue>(
public static ValueTask<TValue?> ReadFromJsonAsync<TValue>(
this HttpRequest request,
JsonSerializerOptions? options,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);

if (!request.HasJsonContentType(out var charset))
{
ThrowContentTypeError(request);
}

options ??= ResolveSerializerOptions(request.HttpContext);
options.EnsureConfigured();

var encoding = GetEncodingFromCharset(charset);
var (inputStream, usesTranscodingStream) = GetInputStream(request.HttpContext, encoding);

try
{
return await JsonSerializer.DeserializeAsync<TValue>(inputStream, options, cancellationToken);
}
finally
{
if (usesTranscodingStream)
{
await inputStream.DisposeAsync();
}
}
return ReadFromJsonAsync(request, (JsonTypeInfo<TValue>)options.GetTypeInfo(typeof(TValue)), cancellationToken);
}

/// <summary>
Expand Down Expand Up @@ -122,41 +96,19 @@ public static class HttpRequestJsonExtensions
}

/// <summary>
/// Read JSON from the request and deserialize to object type.
/// Read JSON from the request and deserialize to the specified type.
/// If the request's content-type is not a known JSON type then an error will be thrown.
/// </summary>
/// <param name="request">The request to read from.</param>
/// <param name="jsonTypeInfo">Metadata about the type to convert.</param>
/// <param name="type">The type of object to read.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> used to cancel the operation.</param>
/// <returns>The deserialized value.</returns>
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
public static async ValueTask<object?> ReadFromJsonAsync(
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
/// <returns>The task object representing the asynchronous operation.</returns>
public static ValueTask<object?> ReadFromJsonAsync(
this HttpRequest request,
JsonTypeInfo jsonTypeInfo,
Type type,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);

if (!request.HasJsonContentType(out var charset))
{
ThrowContentTypeError(request);
}

var encoding = GetEncodingFromCharset(charset);
var (inputStream, usesTranscodingStream) = GetInputStream(request.HttpContext, encoding);

try
{
return await JsonSerializer.DeserializeAsync(inputStream, jsonTypeInfo, cancellationToken);
}
finally
{
if (usesTranscodingStream)
{
await inputStream.DisposeAsync();
}
}
return request.ReadFromJsonAsync(type, options: null, cancellationToken);
}

/// <summary>
Expand All @@ -165,17 +117,22 @@ public static class HttpRequestJsonExtensions
/// </summary>
/// <param name="request">The request to read from.</param>
/// <param name="type">The type of object to read.</param>
/// <param name="options">The serializer options use when deserializing the content.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> used to cancel the operation.</param>
/// <returns>The task object representing the asynchronous operation.</returns>
public static ValueTask<object?> ReadFromJsonAsync(
this HttpRequest request,
Type type,
JsonSerializerOptions? options,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);
ArgumentNullException.ThrowIfNull(type);

var options = ResolveSerializerOptions(request.HttpContext);
return request.ReadFromJsonAsync(jsonTypeInfo: options.GetTypeInfo(type), cancellationToken);
options ??= ResolveSerializerOptions(request.HttpContext);
options.EnsureConfigured();

return ReadFromJsonAsync(request, options.GetTypeInfo(type), cancellationToken);
}

/// <summary>
Expand All @@ -184,33 +141,32 @@ public static class HttpRequestJsonExtensions
/// </summary>
/// <param name="request">The request to read from.</param>
/// <param name="type">The type of object to read.</param>
/// <param name="options">The serializer options use when deserializing the content.</param>
/// <param name="context">A metadata provider for serializable types.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> used to cancel the operation.</param>
/// <returns>The task object representing the asynchronous operation.</returns>
[RequiresUnreferencedCode(RequiresUnreferencedCodeMessage)]
[RequiresDynamicCode(RequiresDynamicCodeMessage)]
/// <returns>The deserialized value.</returns>
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
public static async ValueTask<object?> ReadFromJsonAsync(
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
this HttpRequest request,
Type type,
JsonSerializerOptions? options,
JsonSerializerContext context,
brunolins16 marked this conversation as resolved.
Show resolved Hide resolved
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);
ArgumentNullException.ThrowIfNull(type);
ArgumentNullException.ThrowIfNull(context);

if (!request.HasJsonContentType(out var charset))
{
ThrowContentTypeError(request);
}

options ??= ResolveSerializerOptions(request.HttpContext);

var encoding = GetEncodingFromCharset(charset);
var (inputStream, usesTranscodingStream) = GetInputStream(request.HttpContext, encoding);

try
{
return await JsonSerializer.DeserializeAsync(inputStream, type, options, cancellationToken);
return await JsonSerializer.DeserializeAsync(inputStream, type, context, cancellationToken);
}
finally
{
Expand All @@ -222,25 +178,21 @@ public static class HttpRequestJsonExtensions
}

/// <summary>
/// Read JSON from the request and deserialize to the specified type.
/// Read JSON from the request and deserialize to object type.
/// If the request's content-type is not a known JSON type then an error will be thrown.
/// </summary>
/// <param name="request">The request to read from.</param>
/// <param name="type">The type of object to read.</param>
/// <param name="context">A metadata provider for serializable types.</param>
/// <param name="jsonTypeInfo">Metadata about the type to convert.</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> used to cancel the operation.</param>
/// <returns>The deserialized value.</returns>
#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
public static async ValueTask<object?> ReadFromJsonAsync(
#pragma warning restore RS0026 // Do not add multiple public overloads with optional parameters
this HttpRequest request,
Type type,
JsonSerializerContext context,
JsonTypeInfo jsonTypeInfo,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);
ArgumentNullException.ThrowIfNull(type);
ArgumentNullException.ThrowIfNull(context);

if (!request.HasJsonContentType(out var charset))
{
Expand All @@ -252,7 +204,7 @@ public static class HttpRequestJsonExtensions

try
{
return await JsonSerializer.DeserializeAsync(inputStream, type, context, cancellationToken);
return await JsonSerializer.DeserializeAsync(inputStream, jsonTypeInfo, cancellationToken);
}
finally
{
Expand Down
Loading