Swashbuckle (Swagger) Customization for WebAPI

Who has ever tested their WebAPI knows such tools as Postman or Advanced REST (extensions for Chrome). These tools are convenient in every way, except that they are not able to recognize which models the API accepts, which ones it returns and do not provide information about all possible endpoints. The Swashbuckle package solves this disadvantage. It builds Swagger specification generation and UI in the project. In this article, I will briefly describe how to bind it to the project and provide some details about authorization and work with “overloaded” endpoints.

Binding to Project

Swashbuckle is the NuGet package that integrates the auto-generation of information about nodes in WebAPI according to the OpenAPI specification. This specification is de facto the standard, as WSDL once was. To install it, you need to perform four simple steps:

  1. Install it from NuGet using the Install-Package Swashbuckle command
  2. Enable XML documentation in the project settings
  3. Uncomment the IncludeXmlComments(GetXmlCommentsPath()); line in the SwaggerConfig.cs file, which is created when installing the package
  4. Write return string.Format(@”{0}\bin\BookStoreApiService.XML”, AppDomain.CurrentDomain.BaseDirectory); in the implementation of the GetXmlCommentsPath() method

That is all. Then, it is necessary to describe the API methods, response codes, and customize them.

Nuances when deploying WebAPI

Deploying WebAPI into production may cause the issue when the XML file is missing. The release build does not include them by default. However, we can work around it by editing the csproj file. It is necessary to add <ExcludeXmlAssemblyFiles>false</ExcludeXmlAssemblyFiles> in the project PropertyGroup and the file will be left in bin/.

Another issue relates to those who hide their API behind a proxy. The solution is not universal, however, it works in my case. The proxy adds headers to the request, by which we know what URL of endpoints should be for the client.

Example of URL recognition behind proxy

Adding Response Codes

The return HTTP Status Codes can be added using XML comments and attributes.

Examples of adding status codes

In addition, it is necessary to remember that XML comments override attributes. The latter ones will be ignored if both ways are used simultaneously for the same method. Also, if XML comments are used, it is necessary to specify all the codes, including 200 (OK), while the return model cannot be specified. Therefore, using SwaggerResponse is more preferable, as it does not have these drawbacks. When the endpoint returns another code, for example, 201 (Created), instead of the default 200, the first one must be removed with the [SwaggerResponseRemoveDefaults] attribute.

There is an option to add common codes, like 400 (BadRequest) or 401 (Unauthorized) to all methods at once. To do this, it is necessary to implement the IOperationFilter interface and register such a class using c.OperationFilter<T>();.

An example of using the Apply method to add a certain code list:

Authorization of WebAPI and Swashbuckle

The text below provides several options for implementing the Basic authorization. However, the package supports others.

If AuthorizeAttribute is used, then Swashbuckle will build UI, but queries will fail. There are several ways to provide this information:

  1. Using authorization built into a browser
  2. Using a built-in authorization form in the package
  3. Using operation parameters
  4. Using JavaScript

Authorization built into browser

Authorization built into a browser will be available “out of the box” if you use the attribute and filter:

After adding them in the WebAPI configuration, the browser prompts you to enter data for authentication when executing the query. The difficulty is that dropping this data is not as easy and fast as adding them.

Built-in authorization form in Swashbuckle

The other way is more comfortable as it provides a special form. To include the built-in authorization form in the package, do the following steps:

  1. Enable an attribute and filter for authentication
  2. Uncomment the c.BasicAuth(“basic”).Description(“Basic HTTP Authentication”);  line in the Swagger settings
  3. Add IOperationFilter which adds the information in the c.OperationFilter<MarkSecuredMethodsOperationFilter>(); nodes

Implementation of the Apply method of this filter

After that, you will be able to use this authorization form, and the data you entered will be used for all queries.

Authorization using a parameter and JS code

The following two methods should be considered as examples of working with IOperationFilter and injecting your JavaScript.

Parameters can send data not only to body and query but also to the header. In this case, it is necessary to add a hash.

Adding such a parameter

By injecting your JavaScript, you can also send data in the header of queries. To do this, go through the following steps:

  1. Add a JS file as an embedded resource
  2. Uncomment the line in the Swagger configuration and specify your file as a resource name: InjectJavaScript(thisAssembly, “assembly.namesapce.swagger-basic-auth.js”);
  3. In the file, write the following: swaggerUi.api.clientAuthorizations.add(“basic”, new SwaggerClient.ApiKeyAuthorization(“Authorization”, “Basic U3dhZ2dlcjpUZXN0”, “header”));

Now, this data will be added as a header to each query. Actually, with this JS code, it is possible to send any headers, as I understand. The key parameter, which equals «basic» in the example, should be unique so that you do not receive the JS error when sending a query.

For example, JS sending headers to Swagger

Working with mandatory headers

In some cases, unauthorized headers may be mandatory. For example, headers with information about a client. Usually, a message handler is built into the WebAPI pipeline, the DelegatingHandler is implemented and registered in the WebAPI config.MessageHandlers.Add (new MandatoryHeadersHandler ()); configuration.

In this case, Swagger will stop showing any information, as the handler will not allow passing queries. It is not solved out of the box, that’s why it is necessary to make provision for this case in your handler, i.e. in the case of a query to the swagger URL, the handler should pass it. And then the addition of headers with JS will help, as described above.

Endpoints with overloaded methods

WebAPI allows creating several action methods for one endpoint, the call of which depends on the query parameters.

Swagger does not support these methods by default. UI will throw the error message 500: Not supported by Swagger 2.0: Multiple operations with path ‘api/<URL>’ and method ‘<METHOD>’. See the config setting — \«ResolveConflictingActions\» for a potential workaround.

As the message says, you should solve the situation by yourself. There are several options to do this:

  1. select only one method
  2. create one method with all parameters
  3. change the document generation

The first and the second ways are implemented using the c.ResolveConflictingActions(Func<IEnumerable<ApiDescription>, ApiDescription> conflictingActionsResolver) settings. The principle of the method is to take several conflicted methods and return only one.

An example of combining all the parameters

Drastic method

The third way is more drastic and originates from the OpenAPI specification. We can output all the endpoints with parameters:

To do this, it is necessary to modify the way the Swagger documentation is generated using IDocumentFilter and generate the description by yourself.

In the real world, this method is rarely needed, so we dig even deeper.

Another way I would recommend only to those who are interested in Swashbuckle internals is to replace SwaggerGenerator. This is done in the c.CustomProvider (defaultProvider => new NewSwaggerProvider (defaultProvider)); line. To do this, follow these steps:

  1. create your MySwaggerGenerator: ISwaggerProvider class
  2. find SwaggerGenerator.cs in the Swashbuckle repository on GitHub (ссылка)
  3. copy the GetSwagger method and other related methods into yours
  4. duplicate internal variables and initialize them in the constructor of your class
  5. register in the Swagger configuration

Initializing internal variables

Then, you need to find ar paths = GetApiDescriptionsFor(apiVersion)….., a place where paths are created. For example, to get what is in the example, it is necessary to replace GroupBy() with .GroupBy(apiDesc => apiDesc.RelativePath).

References

  1. Swagger example
  2. RESTful Web API specification formats
  3. Customize Swashbuckle-generated API definitions
  4. Swagger object schema
  5. Authentication Filters in ASP.NET Web API 2
  6. A WebAPI Basic Authentication Authorization Filter
  7. Customize Authentication Header in SwaggerUI using Swashbuckle
  8. HTTP Message Handlers in ASP.NET Web API
  9. Managing Action Conflicts in ASP.Net 5 with Swashbuckle
  10. Tutorial Swagger project at GitHub