dotnet / dotnet/aspnetcore

Produces and Accepts should target IEndpointConventionBuilder

Open
#43,985 4 comments 6 reactions 0 assignees View on GitHub
api-suggestion area-minimal
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.