aspnetcore icon indicating copy to clipboard operation
aspnetcore copied to clipboard

Support description for response status code in the Open API

Open mehdihadeli opened this issue 3 years ago • 1 comments

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.

Hi, In .Net 7, there is no way to add a custom response status code description with Produces<>() method. And status code descriptions are fixed for status codes (for example for 400 and 401 it is client error):

endpoints.MapPost("/", CreateProducts)
            .WithOpenApi(operation => new(operation)
            {
                Summary = "Creating a New Product", Description = "Creating a New Product"
            })
            .RequireAuthorization()
             .Produces<CreateProductResponse>(StatusCodes.Status201Created)
            .Produces<StatusCodeProblemDetails>(StatusCodes.Status401Unauthorized)
            .Produces<StatusCodeProblemDetails>(StatusCodes.Status400BadRequest)
            .WithName("CreateProduct")
            .WithDisplayName("Create a new product."));

1

Describe the solution you'd like

Please add a description to Produces<>() overload for changing default status code response description. This feature exists in Swashbuckle.AspNetCore.Annotations package with bellow functionality

endpoints.MapPost("/", CreateProducts)
            .WithOpenApi(operation => new(operation)
            {
                Summary = "Creating a New Product", Description = "Creating a New Product"
            })
            .RequireAuthorization()
             .Produces<CreateProductResponse>(StatusCodes.Status201Created)
            .WithName("CreateProduct")
            .WithDisplayName("Create a new product.")
            .WithMetadata(new SwaggerResponseAttribute(
                StatusCodes.Status401Unauthorized,
                "UnAuthorized request.",
                typeof(StatusCodeProblemDetails)))
            .WithMetadata(new SwaggerResponseAttribute(
                StatusCodes.Status400BadRequest,
                "Invalid input for creating product.",
                typeof(StatusCodeProblemDetails)))
            .WithMetadata(
                new SwaggerResponseAttribute(
                    StatusCodes.Status201Created,
                    "Product created successfully.",
                    typeof(CreateProductResponse)));

2

Additional context

No response

mehdihadeli avatar Jan 04 '23 16:01 mehdihadeli

cc @captainsafia

davidfowl avatar Jan 05 '23 06:01 davidfowl

@mehdihadeli You should be able to do this by leveraging the WithOpenApi extension method:

endpoints.MapPost("/", CreateProducts)
  .WithOpenApi(operation => 
{
  operation.Responses["200"].Description = "Your description here";
  return operation;
});

captainsafia avatar Jan 05 '23 19:01 captainsafia

Hi @mehdihadeli. We have added the "Needs: Author Feedback" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

ghost avatar Jan 05 '23 19:01 ghost

@captainsafia Thanks, let me check, but it is better we have a separate method like WithSummary and WithDescription for this also

mehdihadeli avatar Jan 05 '23 19:01 mehdihadeli

@captainsafia Thanks, let me check, but it is better we have a separate method like WithSummary and WithDescription for this also

You should be able to create these kinds of wrappers yourself doing something like:

public IEndpointConventionBuilder WithResponseDescription(this IEndpointConventionBuilder builder, int statusCode, string description)
{
  builder.WithOpenApi(operation => 
  {
    operation.Responses[$"{statusCode}"].Description = description;
    return operation;
  });
  return builder;
}

The reason we introduced the WithOpenApi extension method was to provide a catch-all approach for handling different parts of the OpenAPI annotation. So instead of providing an extension method for each and every aspect (which can get really bloated), we provide the more robust API and then users can implement their own extension methods for particular areas they are interested in.

captainsafia avatar Jan 05 '23 20:01 captainsafia

Make sense, Thanks :)

mehdihadeli avatar Jan 05 '23 20:01 mehdihadeli

@captainsafia Hi, Is it possible we add status response schema type also in this extension (WithOpenApi) method instead of calling a separate method .Produces<StatusCodeProblemDetails>(StatusCodes.Status401Unauthorized)?

I want something like this:

 .WithResponseDescription<StatusCodeProblemDetails>(StatusCodes.Status401Unauthorized, "Unauthorized")

