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

Response headers not being displayed in UI #655

Closed
venerik opened this issue Feb 16, 2016 · 7 comments
Closed

Response headers not being displayed in UI #655

venerik opened this issue Feb 16, 2016 · 7 comments

Comments

@venerik
Copy link

venerik commented Feb 16, 2016

I wrote a custom SwaggerResponseHeader attribute to document response headers. I also wrote a custom AddResponseHeadersFilter to add the documentation to the swagger output. I then used the attribute on one of the controller actions. All works fine. The swagger output contains the specified response header and the editor at editor.swagger.io nicely displays the response header.

My API's swagger UI however does not display the response header. Is there a way to fix that?

Attribute

public class SwaggerResponseHeaderAttribute : Attribute
{
    public SwaggerResponseHeaderAttribute(HttpStatusCode statusCode, string name, string type, string description)
    {
        StatusCode = statusCode;
        Name = name;
        Type = type;
        Description = description;
    }

    public HttpStatusCode StatusCode { get; set; }
    public string Name { get; set; }
    public string Type { get; set; }
    public string Description { get; set; }

}

Filter

public class AddResponseHeadersFilter : IOperationFilter
{
    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
        var responseAttributes = apiDescription.GetControllerAndActionAttributes<SwaggerResponseHeaderAttribute>();

        foreach (var attr in responseAttributes)
        {
            var response = operation.responses.FirstOrDefault(x => x.Key == ((int)attr.StatusCode).ToString(CultureInfo.InvariantCulture)).Value;

            if (response != null)
            {
                if (response.headers == null)
                {
                    response.headers = new Dictionary<string, Header>();
                }

                response.headers.Add(attr.Name, new Header {description = attr.Description, type = attr.Type});
            }
        }
    }
}

Controller

[SwaggerResponseRemoveDefaults]
[SwaggerResponse(HttpStatusCode.Created, "Created", typeof(Person))]
[SwaggerResponseHeader(HttpStatusCode.Created, "Location", "string", "Location of the newly created resource")]
public IHttpActionResult Post(Person person)
{
    //...
}        

Documentation (part)

{
    "responses":{
        "201":{
            "description":"Created",
            "schema":{
                "$ref":"#/definitions/Person"
            },
            "headers":{
                "Location":{
                    "description":"Location of the newly created resource.",
                    "type":"string"
                }
            }
        }
    }
}
@VisualBean
Copy link

Unfortunately the swagger editor and swagger ui are separate products. According to the devs doing swagger-ui - some of the editor features might get implemented into swagger-ui.

This is, unfortunately not a swashbuckle issue :)

@domaindrivendev
Copy link
Owner

@VisualBean is correct. If there's not a similar issue logged already, you should submit this here: https://github.com/swagger-api/swagger-ui

@zman13
Copy link

zman13 commented Mar 30, 2017

Thanks venerik! I updated this to work with Swashbuckle 6.0. Note the attribute name change to match the way 6.0 uses ProducesResponseType instead of SwaggerResponse and ints to describe the status code.

Attribute:


    [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
    public class ProducesResponseTypeHeader : Attribute
    {
        public ProducesResponseTypeHeader(int statusCode, string name, string type, string description)
        {
            StatusCode = statusCode;
            Name = name;
            Type = type;
            Description = description;
        }

        public int StatusCode { get; }
        public string Name { get; }
        public string Type { get; }
        public string Description { get; }

    }
}

Filter:

 public class AddResponseHeadersFilter : IOperationFilter
    {

        public void Apply(Operation operation, OperationFilterContext context)
        {
            var responseAttributes = context.ApiDescription.GetActionAttributes().OfType<ProducesResponseTypeHeader>();

            foreach (var attr in responseAttributes)
            {
                var response = operation.Responses.FirstOrDefault(x => x.Key == ((int)attr.StatusCode).ToString(CultureInfo.InvariantCulture)).Value;

                if (response != null)
                {
                    if (response.Headers == null)
                    {
                        response.Headers = new Dictionary<string, Header>();
                    }

                    response.Headers.Add(attr.Name, new Header { Description = attr.Description, Type = attr.Type });
                }
            }
        }
    }

Controller:

        [HttpGet("{id}")]
        [ProducesResponseTypeHeader(200, "X-Total-Count", "int", "Total Records in result")]
        [ProducesResponseTypeHeader(200, "Other Stuff", "string", "Other Stuff Description")]
        [ProducesResponseType(typeof(SampleResult[]), 200)]
        [ProducesResponseTypeHeader(400, "More Stuff", "string", "More Stuff Description")]
        [ProducesResponseType(typeof(string),400)]       
        public async Task<IActionResult> Get(int id) ...

@mattfrear
Copy link

FWIW @venerik's code above seems to work fine 2 years later, with Swashbuckle 5.6.0.

image

I'm currently adding this functionality to my existing NuGet packages, for those too lazy to copy and paste code.

https://www.nuget.org/packages/Swashbuckle.Examples/
https://www.nuget.org/packages/Swashbuckle.AspNetCore.Examples/

@rocklan
Copy link

rocklan commented Aug 1, 2018

Just a small recommendation, the GetCustomAttributes has been depreciated and the code should now look like this:

public class AddResponseHeadersFilter  : IOperationFilter
{
    public void Apply(Operation operation, OperationFilterContext context)
    {
        var responseAttributes = context.MethodInfo.GetCustomAttributes<ProducesResponseTypeHeader>();

        foreach (var attr in responseAttributes)
        {
            var response = operation.Responses.FirstOrDefault(
                x => x.Key == ((int)attr.StatusCode).ToString(CultureInfo.InvariantCulture)).Value;

            if (response != null)
            {
                if (response.Headers == null)
                    response.Headers = new Dictionary<string, Header>();

                response.Headers.Add(attr.Name, new Header { 
                    Description = attr.Description, Type = attr.Type });
            }
        }
       
    }
}

@WhitWaldo
Copy link

Small tweak to @rocklan 's response to accommodate 5.0.0-rc5 (and the shift to OpenApi):

public class AddResponseHeadersFilter : IOperationFilter
{
public void Apply(Operation operation, OperationFilterContext context)
{
var responseAttributes = context.MethodInfo.GetCustomAttributes();

    foreach (var attr in responseAttributes)
    {
        var response = operation.Responses.FirstOrDefault(
            x => x.Key == ((int)attr.StatusCode).ToString(CultureInfo.InvariantCulture)).Value;

        if (response != null)
        {
            if (response.Headers == null)
                response.Headers = new Dictionary<string, OpenApiHeader>();

            response.Headers.Add(attr.Name, new Header { 
                Description = attr.Description,
                Content = new Dictionary<string, OpenApiMediaType> { 
                      [attr.Type] = new OpenApiMediaType()
                }
            });
        }
    }
   
}

}

@mattfrear
Copy link

@WhitWaldo yep, I ported it to OpenApi almost a year ago.

https://github.com/mattfrear/Swashbuckle.AspNetCore.Filters/blob/master/src/Swashbuckle.AspNetCore.Filters/ResponseHeaders/AddResponseHeadersFilter.cs

Or you can get it from my NuGet (Swashbuckle.AspNetCore.Filters).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants