Managing Multiple Versions of Your API with .NET and Swagger

Said El Dah
6 min readDec 10, 2022

--

REST API versioning is a critical aspect of maintaining a successful and well-designed API. By including a version number in the URL or headers of an HTTP request, developers can ensure that clients of the API are using the correct version and can make changes to the API without breaking existing clients. There are several different approaches to versioning REST APIs, including using the version number in the URL, using the Accept header, and using custom headers. In this article, we will explore the different approaches to REST API versioning and discuss the pros and cons of each approach. We will also look at some best practices for implementing REST API versioning in your own projects.

When it comes to building REST APIs in .NET, versioning is an important consideration. By including a version number in the URL or headers of an HTTP request, developers can ensure that clients of the API are using the correct version and can make changes to the API without breaking existing clients. There are several different approaches to versioning REST APIs in .NET, including using the version number in the URL, using the Accept header, and using custom headers. In this article, we will explore the different approaches to REST API versioning in .NET and discuss the pros and cons of each approach. We will also look at some best practices for implementing REST API versioning in your .NET projects.

One common approach to REST API versioning in .NET is to use the version number in the URL. For example, the URL for a version 1 API might look like this:

https://www.example.com/api/v1/

Using the version number in the URL has the advantage of being easy to understand and implement. It also makes it clear to clients of the API which version they are using. However, using the version number in the URL can make it difficult to support multiple versions of the API at the same time. It can also make it difficult to make changes to the API, as any changes that break existing clients will require the API URL to be changed.

Another approach to REST API versioning in .NET is to use the Accept header. This allows the client of the API to specify the version of the API they want to use. For example, a client might send a request like this:

GET /api/products HTTP/1.1
Host: www.example.com
Accept: application/vnd.example-v1+json
ample-v1+json

Using the Accept header has the advantage of making it easier to support multiple versions of the API simultaneously. It also allows the API provider to make changes to the API without changing the URL. However, using the Accept header can be more complex to implement and may not be as intuitive for clients of the API.

The third approach to REST API versioning in .NET is custom headers. This allows the API provider to specify the version of the API in a custom HTTP header, rather than in the URL or the Accept header. For example, the API provider might specify the version of the API like this:

GET /api/products HTTP/1.1
Host: www.example.com
X-API-Version: 1
HTTP/1.1 Host: www.example.com X-API-Version: 1

Using custom headers has the advantage of being flexible and allowing the API provider to choose the best versioning scheme for their API. However, it can also be more complex to implement and may not be as intuitive for clients of the API.

Setup API versioning in .Net 6

To set up API versioning in .NET, you need to install two NuGet packages:

  • Microsoft.AspNetCore.Mvc.Versioning
  • Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer

These packages provide support for implementing API versioning and generating metadata about the API, respectively. You can install both packages using the following command:

dotnet add package Microsoft.AspNetCore.Mvc.Versioning
dotnet add package Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer

After installing the packages, you can use them to add version information to the URL endpoint of your API or to the headers of requests. This can help you to maintain backward compatibility and manage multiple versions of your API. The metadata generated by the ApiExplorer package can be useful for generating interactive documentation or for testing API endpoints.

you need to add the following code to Program.cs after installing the packages.

builder.Services.AddApiVersioning(opt =>
{
opt.DefaultApiVersion = new ApiVersion(1, 0);
opt.AssumeDefaultVersionWhenUnspecified = true;
opt.ReportApiVersions = true;
opt.ApiVersionReader = ApiVersionReader.Combine(new UrlSegmentApiVersionReader(),
new HeaderApiVersionReader("x-api-version"),
new MediaTypeApiVersionReader("x-api-version"));
});

builder.Services.AddVersionedApiExplorer(setup =>
{
setup.GroupNameFormat = "'v'VVV";
setup.SubstituteApiVersionInUrl = true;
});

Now we have the API versioning setup and ready, let’s try to use it to version a controller.

Create 2 version folders and the Controller folder and create 2 user controllers.

