Skip to content

Commit 97a118c

Browse files
authored
Using JsonTypeInfo to read request body in RDF (#45932)
* Using TypeInfo to read request body in RDF * Changing to non-nullable options * Changing to a non-nullable option
1 parent 3a1c02e commit 97a118c

File tree

3 files changed

+27
-32
lines changed

3 files changed

+27
-32
lines changed

src/Http/Http.Extensions/src/RequestDelegateFactory.cs

Lines changed: 25 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ private static RequestDelegateFactoryContext CreateFactoryContext(RequestDelegat
261261

262262
var serviceProvider = options?.ServiceProvider ?? options?.EndpointBuilder?.ApplicationServices ?? EmptyServiceProvider.Instance;
263263
var endpointBuilder = options?.EndpointBuilder ?? new RdfEndpointBuilder(serviceProvider);
264-
var jsonSerializerOptions = serviceProvider.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions;
264+
var jsonSerializerOptions = serviceProvider.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? JsonOptions.DefaultSerializerOptions;
265265

266266
var factoryContext = new RequestDelegateFactoryContext
267267
{
@@ -1000,23 +1000,23 @@ private static Expression AddResponseWritingToMethodCall(Expression methodCall,
10001000
methodCall,
10011001
HttpContextExpr,
10021002
factoryContext.JsonSerializerOptionsExpression,
1003-
Expression.Constant(factoryContext.JsonSerializerOptions?.GetReadOnlyTypeInfo(typeof(object)), typeof(JsonTypeInfo<object>)));
1003+
Expression.Constant(factoryContext.JsonSerializerOptions.GetReadOnlyTypeInfo(typeof(object)), typeof(JsonTypeInfo<object>)));
10041004
}
10051005
else if (returnType == typeof(ValueTask<object>))
10061006
{
10071007
return Expression.Call(ExecuteValueTaskOfObjectMethod,
10081008
methodCall,
10091009
HttpContextExpr,
10101010
factoryContext.JsonSerializerOptionsExpression,
1011-
Expression.Constant(factoryContext.JsonSerializerOptions?.GetReadOnlyTypeInfo(typeof(object)), typeof(JsonTypeInfo<object>)));
1011+
Expression.Constant(factoryContext.JsonSerializerOptions.GetReadOnlyTypeInfo(typeof(object)), typeof(JsonTypeInfo<object>)));
10121012
}
10131013
else if (returnType == typeof(Task<object>))
10141014
{
10151015
return Expression.Call(ExecuteTaskOfObjectMethod,
10161016
methodCall,
10171017
HttpContextExpr,
10181018
factoryContext.JsonSerializerOptionsExpression,
1019-
Expression.Constant(factoryContext.JsonSerializerOptions?.GetReadOnlyTypeInfo(typeof(object)), typeof(JsonTypeInfo<object>)));
1019+
Expression.Constant(factoryContext.JsonSerializerOptions.GetReadOnlyTypeInfo(typeof(object)), typeof(JsonTypeInfo<object>)));
10201020
}
10211021
else if (AwaitableInfo.IsTypeAwaitable(returnType, out _))
10221022
{
@@ -1052,9 +1052,9 @@ private static Expression AddResponseWritingToMethodCall(Expression methodCall,
10521052
}
10531053
else
10541054
{
1055-
var jsonTypeInfo = factoryContext.JsonSerializerOptions?.GetReadOnlyTypeInfo(typeArg);
1055+
var jsonTypeInfo = factoryContext.JsonSerializerOptions.GetReadOnlyTypeInfo(typeArg);
10561056

1057-
if (jsonTypeInfo?.IsPolymorphicSafe() == true)
1057+
if (jsonTypeInfo.IsPolymorphicSafe() == true)
10581058
{
10591059
return Expression.Call(
10601060
ExecuteTaskOfTFastMethod.MakeGenericMethod(typeArg),
@@ -1093,9 +1093,9 @@ private static Expression AddResponseWritingToMethodCall(Expression methodCall,
10931093
}
10941094
else
10951095
{
1096-
var jsonTypeInfo = factoryContext.JsonSerializerOptions?.GetReadOnlyTypeInfo(typeArg);
1096+
var jsonTypeInfo = factoryContext.JsonSerializerOptions.GetReadOnlyTypeInfo(typeArg);
10971097

1098-
if (jsonTypeInfo?.IsPolymorphicSafe() == true)
1098+
if (jsonTypeInfo.IsPolymorphicSafe() == true)
10991099
{
11001100
return Expression.Call(
11011101
ExecuteValueTaskOfTFastMethod.MakeGenericMethod(typeArg),
@@ -1137,9 +1137,9 @@ private static Expression AddResponseWritingToMethodCall(Expression methodCall,
11371137
}
11381138
else
11391139
{
1140-
var jsonTypeInfo = factoryContext.JsonSerializerOptions?.GetReadOnlyTypeInfo(returnType);
1140+
var jsonTypeInfo = factoryContext.JsonSerializerOptions.GetReadOnlyTypeInfo(returnType);
11411141

1142-
if (jsonTypeInfo?.IsPolymorphicSafe() == true)
1142+
if (jsonTypeInfo.IsPolymorphicSafe() == true)
11431143
{
11441144
return Expression.Call(
11451145
JsonResultWriteResponseOfTFastAsyncMethod.MakeGenericMethod(returnType),
@@ -1204,6 +1204,7 @@ private static Expression AddResponseWritingToMethodCall(Expression methodCall,
12041204
Debug.Assert(factoryContext.JsonRequestBodyParameter is not null, "factoryContext.JsonRequestBodyParameter is null for a JSON body.");
12051205

12061206
var bodyType = factoryContext.JsonRequestBodyParameter.ParameterType;
1207+
var jsonTypeInfo = factoryContext.JsonSerializerOptions.GetReadOnlyTypeInfo(bodyType);
12071208
var parameterTypeName = TypeNameHelper.GetTypeDisplayName(factoryContext.JsonRequestBodyParameter.ParameterType, fullName: false);
12081209
var parameterName = factoryContext.JsonRequestBodyParameter.Name;
12091210

@@ -1236,7 +1237,7 @@ private static Expression AddResponseWritingToMethodCall(Expression methodCall,
12361237
parameterName,
12371238
factoryContext.AllowEmptyRequestBody,
12381239
factoryContext.ThrowOnBadRequest,
1239-
factoryContext.JsonSerializerOptions);
1240+
jsonTypeInfo);
12401241

12411242
if (!successful)
12421243
{
@@ -1261,7 +1262,7 @@ private static Expression AddResponseWritingToMethodCall(Expression methodCall,
12611262
parameterName,
12621263
factoryContext.AllowEmptyRequestBody,
12631264
factoryContext.ThrowOnBadRequest,
1264-
factoryContext.JsonSerializerOptions);
1265+
jsonTypeInfo);
12651266

12661267
if (!successful)
12671268
{
@@ -1279,7 +1280,7 @@ private static Expression AddResponseWritingToMethodCall(Expression methodCall,
12791280
string parameterName,
12801281
bool allowEmptyRequestBody,
12811282
bool throwOnBadRequest,
1282-
JsonSerializerOptions? jsonSerializerOptions)
1283+
JsonTypeInfo jsonTypeInfo)
12831284
{
12841285
object? defaultBodyValue = null;
12851286

@@ -1301,7 +1302,7 @@ private static Expression AddResponseWritingToMethodCall(Expression methodCall,
13011302
}
13021303
try
13031304
{
1304-
bodyValue = await httpContext.Request.ReadFromJsonAsync(bodyType, jsonSerializerOptions);
1305+
bodyValue = await httpContext.Request.ReadFromJsonAsync(jsonTypeInfo);
13051306
}
13061307
catch (IOException ex)
13071308
{
@@ -2091,9 +2092,9 @@ private static MemberInfo GetMemberInfo<T>(Expression<T> expr)
20912092
// if necessary and restart the cycle until we've reached a terminal state (unknown type).
20922093
// We currently don't handle Task<unknown> or ValueTask<unknown>. We can support this later if this
20932094
// ends up being a common scenario.
2094-
private static Task ExecuteValueTaskOfObject(ValueTask<object> valueTask, HttpContext httpContext, JsonSerializerOptions? options, JsonTypeInfo<object>? jsonTypeInfo)
2095+
private static Task ExecuteValueTaskOfObject(ValueTask<object> valueTask, HttpContext httpContext, JsonSerializerOptions options, JsonTypeInfo<object> jsonTypeInfo)
20952096
{
2096-
static async Task ExecuteAwaited(ValueTask<object> valueTask, HttpContext httpContext, JsonSerializerOptions? options, JsonTypeInfo<object>? jsonTypeInfo)
2097+
static async Task ExecuteAwaited(ValueTask<object> valueTask, HttpContext httpContext, JsonSerializerOptions options, JsonTypeInfo<object> jsonTypeInfo)
20972098
{
20982099
await ExecuteAwaitedReturn(await valueTask, httpContext, options, jsonTypeInfo);
20992100
}
@@ -2106,9 +2107,9 @@ static async Task ExecuteAwaited(ValueTask<object> valueTask, HttpContext httpCo
21062107
return ExecuteAwaited(valueTask, httpContext, options, jsonTypeInfo);
21072108
}
21082109

2109-
private static Task ExecuteTaskOfObject(Task<object> task, HttpContext httpContext, JsonSerializerOptions? options, JsonTypeInfo<object>? jsonTypeInfo)
2110+
private static Task ExecuteTaskOfObject(Task<object> task, HttpContext httpContext, JsonSerializerOptions options, JsonTypeInfo<object> jsonTypeInfo)
21102111
{
2111-
static async Task ExecuteAwaited(Task<object> task, HttpContext httpContext, JsonSerializerOptions? options, JsonTypeInfo<object>? jsonTypeInfo)
2112+
static async Task ExecuteAwaited(Task<object> task, HttpContext httpContext, JsonSerializerOptions options, JsonTypeInfo<object> jsonTypeInfo)
21122113
{
21132114
await ExecuteAwaitedReturn(await task, httpContext, options, jsonTypeInfo);
21142115
}
@@ -2121,7 +2122,7 @@ static async Task ExecuteAwaited(Task<object> task, HttpContext httpContext, Jso
21212122
return ExecuteAwaited(task, httpContext, options, jsonTypeInfo);
21222123
}
21232124

2124-
private static Task ExecuteAwaitedReturn(object obj, HttpContext httpContext, JsonSerializerOptions? options, JsonTypeInfo<object>? jsonTypeInfo)
2125+
private static Task ExecuteAwaitedReturn(object obj, HttpContext httpContext, JsonSerializerOptions options, JsonTypeInfo<object> jsonTypeInfo)
21252126
{
21262127
// Terminal built ins
21272128
if (obj is IResult result)
@@ -2157,11 +2158,11 @@ static async Task ExecuteAwaited(Task<T> task, HttpContext httpContext, JsonType
21572158
return ExecuteAwaited(task, httpContext, jsonTypeInfo);
21582159
}
21592160

2160-
private static Task ExecuteTaskOfT<T>(Task<T> task, HttpContext httpContext, JsonSerializerOptions? options, JsonTypeInfo<T> jsonTypeInfo)
2161+
private static Task ExecuteTaskOfT<T>(Task<T> task, HttpContext httpContext, JsonSerializerOptions options, JsonTypeInfo<T> jsonTypeInfo)
21612162
{
21622163
EnsureRequestTaskNotNull(task);
21632164

2164-
static async Task ExecuteAwaited(Task<T> task, HttpContext httpContext, JsonSerializerOptions? options, JsonTypeInfo<T> jsonTypeInfo)
2165+
static async Task ExecuteAwaited(Task<T> task, HttpContext httpContext, JsonSerializerOptions options, JsonTypeInfo<T> jsonTypeInfo)
21652166
{
21662167
await WriteJsonResponse(httpContext.Response, await task, options, jsonTypeInfo);
21672168
}
@@ -2262,9 +2263,9 @@ static async Task ExecuteAwaited(ValueTask<T> task, HttpContext httpContext, Jso
22622263
return ExecuteAwaited(task, httpContext, jsonTypeInfo);
22632264
}
22642265

2265-
private static Task ExecuteValueTaskOfT<T>(ValueTask<T> task, HttpContext httpContext, JsonSerializerOptions? options, JsonTypeInfo<T> jsonTypeInfo)
2266+
private static Task ExecuteValueTaskOfT<T>(ValueTask<T> task, HttpContext httpContext, JsonSerializerOptions options, JsonTypeInfo<T> jsonTypeInfo)
22662267
{
2267-
static async Task ExecuteAwaited(ValueTask<T> task, HttpContext httpContext, JsonSerializerOptions? options, JsonTypeInfo<T> jsonTypeInfo)
2268+
static async Task ExecuteAwaited(ValueTask<T> task, HttpContext httpContext, JsonSerializerOptions options, JsonTypeInfo<T> jsonTypeInfo)
22682269
{
22692270
await WriteJsonResponse(httpContext.Response, await task, options, jsonTypeInfo);
22702271
}
@@ -2326,16 +2327,10 @@ private static async Task ExecuteResultWriteResponse(IResult? result, HttpContex
23262327
private static Task WriteJsonResponseFast<T>(HttpResponse response, T value, JsonTypeInfo<T> jsonTypeInfo)
23272328
=> HttpResponseJsonExtensions.WriteAsJsonAsync(response, value, jsonTypeInfo, default);
23282329

2329-
private static Task WriteJsonResponse<T>(HttpResponse response, T? value, JsonSerializerOptions? options, JsonTypeInfo<T>? jsonTypeInfo)
2330+
private static Task WriteJsonResponse<T>(HttpResponse response, T? value, JsonSerializerOptions options, JsonTypeInfo<T> jsonTypeInfo)
23302331
{
23312332
var runtimeType = value?.GetType();
23322333

2333-
// Edge case but possible if the RequestDelegateFactoryOptions.ServiceProvider and
2334-
// RequestDelegateFactoryOptions.EndpointBuilder.ServiceProvider are null
2335-
// In this situation both options and jsonTypeInfo are null.
2336-
options ??= response.HttpContext.RequestServices.GetService<IOptions<JsonOptions>>()?.Value.SerializerOptions ?? JsonOptions.DefaultSerializerOptions;
2337-
jsonTypeInfo ??= (JsonTypeInfo<T>)options.GetTypeInfo(typeof(T));
2338-
23392334
if (runtimeType is null || jsonTypeInfo.Type == runtimeType || jsonTypeInfo.IsPolymorphicSafe())
23402335
{
23412336
// In this case the polymorphism is not

src/Http/Http.Extensions/src/RequestDelegateFactoryContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,6 @@ internal sealed class RequestDelegateFactoryContext
5858
public List<ParameterInfo> Parameters { get; set; } = new();
5959

6060
// Grab these options upfront to avoid the per request DI scope that would be made otherwise to get the options when writing Json
61-
public JsonSerializerOptions? JsonSerializerOptions { get; set; }
61+
public required JsonSerializerOptions JsonSerializerOptions { get; set; }
6262
public required Expression JsonSerializerOptionsExpression { get; set; }
6363
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1881,7 +1881,7 @@ public async Task RequestDelegatePopulatesFromBodyParameter(Delegate action)
18811881
});
18821882
httpContext.RequestServices = mock.Object;
18831883

1884-
var factoryResult = RequestDelegateFactory.Create(action);
1884+
var factoryResult = RequestDelegateFactory.Create(action, new RequestDelegateFactoryOptions() { ServiceProvider = mock.Object });
18851885
var requestDelegate = factoryResult.RequestDelegate;
18861886

18871887
await requestDelegate(httpContext);

0 commit comments

Comments
 (0)