Swashbuckle Swagger - How to annotate content types?
Asked Answered
D

4

23

How do I annotate my ASP.NET WebAPI actions so that the swagger metadata includes the content-types that my resources support?

Specifically, I want the documentation to show that one of my resources can return the 'original' application/json and application/xml but also now returns a new format, application/vnd.blah+json or +xml.

Drying answered 25/1, 2016 at 10:28 Comment(3)
Shashbuckle 5 should take care of this for you if you register the MediaTypeFormatter during the Web Api configuration.Impassible
Thanks. That sounds clever, but I want it per action/route.Drying
I think you just need to add it as a formatter in your webconfig - this is global though, on not on a per action basis. What you could do was create your own operationsFilter and apply it only to those operations that returns the new formatPeepul
P
10

What you need to do is this; Swagger spec: you need to add your response-type to the list of response-types for that operation:

"produces": [
            "application/json",
            "text/json"
            ],

This can be done with an OperationFilter.

Pseudo Code incoming!!!

public class CustomResponseType : IOperationFilter
{        
    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {            
            if (operation.operationId == "myCustomName")
            {
                operation.produces.Add("application/vnd.blah+json");
            }            
    }      
}

the OperationId can be set through the [SwaggerOperation("myCustomName")] annotation.

Then apply the operationsFilter in the swaggerConfig.cs:

c.OperationFilter<CustomResponseType>();

Note: instead of operation.operationId == "myCustomName" you could do it for a particular route or anything else basically. ApiDescription gives a LOT of info about context.

Peepul answered 1/2, 2016 at 11:32 Comment(0)
C
51

Extending @VisualBean's answer.

On a Controller's API method, you could use the code below to set an Attribute like:

[SwaggerResponseContentType(responseType:"application/pdf", Exclusive=true)]
public HttpResponseMessage GetAuthorityForm(string id)
{
....

Note: 'Exclusive=true' will remove all other content types, otherwise using the new Attribute will add a new Response Content Type in the Swagger UI drop down. It will NOT modify your Controller or API, just the documentation.

SwaggerConfig.cs

 GlobalConfiguration.Configuration
            .EnableSwagger(c =>
 // Set filter to apply Custom Content Types to operations
 //
 c.OperationFilter<ResponseContentTypeOperationFilter>();

SwaggerResponseContentTypeAttribute.cs

/// <summary>
/// SwaggerResponseContentTypeAttribute
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public sealed class SwaggerResponseContentTypeAttribute : Attribute
{
    /// <summary>
    /// SwaggerResponseContentTypeAttribute
    /// </summary>
    /// <param name="responseType"></param>
    public SwaggerResponseContentTypeAttribute(string responseType)
    {
        ResponseType = responseType;
    }
    /// <summary>
    /// Response Content Type
    /// </summary>
    public string ResponseType { get; private set; }

    /// <summary>
    /// Remove all other Response Content Types
    /// </summary>
    public bool Exclusive { get; set; }
}

ResponseContentTypeOperationFilter.cs

public class ResponseContentTypeOperationFilter : IOperationFilter
{
    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
        var requestAttributes = apiDescription.GetControllerAndActionAttributes<SwaggerResponseContentTypeAttribute>().FirstOrDefault();

        if (requestAttributes != null)
        {
            if (requestAttributes.Exclusive)
                operation.produces.Clear();

            operation.produces.Add(requestAttributes.ResponseType);
        }
    }
}
Coopery answered 1/4, 2016 at 5:42 Comment(0)
P
10

What you need to do is this; Swagger spec: you need to add your response-type to the list of response-types for that operation:

"produces": [
            "application/json",
            "text/json"
            ],

This can be done with an OperationFilter.

Pseudo Code incoming!!!

public class CustomResponseType : IOperationFilter
{        
    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {            
            if (operation.operationId == "myCustomName")
            {
                operation.produces.Add("application/vnd.blah+json");
            }            
    }      
}

the OperationId can be set through the [SwaggerOperation("myCustomName")] annotation.

Then apply the operationsFilter in the swaggerConfig.cs:

c.OperationFilter<CustomResponseType>();

Note: instead of operation.operationId == "myCustomName" you could do it for a particular route or anything else basically. ApiDescription gives a LOT of info about context.

Peepul answered 1/2, 2016 at 11:32 Comment(0)
S
7

@OzBob's answer assumes you only want to add a single attribute to a method. If you want to add and document more than one content types for the same method, you can use the following:

SwaggerResponseContentTypeAttribute.cs

/// <summary>
/// SwaggerResponseContentTypeAttribute
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class SwaggerResponseContentTypeAttribute : Attribute
{
    /// <summary>
    /// SwaggerResponseContentTypeAttribute
    /// </summary>
    /// <param name="responseType"></param>
    public SwaggerResponseContentTypeAttribute(string responseType)
    {
        ResponseType = responseType;
    }
    /// <summary>
    /// Response Content Type
    /// </summary>
    public string ResponseType { get; private set; }

    /// <summary>
    /// Remove all other Response Content Types
    /// </summary>
    public bool Exclusive { get; set; }
}

ResponseContentTypeOperationFilter.cs

public class ResponseContentTypeOperationFilter : IOperationFilter
{
    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
        var requestAttributes = apiDescription.GetControllerAndActionAttributes<SwaggerResponseContentTypeAttribute>();

        foreach (var requestAttribute in requestAttributes)
        {
            if (requestAttribute.Exclusive)
            {
                operation.produces.Clear();
            }
            operation.produces.Add(requestAttribute.ResponseType);
        }
    }
}

Note that when you have multiple attributes on the same method and you want to replace the existing content types, you should set Exclusive = true on the first attribute only. Otherwise, you won't get all the attributes into the documentation.

Sequential answered 1/3, 2017 at 10:12 Comment(1)
Having to put the Exclusive=true as the first attribute is not obvious for the developer. I think the order of the attributes shouldn't affect the output. Sorting the list of attributes in the filter should remove the limitation : requestAttributes.OrderByDescending(a=>a.Exclusive)Wireman
L
4

Following on OzBob's answer. Since Swashbuckle 4.0.x, you may need to update the operation filter code slightly:

ResponseContentTypeOperationFilter.cs

using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Linq;

public class ResponseContentTypeOperationFilter : IOperationFilter
{
    public void Apply(Operation operation, OperationFilterContext context)
    {
        if (!context.ApiDescription.TryGetMethodInfo(out var methodInfo))
        {
            return;
        }
        var requestAttributes = methodInfo.GetCustomAttributes(true).OfType<SwaggerResponseContentTypeAttribute>().FirstOrDefault();

        if (requestAttributes != null)
        {
            if (requestAttributes.Exclusive)
                operation.Produces.Clear();

            operation.Produces.Add(requestAttributes.ResponseType);
        }
    }
}
Liard answered 15/11, 2018 at 9:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.