ASP.NET Web API documentation using Swagger – Add custom headers

In this post, we will look into how to add custom headers to your Swagger documentation using Swashbuckle.
We will add well-known “Accept-Language” header with conditionally visibility based on API method!

Setup application culture based on Accept-Language header

First, add CultureHandler and enable it into WEB API pipeline (for better understanding on how ASP.NET WEB API pipeline works and what are delegating handler, please refer to the-asp-net-web-api-2-http-message-lifecycle-in-43-easy-steps-2/

public class CultureHandler : DelegatingHandler
{
    private static readonly string[] _acceptedCultures = { "en-GB", "de-DE" };
    private static readonly string _defaultCulture = "en-GB";

    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        Thread.CurrentThread.CurrentCulture = new CultureInfo(_defaultCulture);
            
        if (request.Headers.AcceptLanguage != null)
        {
            foreach (var v in request.Headers.AcceptLanguage.OrderBy(al => al.Quality))
            {
                if (_acceptedCultures.Contains(v.Value))
                {
                    Thread.CurrentThread.CurrentCulture = new CultureInfo(v.Value);
                }
            }
        }

        return base.SendAsync(request, cancellationToken);
    }
}

Next, register CultureHandler in WebApiConfig:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();

        config.MessageHandlers.Add(new CultureHandler());

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

Display Accept-Language header in Swagger UI

Ok, now it is time to add the possibility to display new header in Swagger UI.
For that, we will extend Swashbuckle behavior by introducing CultureAwareOperationFilter:

public class CultureAwareOperationFilter : IOperationFilter
{
    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
        if (operation.parameters == null)
        {
            operation.parameters = new List<Parameter>();
        }

        operation.parameters.Add(new Parameter
        {
            name = "Accept-Language",
            @in = "header",
            type = "string",
            required = true,
            @enum = new [] { "en-GB", "de-DE"}
        });
    }
}

Register new Operation filter in SwaggerConfig

c.OperationFilter<CultureAwareOperationFilter>();

To test new behavior let’s add new controller with simple localized content

public class ProductController : ApiController
{
    [Route("products")]
    public IHttpActionResult Get()
    {
        if (Thread.CurrentThread.CurrentCulture.Name == "de-DE")
        {
            return Ok("I am German product!");
        }

        return Ok("I am English product!");
    }
}

Now, it is time to run the project, if go by URI: http://localhost:50217/swagger/ui/index#!/Product/Product_Get, you could now see Accept-Language dropdown with English and German culture inside:

Now if select de-DE there, as a response you will get “I am German product!”.

Restricting Accept-Language header for specific APIs

If you now look at Discount API, you will see that it also got new header selection. That is quite misleading as API does not provide localization. Let’s try to fix this by extending CultureAwareOperationFilter. To make OperationFilter configuration decoupled from actual actions we will be using CultureAwareAttribute that will decorate specific methods.

public class CultureAwareAttribute : Attribute
{
}

Extend CultureAwareOperationFilter

public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
    if (!apiDescription.ActionDescriptor.GetCustomAttributes<CultureAwareAttribute>().Any())
    {
        return;
    }

    ...
}

The last step is to decorate required actions with CultureAwareAttribute,

[Route("products")]
[CultureAware]
public IHttpActionResult Get()

Now if we open Swagger, we will see language header selection only for Products API, but it won’t appear for others.

Another usage example of operation filter

The operation is displayed as dropdown if @enum property is specified.

Another alternative is to specify @defaultValue


operation.parameters.Add(new Parameter
{
name = "X-Custom-Header",
@in = "header",
type = "string",
required = true,
@default = "Default value"
});

In that case operation is displayed as textbox. That representation is often used to supply security key for API usage.

Summary:

We have looked into how to use operation filters to display custom headers in Swagger UI.
For basic usage, remember that @enum option is rendered as dropdown of choices, and as textbox otherwise.

This entry was posted in Documentation, WEB API and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s