Produces and Accepts should target IEndpointConventionBuilder
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 281
Description
## Background and Motivation
This is a continuation of #41428. I first proposed this API there, but it only got partially approved. #43675 demonstrates there are scenarios when we want to apply this kind of metadata to a group.`ProducesMetadata`, but calling an extension method on group might be more convenient for app developers.
## Proposed API
```diff
namespace Microsoft.AspNetCore.Http;
public static class OpenApiRouteHandlerBuilderExtensions
{
public static RouteHandlerBuilder Produces(
this RouteHandlerBuilder builder,
int statusCode = StatusCodes.Status200OK,
string? contentType = null,
params string[] additionalContentTypes);
public static RouteHandlerBuilder Produces(
this RouteHandlerBuilder builder,
int statusCode,
Type? responseType = null,
string? contentType = null,
params string[] additionalContentTypes);
+ public static TBuilder Produces(
+ this TBuilder builder,
+ Type? responseType = null,
+ int statusCode = StatusCodes.Status200OK,
+ string? contentType = null,
+ params string[] additionalContentTypes) where TBuilder : IEndpointConventionBuilder;
public static RouteHandlerBuilder ProducesProblem(
this RouteHandlerBuilder builder,
int statusCode,
string? contentType = null);
+ public static TBuilder ProducesProblem(
+ this TBuilder builder,
+ int statusCode,
+ string? contentType = null) where TBuilder : IEndpointConventionBuilder;
public static RouteHandlerBuilder ProducesValidationProblem(
this RouteHandlerBuilder builder,
int statusCode = StatusCodes.Status400BadRequest,
string? contentType = null);
+ public static TBuilder ProducesValidationProblem(
+ this TBuilder builder,
+ int statusCode = StatusCodes.Status400BadRequest,
+ string? contentType = null) where TBuilder : IEndpointConventionBuilder;
public static RouteHandlerBuilder Accepts(
this RouteHandlerBuilder builder,
bool isOptional,
string contentType,
params string[] additionalContentTypes) where TRequest : notnull;
public static RouteHandlerBuilder Accepts(
this RouteHandlerBuilder builder,
Type requestType,
bool isOptional,
string contentType,
params string[] additionalContentTypes);
public static RouteHandlerBuilder Accepts(
this RouteHandlerBuilder builder,
Type requestType,
string contentType,
params string[] additionalContentTypes);
+ public static TBuilder Accepts(
+ this TBuilder builder,
+ Type requestType,
+ bool isOptional,
+ string contentType,
+ params string[] additionalContentTypes) where TBuilder : IEndpointConventionBuilder;
+ public static TBuilder Accepts(
+ this TBuilder builder,
+ Type requestType,
+ string contentType,
+ params string[] additionalContentTypes) where TBuilder : IEndpointConventionBuilder;
```
## Usage Examples
Now you're forced to manually add attributes using `WithMetadata()`:
```csharp
// Describe that all APIs can return errors as JSON or plain text
examples.WithMetadata(new ProducesResponseTypeAttribute(typeof(ProblemDetails), 401, "application/problem+json", "text/plain"));
examples.WithMetadata(new ProducesResponseTypeAttribute(typeof(ProblemDetails), 403, "application/problem+json", "text/plain"));
```
Rather than
```csharp
// Describe that all APIs can return errors as JSON or plain text
examples.ProducesProblem(401);
examples.ProducesProblem(403);
```
I don't have as concrete a scenario for adding accepts metadata to an entire group. We could consider skipping this again, but I like having both for consistency. In theory, middleware or `BindAsync` could be adding additional accepted content types to an entire group somehow. There are alternatives for types implementing `BindAsync` like also implementing `PopulateMetadata`, but who knows? Maybe it's too dynamic to be implemented via a static interface.
## Alternative Designs
- Only target `IEndpointConventionBuilder` for `Produces`, `ProducesProblem` and `ProducesValidationProblem`, but not `Accepts`.
- Add an overload for `RouteGroupBuilder` rather than `IEndpointConventionBuilder` even though that's less extensible.
## Risks
These are just more extension methods that now show up in intellisense for all `IEndpointConventionBuilder` types.
Contributor guide
Research direction
Start by reviewing OpenApiRouteHandlerBuilderExtensions and the existing RouteHandlerBuilder overloads, then read the context in issues #41428 and #43675. The change is complete when the proposed Produces, ProducesProblem, ProducesValidationProblem, and Accepts APIs support IEndpointConventionBuilder, including endpoint groups, with the stated signatures and usage examples working.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- api, backend
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100