Integrating Swagger with URL based Asp.Net core API versioning

Recently, I worked with an Asp.Net Core API project. I have added API specification with swagger / openApi. The API also need to support URL based API Versioning. And here I am to share with the world what I have learned on this journey.

Update on December 27, 2019 : I have added another post to migrate the project to ASP.Net core 3.1  

Overview

As we learn and explore we change our systems to cope with changes. Embracing changes and being flexible is a couple of imperative requirements for any business. API versioning allows your service to grow with new changes gradually so that your client can join you at their pace. However, we have a legacy API behind an AWS API Gateway which we want to rewrite. So, we chose the URL path segment versioning. Depending on the URL, API gateway will send the request to version-1 or new API. However, we also keep in mind that new API may support more than one version. To learn how to configure an AWS API Gateway you can check my post Build an API Gateway with Lambda Function in dotnet Core. You can also check Scott Hanselman’s ASP.NET Core RESTful Web API versioning made easy

OpenAPI Specification

As Swagger.io define about OAS

The OpenAPI Specification (OAS) defines a standard, language-agnostic interface to RESTful APIs which allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection.

API Version with OpenAPI Specification – Swagger

Create A new Asp.net Core web application project and select API. Next up we need to add following packages

Nuget Packages

Just add following two packages and that’s all the packages you need for version and API documentation. One for API Versioning and another for swagger

  1. Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer
  2. Swashbuckle.AspNetCore

Microsoft.AspNetCore.Mvc.Versionning.ApiExplorer (3.2.0) depends on Microsoft.AspNetCore.Mvc.ApiExplorer (2.2.0) and Microsoft.AspNetCore.Mvc.Versioning (3.1.2). You may find the implementation of these packages here

Swashbuckle.AspNetCore depends on three packages and they are

  • Swashbuckle.AspNetCore.Swagger (4.0.1)
  • Swashbuckle.AspNetCore.SwaggerGen (4.0.1)
  • Swashbuckle.AspNetCore.SwaggerUI (4.0.1)

XML Documentation

You need to enable the XML documentation for the project, so that swagger gen can read the XML comments and use as the descriptions of the services and models. You can add this from the property window. Or you may add the following line manually to your *.csproj file

<PropertyGroup>
    <DocumentationFile>bin\$(Configuration)\$(TargetFramework)\RestAPI.Example.xml</DocumentationFile>
   <LangVersion>latest</LangVersion>
</PropertyGroup>

Configure XML Documentation in a way so that XML file creates for all builds. You can also do this from the project property page, but careful about the configuration and target framework!!

Startup.cs

Configure Services

The following code will add API versioning and an API explorer (that is API version aware) services in Startup.ConfigureService method

services.AddVersionedApiExplorer().AddApiVersioning(); 

And also we need to add swagger gen service.

 services.AddSwaggerGen();

To configure swagger generate service according to your requirements you may inject custom implementations of IConfigureOptions or just configure the option while adding the service. You may find more about IConfigurationOptions<T> here.

public sealed class ConfigureSwaggerGenOptions : IConfigureOptions<SwaggerGenOptions>{
      // ctor....................
      // Configure(SwaggerGenOptions options)
      private void AddSwaggerDocumentForEachDiscoveredApiVersion(SwaggerGenOptions options) {
           foreach (var description in provider.ApiVersionDescriptions){
             settings.Info.Version = description.ApiVersion.ToString();
        if (description.IsDeprecated) settings.Info.Description += " - DEPRECATED";
         options.SwaggerDoc(description.GroupName, settings.Info);
         }
     }
}

Add version group as the document name. Swagger info is reading from the configuration, and also depreciation is added to the description if the version is marked as Depreciated.

Find the full implementation of ConfigureSwaggerGenOptions here

I have added following three services to customise swagger options

services
.AddTransient<IConfigureOptions<SwaggerOptions>, ConfigureSwaggerOptions>()
.AddTransient<IConfigureOptions<SwaggerUIOptions>, ConfigureSwaggerUiOptions>()
.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerGenOptions>();

Configure

I have used an extension method to add foolowing two swagger middlewares in Configure method in Startup.cs

app.UseSwagger();
app.UseSwaggerUI();

To configure Swagger and SwaggerUI I have added two transient services ConfigureSwaggerOptions and ConfigureSwaggerUiOptions. You can directly use it on startup.cs, but I would like to keep separate from this class. I find this approach neat and clean. I have added a sample below

public sealed class ConfigureSwaggerUiOptions : IConfigureOptions<SwaggerUIOptions> {
private readonly IApiVersionDescriptionProvider provider;
private readonly SwaggerSettings settings;
public ConfigureSwaggerUiOptions(IApiVersionDescriptionProvider versionDescriptionProvider, IOptions settings) {
this.provider = versionDescriptionProvider;
this.settings = settings?.Value ?? new SwaggerSettings();
}
public void Configure(SwaggerUIOptions options) {
provider
.ApiVersionDescriptions
.ToList()
.ForEach(description => {
options.SwaggerEndpoint($"/{settings.RoutePrefixWithSlash}{description.GroupName}/swagger.json",
description.GroupName.ToUpperInvariant());
options.RoutePrefix = settings.RoutePrefix;
});
}
}

Here, I am using IApiVersionDescriptionProvider to add the route prefix and document name for each version. If you do not want to create a separate class, you may use the following code to add swagger middleware

app.UseSwagger(options =>
options.RouteTemplate = settings.RoutePrefixWithSlash + "{documentName}/swagger.json"
);

UI

You can use SwaggerUI or ReDoc to show the API specifications to API users. If you download the asp.net core sample and run it locally, you will see the version dropdown on the right, and selecting version filters the list of API calls.

Rest API version 2.1 Specification

Conclusion

Finally, here is an example implementation I have added to GitHub. And also, I have added a few links that helped me to do the implementation. It is worth reading those to know more. HAPPY CODING…

References

I would like to hear your thoughts