[MinimalAPI] Resolve JsonSourceGenerationOptions for build-in parameter binding and response
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 281
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.
Related issues:
- #52088
- #52642
- #53189
There has been discussion in the above-mentioned issue. But it seems like we haven't reached a conclusion.
For now we can only add the `TypeInfoResolver` into `TypeInfoResolverChain`.
And the `SerializerOptions` can only be set globally.
What if we want to specify the `SerializerOptions` based on `Type` or `JsonSerializerContext` as a group configuration?
There should be the flexibility that different `Type`s can be [de]serialized with different options.
Also, from first impression, the following code should apply the `JsonSourceGenerationOptions` for all related part.
```csharp
builder.Services.ConfigureHttpJsonOptions(
options => { options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default); });
```
### Parameter Binding
The `JsonSourceGenerationOptions` is not resolved when build-in parameter binding which will result in binding failure:
```csharp
//request json object uses options same as JsonSourceGenerationOptions
//for now, asp.net core always provide BadRequest since it does not use JsonSourceGenerationOptions. The binding failed.
app.MapPost("/todos", ([FromBody] Todo todo)) => Handle(todo))
```
There are solutions, but they introduce verbose:
```csharp
//Manually parse from request
app.MapPost("/todos", (HttpRequest httpRequest) =>
{
request = await httpRequest.ReadFromJsonAsync(AppJsonSerializerContext.Default.Todo);
//manually handle bad request. e.g., return TypedResults.BadRequest() when deserializing failed`
}
//Implement custom binding
public record Todo(int Id, Color Color, string? Title, DateOnly? DueBy = null, bool IsComplete = false)
{
public static async ValueTask BindAsync(HttpContext context)
=> await context.Request.ReadFromJsonAsync(AppJsonSerializerContext.Default.Todo);
}
```
### Response
The response also does not apply the `JsonSourceGenerationOptions`:
The output json just uses global `SerializerOptions`. If we want to apply the `JsonSourceGenerationOptions` we have to use overloads manually which introduces verbose.
```csharp
//issue 53189 reports that not all necessary methods have overloads like this
app.MapGet("/", () => TypedResults.Json(todo, AppJsonSerializerContext.Default.Todo))
```
### Code Snippet
```csharp
public record Todo(int Id, Color Color, string? Title, DateOnly? DueBy = null, bool IsComplete = false);
public enum Color
{
Red,
Blue,
Orange
}
[JsonSourceGenerationOptions(
PropertyNamingPolicy = JsonKnownNamingPolicy.SnakeCaseLower,
Converters =
[
typeof(CamelCaseJsonStringEnumConverter)
])]
[JsonSerializable(typeof(Color))]
[JsonSerializable(typeof(Todo[]))]
internal partial class AppJsonSerializerContext : JsonSerializerContext;
public class CamelCaseJsonStringEnumConverter() : JsonStringEnumConverter(JsonNamingPolicy.CamelCase)
where TEnum : struct, Enum;
```
### Describe the solution you'd like
When we configured like:
```csharp
builder.Services.ConfigureHttpJsonOptions(
options => { options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default); });
```
`JsonSourceGenerationOptions` should be build-in resolved, and we can avoid above mentioned verbose.
If that's not enough, add a centralized place to configure the `SerializerOptions` based on `Type` | `JsonSerializerContext` | Group of `Type`s.
### Additional context
_No response_
Contributor guide
Assessment
This issue has not been assessed yet.