To implement API versioning in .NET, you can use attributes such as Route to specify the URL endpoint or headers for accessing different versions of your API. For example, you can use the Route attribute with the apiVersion parameter to specify the version number in the URL endpoint, like this:

[Route("api/v{version:apiVersion}/[controller]")]

This will allow you to access different versions of your API using the version number in the URL.

Alternatively, you can use the Route attribute without the apiVersion parameter to specify a default endpoint, and use the x-api-version header to specify the version of the API in requests. For example:

[Route("api/[controller]")]

With this approach, clients can specify the version of the API they want to use using the x-api-version header in their requests. This can be useful if you want to allow clients to access different versions of your API using the same endpoint.

Under v1 Folder Create a User Controller

using Microsoft.AspNetCore.Mvc;

namespace WebApiVersioningTest.Controllers.V1;


[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[Route("api/[controller]")]
[ApiVersion("1.0")]
public class UserController : ControllerBase
{


[MapToApiVersion("1.0")]
[HttpGet]
public IActionResult Get()
{
return Ok("V1");
}
}

Under v2 Folder Create the User V2 controller:

using Microsoft.AspNetCore.Mvc;

namespace WebApiVersioningTest.Controllers.V2;


[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[Route("api/[controller]")]
[ApiVersion("2.0")]
public class UserController : ControllerBase
{


[MapToApiVersion("2.0")]
[HttpGet]
public IActionResult Get()
{
return Ok("V2");
}
}

Swagger Versioning Configuration

To configure Swagger to work with different versions of an API in .NET, you can implement the IConfigureNamedOptions<SwaggerGenOptions> interface and use it to register each API version and identify the default version. This can be done by creating a ConfigureSwaggerOptions class that implements the Configure method and the IConfigureNamedOptions<SwaggerGenOptions> interface, like this:

public class ConfigureSwaggerOptions
: IConfigureNamedOptions<SwaggerGenOptions>
{
private readonly IApiVersionDescriptionProvider _provider;

public ConfigureSwaggerOptions(
IApiVersionDescriptionProvider provider)
{
_provider = provider;
}

/// <summary>
/// Configure each API discovered for Swagger Documentation
/// </summary>
/// <param name="options"></param>
public void Configure(SwaggerGenOptions options)
{
// add swagger document for every API version discovered
foreach (var description in _provider.ApiVersionDescriptions)
{
options.SwaggerDoc(
description.GroupName,
CreateVersionInfo(description));
}
}

/// <summary>
/// Configure Swagger Options. Inherited from the Interface
/// </summary>
/// <param name="name"></param>
/// <param name="options"></param>
public void Configure(string name, SwaggerGenOptions options)
{
Configure(options);
}

/// <summary>
/// Create information about the version of the API
/// </summary>
/// <param name="description"></param>
/// <returns>Information about the API</returns>
private OpenApiInfo CreateVersionInfo(
ApiVersionDescription desc)
{
var info = new OpenApiInfo()
{
Title = ".NET Core (.NET 6) Web API",
Version = desc.ApiVersion.ToString()
};

if (desc.IsDeprecated)
{
info.Description += " This API version has been deprecated. Please use one of the new APIs available from the explorer.";
}

return info;
}
}

After creating this class, you can add the following code to your Program.cs file to use it for configuring Swagger:

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
var apiVersionDescriptionProvider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>();

app.UseSwagger();
app.UseSwaggerUI(options =>
{
foreach (var description in apiVersionDescriptionProvider.ApiVersionDescriptions)
{
options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json",
description.GroupName.ToUpperInvariant());
}
});
}

After implementing this code, the Swagger UI should display documentation for each version of the API. This can be useful for providing API consumers with information about the different versions of the API and for testing different versions of the API.

In summary, there are several different approaches to REST API versioning in .NET, each with its own pros and cons. The best approach for your situation will depend on the specific requirements of your API and the preferences of your team. It’s important to carefully consider all the options and choose the approach that will work best for your situation.

The full code for this project can be found in the following repository: https://github.com/saideldah/api-versioining-dot-net-6

--

--

Said El Dah

Experienced software professional with a strong background in software products. Skilled in driving digital transformation, and managing teams.