5
5
using System . Diagnostics . CodeAnalysis ;
6
6
using System . Linq ;
7
7
using System . Text ;
8
+ using System . Text . Json ;
9
+ using System . Text . Json . Serialization ;
8
10
using Microsoft . AspNetCore . Builder ;
9
11
using Microsoft . AspNetCore . Diagnostics . RazorViews ;
10
12
using Microsoft . AspNetCore . Hosting ;
11
13
using Microsoft . AspNetCore . Http ;
12
14
using Microsoft . AspNetCore . Http . Features ;
15
+ using Microsoft . AspNetCore . Http . Json ;
13
16
using Microsoft . AspNetCore . Mvc ;
14
17
using Microsoft . AspNetCore . Routing ;
15
18
using Microsoft . Extensions . FileProviders ;
@@ -34,6 +37,7 @@ internal class DeveloperExceptionPageMiddlewareImpl
34
37
private readonly ExceptionDetailsProvider _exceptionDetailsProvider ;
35
38
private readonly Func < ErrorContext , Task > _exceptionHandler ;
36
39
private static readonly MediaTypeHeaderValue _textHtmlMediaType = new MediaTypeHeaderValue ( "text/html" ) ;
40
+ private readonly ExtensionsExceptionJsonContext _serializationContext ;
37
41
private readonly IProblemDetailsService ? _problemDetailsService ;
38
42
39
43
/// <summary>
@@ -45,6 +49,7 @@ internal class DeveloperExceptionPageMiddlewareImpl
45
49
/// <param name="hostingEnvironment"></param>
46
50
/// <param name="diagnosticSource">The <see cref="DiagnosticSource"/> used for writing diagnostic messages.</param>
47
51
/// <param name="filters">The list of registered <see cref="IDeveloperPageExceptionFilter"/>.</param>
52
+ /// <param name="jsonOptions">The <see cref="JsonOptions"/> used for serialization.</param>
48
53
/// <param name="problemDetailsService">The <see cref="IProblemDetailsService"/> used for writing <see cref="ProblemDetails"/> messages.</param>
49
54
public DeveloperExceptionPageMiddlewareImpl (
50
55
RequestDelegate next ,
@@ -53,6 +58,7 @@ public DeveloperExceptionPageMiddlewareImpl(
53
58
IWebHostEnvironment hostingEnvironment ,
54
59
DiagnosticSource diagnosticSource ,
55
60
IEnumerable < IDeveloperPageExceptionFilter > filters ,
61
+ IOptions < JsonOptions > ? jsonOptions = null ,
56
62
IProblemDetailsService ? problemDetailsService = null )
57
63
{
58
64
if ( next == null )
@@ -77,15 +83,22 @@ public DeveloperExceptionPageMiddlewareImpl(
77
83
_diagnosticSource = diagnosticSource ;
78
84
_exceptionDetailsProvider = new ExceptionDetailsProvider ( _fileProvider , _logger , _options . SourceCodeLineCount ) ;
79
85
_exceptionHandler = DisplayException ;
86
+ _serializationContext = CreateSerializationContext ( jsonOptions ? . Value ) ;
80
87
_problemDetailsService = problemDetailsService ;
81
-
82
88
foreach ( var filter in filters . Reverse ( ) )
83
89
{
84
90
var nextFilter = _exceptionHandler ;
85
91
_exceptionHandler = errorContext => filter . HandleExceptionAsync ( errorContext , nextFilter ) ;
86
92
}
87
93
}
88
94
95
+ private static ExtensionsExceptionJsonContext CreateSerializationContext ( JsonOptions ? jsonOptions )
96
+ {
97
+ // Create context from configured options to get settings such as PropertyNamePolicy and DictionaryKeyPolicy.
98
+ jsonOptions ??= new JsonOptions ( ) ;
99
+ return new ExtensionsExceptionJsonContext ( new JsonSerializerOptions ( jsonOptions . SerializerOptions ) ) ;
100
+ }
101
+
89
102
/// <summary>
90
103
/// Process an individual request.
91
104
/// </summary>
@@ -172,21 +185,7 @@ private async Task DisplayExceptionContent(ErrorContext errorContext)
172
185
173
186
if ( _problemDetailsService != null )
174
187
{
175
- var problemDetails = new ProblemDetails
176
- {
177
- Title = TypeNameHelper . GetTypeDisplayName ( errorContext . Exception . GetType ( ) ) ,
178
- Detail = errorContext . Exception . Message ,
179
- Status = httpContext . Response . StatusCode
180
- } ;
181
-
182
- problemDetails . Extensions [ "exception" ] = new
183
- {
184
- Details = errorContext . Exception . ToString ( ) ,
185
- Headers = httpContext . Request . Headers ,
186
- Path = httpContext . Request . Path . ToString ( ) ,
187
- Endpoint = httpContext . GetEndpoint ( ) ? . ToString ( ) ,
188
- RouteValues = httpContext . Features . Get < IRouteValuesFeature > ( ) ? . RouteValues ,
189
- } ;
188
+ var problemDetails = CreateProblemDetails ( errorContext , httpContext ) ;
190
189
191
190
await _problemDetailsService . WriteAsync ( new ( )
192
191
{
@@ -214,6 +213,31 @@ await _problemDetailsService.WriteAsync(new()
214
213
}
215
214
}
216
215
216
+ [ UnconditionalSuppressMessage ( "Trimming" , "IL2026" , Justification = "Values set on ProblemDetails.Extensions are supported by the default writer." ) ]
217
+ [ UnconditionalSuppressMessage ( "AOT" , "IL3050" , Justification = "Values set on ProblemDetails.Extensions are supported by the default writer." ) ]
218
+ private ProblemDetails CreateProblemDetails ( ErrorContext errorContext , HttpContext httpContext )
219
+ {
220
+ var problemDetails = new ProblemDetails
221
+ {
222
+ Title = TypeNameHelper . GetTypeDisplayName ( errorContext . Exception . GetType ( ) ) ,
223
+ Detail = errorContext . Exception . Message ,
224
+ Status = httpContext . Response . StatusCode
225
+ } ;
226
+
227
+ // Problem details source gen serialization doesn't know about IHeaderDictionary or RouteValueDictionary.
228
+ // Serialize payload to a JsonElement here. Problem details serialization can write JsonElement in extensions dictionary.
229
+ problemDetails . Extensions [ "exception" ] = JsonSerializer . SerializeToElement ( new ExceptionExtensionData
230
+ (
231
+ Details : errorContext . Exception . ToString ( ) ,
232
+ Headers : httpContext . Request . Headers ,
233
+ Path : httpContext . Request . Path . ToString ( ) ,
234
+ Endpoint : httpContext . GetEndpoint ( ) ? . ToString ( ) ,
235
+ RouteValues : httpContext . Features . Get < IRouteValuesFeature > ( ) ? . RouteValues
236
+ ) , _serializationContext . ExceptionExtensionData ) ;
237
+
238
+ return problemDetails ;
239
+ }
240
+
217
241
private Task DisplayCompilationException (
218
242
HttpContext context ,
219
243
ICompilationException compilationException )
@@ -328,3 +352,10 @@ private Task DisplayRuntimeException(HttpContext context, Exception ex)
328
352
return errorPage . ExecuteAsync ( context ) ;
329
353
}
330
354
}
355
+
356
+ internal record ExceptionExtensionData ( string Details , IHeaderDictionary Headers , string Path , string ? Endpoint , RouteValueDictionary ? RouteValues ) ;
357
+
358
+ [ JsonSerializable ( typeof ( ExceptionExtensionData ) ) ]
359
+ internal sealed partial class ExtensionsExceptionJsonContext : JsonSerializerContext
360
+ {
361
+ }
0 commit comments