@@ -23,6 +23,10 @@ public static class HttpRequestJsonExtensions
23
23
"Use the overload that takes a JsonTypeInfo or JsonSerializerContext, or make sure all of the required types are preserved." ;
24
24
private const string RequiresDynamicCodeMessage = "JSON serialization and deserialization might require types that cannot be statically analyzed and need runtime code generation. " +
25
25
"Use the overload that takes a JsonTypeInfo or JsonSerializerContext for native AOT applications." ;
26
+ // Fallback to the stream-based overloads for JsonSerializer.DeserializeAsync
27
+ // This is to give users with custom JsonConverter implementations the chance to update their
28
+ // converters to support ReadOnlySequence<T> if needed while still keeping their apps working.
29
+ private static readonly bool _useStreamJsonOverload = AppContext . TryGetSwitch ( "Microsoft.AspNetCore.UseStreamBasedJsonParsing" , out var isEnabled ) && isEnabled ;
26
30
27
31
/// <summary>
28
32
/// Read JSON from the request and deserialize to the specified type.
@@ -68,15 +72,33 @@ public static class HttpRequestJsonExtensions
68
72
options ??= ResolveSerializerOptions ( request . HttpContext ) ;
69
73
70
74
var encoding = GetEncodingFromCharset ( charset ) ;
71
- var ( inputStream , usesTranscodingStream ) = GetInputStream ( request . HttpContext , encoding ) ;
75
+ Stream ? inputStream = null ;
76
+ ValueTask < TValue ? > deserializeTask ;
72
77
73
78
try
74
79
{
75
- return await JsonSerializer . DeserializeAsync < TValue > ( inputStream , options , cancellationToken ) ;
80
+ if ( encoding == null || encoding . CodePage == Encoding . UTF8 . CodePage )
81
+ {
82
+ if ( _useStreamJsonOverload )
83
+ {
84
+ deserializeTask = JsonSerializer . DeserializeAsync < TValue > ( request . Body , options , cancellationToken ) ;
85
+ }
86
+ else
87
+ {
88
+ deserializeTask = JsonSerializer . DeserializeAsync < TValue > ( request . BodyReader , options , cancellationToken ) ;
89
+ }
90
+ }
91
+ else
92
+ {
93
+ inputStream = Encoding . CreateTranscodingStream ( request . Body , encoding , Encoding . UTF8 , leaveOpen : true ) ;
94
+ deserializeTask = JsonSerializer . DeserializeAsync < TValue > ( inputStream , options , cancellationToken ) ;
95
+ }
96
+
97
+ return await deserializeTask ;
76
98
}
77
99
finally
78
100
{
79
- if ( usesTranscodingStream )
101
+ if ( inputStream is not null )
80
102
{
81
103
await inputStream . DisposeAsync ( ) ;
82
104
}
@@ -106,15 +128,33 @@ public static class HttpRequestJsonExtensions
106
128
}
107
129
108
130
var encoding = GetEncodingFromCharset ( charset ) ;
109
- var ( inputStream , usesTranscodingStream ) = GetInputStream ( request . HttpContext , encoding ) ;
131
+ Stream ? inputStream = null ;
132
+ ValueTask < TValue ? > deserializeTask ;
110
133
111
134
try
112
135
{
113
- return await JsonSerializer . DeserializeAsync ( inputStream , jsonTypeInfo , cancellationToken ) ;
136
+ if ( encoding == null || encoding . CodePage == Encoding . UTF8 . CodePage )
137
+ {
138
+ if ( _useStreamJsonOverload )
139
+ {
140
+ deserializeTask = JsonSerializer . DeserializeAsync ( request . Body , jsonTypeInfo , cancellationToken ) ;
141
+ }
142
+ else
143
+ {
144
+ deserializeTask = JsonSerializer . DeserializeAsync ( request . BodyReader , jsonTypeInfo , cancellationToken ) ;
145
+ }
146
+ }
147
+ else
148
+ {
149
+ inputStream = Encoding . CreateTranscodingStream ( request . Body , encoding , Encoding . UTF8 , leaveOpen : true ) ;
150
+ deserializeTask = JsonSerializer . DeserializeAsync ( inputStream , jsonTypeInfo , cancellationToken ) ;
151
+ }
152
+
153
+ return await deserializeTask ;
114
154
}
115
155
finally
116
156
{
117
- if ( usesTranscodingStream )
157
+ if ( inputStream is not null )
118
158
{
119
159
await inputStream . DisposeAsync ( ) ;
120
160
}
@@ -144,15 +184,33 @@ public static class HttpRequestJsonExtensions
144
184
}
145
185
146
186
var encoding = GetEncodingFromCharset ( charset ) ;
147
- var ( inputStream , usesTranscodingStream ) = GetInputStream ( request . HttpContext , encoding ) ;
187
+ Stream ? inputStream = null ;
188
+ ValueTask < object ? > deserializeTask ;
148
189
149
190
try
150
191
{
151
- return await JsonSerializer . DeserializeAsync ( inputStream , jsonTypeInfo , cancellationToken ) ;
192
+ if ( encoding == null || encoding . CodePage == Encoding . UTF8 . CodePage )
193
+ {
194
+ if ( _useStreamJsonOverload )
195
+ {
196
+ deserializeTask = JsonSerializer . DeserializeAsync ( request . Body , jsonTypeInfo , cancellationToken ) ;
197
+ }
198
+ else
199
+ {
200
+ deserializeTask = JsonSerializer . DeserializeAsync ( request . BodyReader , jsonTypeInfo , cancellationToken ) ;
201
+ }
202
+ }
203
+ else
204
+ {
205
+ inputStream = Encoding . CreateTranscodingStream ( request . Body , encoding , Encoding . UTF8 , leaveOpen : true ) ;
206
+ deserializeTask = JsonSerializer . DeserializeAsync ( inputStream , jsonTypeInfo , cancellationToken ) ;
207
+ }
208
+
209
+ return await deserializeTask ;
152
210
}
153
211
finally
154
212
{
155
- if ( usesTranscodingStream )
213
+ if ( inputStream is not null )
156
214
{
157
215
await inputStream . DisposeAsync ( ) ;
158
216
}
@@ -206,15 +264,33 @@ public static class HttpRequestJsonExtensions
206
264
options ??= ResolveSerializerOptions ( request . HttpContext ) ;
207
265
208
266
var encoding = GetEncodingFromCharset ( charset ) ;
209
- var ( inputStream , usesTranscodingStream ) = GetInputStream ( request . HttpContext , encoding ) ;
267
+ Stream ? inputStream = null ;
268
+ ValueTask < object ? > deserializeTask ;
210
269
211
270
try
212
271
{
213
- return await JsonSerializer . DeserializeAsync ( inputStream , type , options , cancellationToken ) ;
272
+ if ( encoding == null || encoding . CodePage == Encoding . UTF8 . CodePage )
273
+ {
274
+ if ( _useStreamJsonOverload )
275
+ {
276
+ deserializeTask = JsonSerializer . DeserializeAsync ( request . Body , type , options , cancellationToken ) ;
277
+ }
278
+ else
279
+ {
280
+ deserializeTask = JsonSerializer . DeserializeAsync ( request . BodyReader , type , options , cancellationToken ) ;
281
+ }
282
+ }
283
+ else
284
+ {
285
+ inputStream = Encoding . CreateTranscodingStream ( request . Body , encoding , Encoding . UTF8 , leaveOpen : true ) ;
286
+ deserializeTask = JsonSerializer . DeserializeAsync ( inputStream , type , options , cancellationToken ) ;
287
+ }
288
+
289
+ return await deserializeTask ;
214
290
}
215
291
finally
216
292
{
217
- if ( usesTranscodingStream )
293
+ if ( inputStream is not null )
218
294
{
219
295
await inputStream . DisposeAsync ( ) ;
220
296
}
@@ -248,15 +324,33 @@ public static class HttpRequestJsonExtensions
248
324
}
249
325
250
326
var encoding = GetEncodingFromCharset ( charset ) ;
251
- var ( inputStream , usesTranscodingStream ) = GetInputStream ( request . HttpContext , encoding ) ;
327
+ Stream ? inputStream = null ;
328
+ ValueTask < object ? > deserializeTask ;
252
329
253
330
try
254
331
{
255
- return await JsonSerializer . DeserializeAsync ( inputStream , type , context , cancellationToken ) ;
332
+ if ( encoding == null || encoding . CodePage == Encoding . UTF8 . CodePage )
333
+ {
334
+ if ( _useStreamJsonOverload )
335
+ {
336
+ deserializeTask = JsonSerializer . DeserializeAsync ( request . Body , type , context , cancellationToken ) ;
337
+ }
338
+ else
339
+ {
340
+ deserializeTask = JsonSerializer . DeserializeAsync ( request . BodyReader , type , context , cancellationToken ) ;
341
+ }
342
+ }
343
+ else
344
+ {
345
+ inputStream = Encoding . CreateTranscodingStream ( request . Body , encoding , Encoding . UTF8 , leaveOpen : true ) ;
346
+ deserializeTask = JsonSerializer . DeserializeAsync ( inputStream , type , context , cancellationToken ) ;
347
+ }
348
+
349
+ return await deserializeTask ;
256
350
}
257
351
finally
258
352
{
259
- if ( usesTranscodingStream )
353
+ if ( inputStream is not null )
260
354
{
261
355
await inputStream . DisposeAsync ( ) ;
262
356
}
@@ -312,17 +406,6 @@ private static void ThrowContentTypeError(HttpRequest request)
312
406
throw new InvalidOperationException ( $ "Unable to read the request as JSON because the request content type '{ request . ContentType } ' is not a known JSON content type.") ;
313
407
}
314
408
315
- private static ( Stream inputStream , bool usesTranscodingStream ) GetInputStream ( HttpContext httpContext , Encoding ? encoding )
316
- {
317
- if ( encoding == null || encoding . CodePage == Encoding . UTF8 . CodePage )
318
- {
319
- return ( httpContext . Request . Body , false ) ;
320
- }
321
-
322
- var inputStream = Encoding . CreateTranscodingStream ( httpContext . Request . Body , encoding , Encoding . UTF8 , leaveOpen : true ) ;
323
- return ( inputStream , true ) ;
324
- }
325
-
326
409
private static Encoding ? GetEncodingFromCharset ( StringSegment charset )
327
410
{
328
411
if ( charset . Equals ( "utf-8" , StringComparison . OrdinalIgnoreCase ) )
0 commit comments