Improve error response for deserialization errors in Minimal APIs
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 6h
- Merged PRs (30d)
- 290
Description
### Is there an existing issue for this?
- [x] I have searched the existing issues
### Is your feature request related to a problem? Please describe the problem.
The error response for a deserialization error in a Minimal API app is very verbose and difficult to parse. Here's an example:
```text
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "Microsoft.AspNetCore.Http.BadHttpRequestException",
"status": 400,
"detail": "Failed to read parameter \"WeatherForecast body\" from the request body as JSON.",
"exception": {
"details": "Microsoft.AspNetCore.Http.BadHttpRequestException: Failed to read parameter \"WeatherForecast body\" from the request body as JSON.\n ---> System.Text.Json.JsonException: The JSON value could not be converted to System.String. Path: $.summary | LineNumber: 3 | BytePositionInLine: 17.\n ---> System.InvalidOperationException: Cannot get the value of a token type 'Number' as a string.\n at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_ExpectedString(JsonTokenType tokenType)\n at System.Text.Json.Utf8JsonReader.GetString()\n at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)\n at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)\n at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)\n at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, T& value, JsonSerializerOptions options, ReadStack& state)\n --- End of inner exception stack trace ---\n at System.Text.Json.ThrowHelper.ReThrowWithPath(ReadStack& state, Utf8JsonReader& reader, Exception ex)\n at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, T& value, JsonSerializerOptions options, ReadStack& state)\n at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.ContinueDeserialize(ReadBufferState& bufferState, JsonReaderState& jsonReaderState, ReadStack& readStack, T& value)\n at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsync(Stream utf8Json, CancellationToken cancellationToken)\n at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsObjectAsync(Stream utf8Json, CancellationToken cancellationToken)\n at Microsoft.AspNetCore.Http.HttpRequestJsonExtensions.ReadFromJsonAsync(HttpRequest request, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)\n at Microsoft.AspNetCore.Http.HttpRequestJsonExtensions.ReadFromJsonAsync(HttpRequest request, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken)\n at Microsoft.AspNetCore.Http.RequestDelegateFactory.g__TryReadBodyAsync|102_0(HttpContext httpContext, Type bodyType, String parameterTypeName, String parameterName, Boolean allowEmptyRequestBody, Boolean throwOnBadRequest, JsonTypeInfo jsonTypeInfo)\n --- End of inner exception stack trace ---\n at Microsoft.AspNetCore.Http.RequestDelegateFactory.Log.InvalidJsonRequestBody(HttpContext httpContext, String parameterTypeName, String parameterName, Exception exception, Boolean shouldThrow)\n at Microsoft.AspNetCore.Http.RequestDelegateFactory.g__TryReadBodyAsync|102_0(HttpContext httpContext, Type bodyType, String parameterTypeName, String parameterName, Boolean allowEmptyRequestBody, Boolean throwOnBadRequest, JsonTypeInfo jsonTypeInfo)\n at Microsoft.AspNetCore.Http.RequestDelegateFactory.<>c__DisplayClass102_2.<b__2>d.MoveNext()\n--- End of stack trace from previous location ---\n at Microsoft.AspNetCore.Diagnostics.StatusCodePagesMiddleware.Invoke(HttpContext context)\n at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)\n at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)\n at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)",
"headers": {
"Accept": [
"application/json"
],
"Connection": [
"close"
],
"Host": [
"localhost:5280"
],
"User-Agent": [
"vscode-restclient"
],
"Accept-Encoding": [
"gzip, deflate"
],
"Content-Type": [
"application/json"
],
"Content-Length": [
"71"
]
},
"path": "/forecast/",
"endpoint": "HTTP: POST /forecast",
"routeValues": {}
},
"traceId": "00-0ee0c4d5c4664bcba04e8368d7cb98bc-85d7d92bfdd90048-00"
}
```
Contrast this with the error response for the same scenario with a controller-based app:
```text
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json; charset=utf-8
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"errors": {
"body": [
"The body field is required."
],
"$.summary": [
"The JSON value could not be converted to System.String. Path: $.summary | LineNumber: 3 | BytePositionInLine: 17."
]
},
"traceId": "00-d3c338ac09225363af3fd502ee551533-db5efa0694a6a7ca-00"
}
```
### Describe the solution you'd like
Error responses for deserialization errors in Minimal APIs should have concise and clear error responses that are _at least_ as good as the error responses from controller-based apps.
### Additional context
A project that illustrates the difference between deserialization errors from a Minimal API app vs a controller-based app is in the `deser-error-handling` directory of my [dotnet10-issue-repros repo](https://github.com/mikekistler/dotnet10-issue-repros).
Contributor guide
Assessment
This issue has not been assessed yet.