Should I call .Produces<StatusCodeProblemDetails>(StatusCodes.Status401Unauthorized) after WithOpenApi inner extension method or there is a way to do this inner WithOpenApi?

mehdihadeli avatar Jan 10 '23 12:01 mehdihadeli

@captainsafia Hi, Is it possible we add status response schema type also in this extension (WithOpenApi) method instead of calling a separate method .Produces<StatusCodeProblemDetails>(StatusCodes.Status401Unauthorized)?

I want something like this:

 .WithResponseDescription<StatusCodeProblemDetails>(StatusCodes.Status401Unauthorized, "Unauthorized")

Should I call .Produces<StatusCodeProblemDetails>(StatusCodes.Status401Unauthorized) after WithOpenApi inner extension method or there is a way to do this inner WithOpenApi?

Yep, you'll have to do this.

At the moment, response schema generation is done in the Swashbuckle.AspNetCore library, not in the the Microsoft.OpenApi package. Swashbuckle.AspNetCore expects to get the response type in a Produces metadata item on the endpoint. It takes the type defined there, generates the schema, and then applies it to the schema property in the request.

What this means, is that if you wanted to wrap this functionality in one step, you'd have to do call both WithOpenApi in your extension method to set the custom description method and builder.Produces to add the response type to metadata so Swashbuckle can pick it up to generate the schema.

public IEndpointConventionBuilder WithResponseDescription<TResponse>(this IEndpointConventionBuilder builder, int statusCode, string description)
{
  builder.Produces<TResponse>(statusCode);
  builder.WithOpenApi(operation => 
  {
    operation.Responses[$"{statusCode}"].Description = description;
    return operation;
  });
  return builder;
}

captainsafia avatar Jan 10 '23 18:01 captainsafia

@captainsafia Hi, Is it possible we add status response schema type also in this extension (WithOpenApi) method instead of calling a separate method .Produces<StatusCodeProblemDetails>(StatusCodes.Status401Unauthorized)? I want something like this:

 .WithResponseDescription<StatusCodeProblemDetails>(StatusCodes.Status401Unauthorized, "Unauthorized")

Should I call .Produces<StatusCodeProblemDetails>(StatusCodes.Status401Unauthorized) after WithOpenApi inner extension method or there is a way to do this inner WithOpenApi?

Yep, you'll have to do this.

At the moment, response schema generation is done in the Swashbuckle.AspNetCore library, not in the the Microsoft.OpenApi package. Swashbuckle.AspNetCore expects to get the response type in a Produces metadata item on the endpoint. It takes the type defined there, generates the schema, and then applies it to the schema property in the request.

What this means, is that if you wanted to wrap this functionality in one step, you'd have to do call both WithOpenApi in your extension method to set the custom description method and builder.Produces to add the response type to metadata so Swashbuckle can pick it up to generate the schema.

public IEndpointConventionBuilder WithResponseDescription<TResponse>(this IEndpointConventionBuilder builder, int statusCode, string description)
{
  builder.Produces<TResponse>(statusCode);
  builder.WithOpenApi(operation => 
  {
    operation.Responses[$"{statusCode}"].Description = description;
    return operation;
  });
  return builder;
}

Thanks for your response, You mean about Swashbuckle.AspNetCore is using this Swashbuckle.AspNetCore.Annotations package and this attribute SwaggerResponseAttribute? Something like this:

builder.WithMetadata(new SwaggerResponseAttribute(
            StatusCodes.Status401Unauthorized,
            "UnAuthorized request.",
            typeof(StatusCodeProblemDetails)))

mehdihadeli avatar Jan 10 '23 18:01 mehdihadeli

You mean about Swashbuckle.AspNetCore is using this Swashbuckle.AspNetCore.Annotations package and this attribute SwaggerResponseAttribute?

Nope, Swashbuckle is using the ProducesResponseTypeAttribute which is what the Produces extension methods referenced in the code above add to endpoint's metadata.

captainsafia avatar Jan 10 '23 18:01 captainsafia

Ok, Thanks.

mehdihadeli avatar Jan 11 '23 08:01 mehdihadeli