Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Content Negotiation in EndpointMetadataApiDescriptionProvider #60590

Open
1 task done
adamvandenhoven opened this issue Feb 24, 2025 · 0 comments
Open
1 task done
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates

Comments

@adamvandenhoven
Copy link

adamvandenhoven commented Feb 24, 2025

Is there an existing issue for this?

  • I have searched the existing issues

Is your feature request related to a problem? Please describe the problem.

For a variety of reasons, I want content negotiation in my API. Specifically I want my routes to be able to return different Schemas for different media types. I know that actually doing the serialization is not something that is going to work out of the box but we already many examples where in Minimal API we can negotiate a JSON or an XML serialization of the object, and its absolutely no different (other than wiring up thing so its not onerous) to return different schemas serialized in different ways.

In my case, I'm working with Fast Endpoints and the Swagger generation is handled by NSwag, but I've tracked the source of "why doesn't this work" to EndpointMetadataApiDescriptionProvider. An example of what I want to have is:

public class GetById : Endpoint<GetUserByIdRequest, UserAggregate>
    public override void Configure()
    {
        Get(GetUserByIdRequest.Route);
        Description(builder =>
        {
            builder
                .Produces<UserRecord>((int)HttpStatusCode.OK, "application/json", "application/vnd.example.user+json")
                .Produces<Summary>((int)HttpStatusCode.OK, "application/vnd.example.summary+json")
                .Produces((int)HttpStatusCode.NotFound)
                .ProducesProblemFE();
        });
    }
}

So my method returns a UserAggregate but the serialization has 3 media types and 2 different schemas, and I know I can convert UserAggregate into then both.

The issue arises here:

if (!supportedResponseTypes.Any(existingResponseType => existingResponseType.StatusCode == apiResponseType.StatusCode))
{
supportedResponseTypes.Add(apiResponseType);
}

The code assumes that you can only have one response for a particular status code, which is not true. What's also annoying is that you would expect these two things to be the same:

public class GetById : Endpoint<GetUserByIdRequest, UserAggregate>
    public override void Configure()
    {
        Get(GetUserByIdRequest.Route);
        Description(builder =>
        {
            builder
                .Produces<UserRecord>((int)HttpStatusCode.OK, "application/json", "application/vnd.example.user+json")
                .Produces((int)HttpStatusCode.NotFound)
                .ProducesProblemFE();
        });
    }
}

and

public class GetById : Endpoint<GetUserByIdRequest, UserAggregate>
    public override void Configure()
    {
        Get(GetUserByIdRequest.Route);
        Description(builder =>
        {
            builder
                .Produces<UserRecord>((int)HttpStatusCode.OK, "application/json")
                .Produces<UserRecord>((int)HttpStatusCode.OK, "application/vnd.example.user+json")
                .Produces((int)HttpStatusCode.NotFound)
                .ProducesProblemFE();
        });
    }
}

but they are not. In the second instance only one media type ends up in the generated swagger. You could argue, "then why do it" but the second is arguably cleaner

Describe the solution you'd like

I would like to have some mechanism whereby I can have multiple response types for a give status code. From what I've looked at in NSwag, for example, it shouldn't be a problem to generate the swagger.

I assume there is some reason other than "you're never going to need it" for this restriction so maybe an explicit opt-in per document or per route is the way to go but I'd really like to see it in there.

Additional context

No response

@dotnet-issue-labeler dotnet-issue-labeler bot added the area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates label Feb 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-mvc Includes: MVC, Actions and Controllers, Localization, CORS, most templates
Projects
None yet
Development

No branches or pull requests

1 participant