API Versioning
The process of creating different versions of an API to coexist at the same time is called API versioning. By employing this method, we may allow new features and enhancements for clients that are more recent while preserving backward compatibility with older clients. Your application may require breaking updates to your API as it develops. Managing these changes in a way that doesn't affect current users who are dependent on previous API versions is helpful.
There are various ways to implement API versioning in .NET Core web API. The most common ways to implement API versioning are:
- URL versioning: This is the most common approach to implementing API versioning. In this technique, the version is part of the endpoint itself.
- For Example: /api/v1/purchaseOrders
- Query String Versioning: In this technique, the version is passed as a query parameter. This approach maintains the same URL path, with the version indicated by an additional query parameter.
- For Example: /api/purchaseOrders?api-version=1.0
- Header Versioning: In this technique, the version is passed in a custom request header. The version is included in the request headers rather than the URL.
- For Example
GET /api/purchaseOrders
- Headers: x-api-version: 1.0Media Type Versioning (Accept Header Versioning): In this technique, the version is passed via content negotiation. The version is included in the Accept header with a custom media type. The client requests a specific version of the API by setting the Accept header to a custom MIME type.
- For Example
GET /api/purchaseOrder
- Headers: Accept: application/vnd.companyname.v1+json
How to implement API versioning?
How to implement API versioning using URL versioning and enable the token authorization option in Swagger step by step. For this, I am using .NET Core 8.
Create a web API: Create a .NET Core web API and name it "APIVersioingPOC".
Add required packages: Add the below-required packages for API versioning using the Nuget package manager.
Install-Package Asp.Versioning.Mvc
Install-Package Asp.Versioning.Mvc.ApiExplorer
Create Entity Class: Create a folder with the name "Entity" and add an entity class named "PurchaseDetails"
namespace APIVersioingPOC.Entity
{
public class PurchaseDetails
{
public string ProductName { get; set; }
public int Rate { get; set; }
public int Qty { get; set; }
public int Amount { get; set; }
}
}
Create Service: Create a folder with the name "Service" and add an interface with the name "IPurchaseOrderService" and a service class with the name "PurchaseOrderService" as below.
using APIVersioingPOC.Entity;
namespace APIVersioingPOC.Service
{
public interface IPurchaseOrderService
{
List<PurchaseDetails> GetPurchaseOrders();
}
}
using APIVersioingPOC.Entity;
namespace APIVersioingPOC.Service
{
public class PurchaseOrderService: IPurchaseOrderService
{
public List<PurchaseDetails> GetPurchaseOrders()
{
return new List<PurchaseDetails>
{
new PurchaseDetails { ProductName="Laptop", Rate=80000, Qty=2, Amount=160000},
new PurchaseDetails { ProductName="Dekstop", Rate=40000, Qty=1, Amount=40000},
new PurchaseDetails { ProductName="Hard Disk", Rate=4000, Qty=10, Amount=40000},
new PurchaseDetails { ProductName="Pen Drive", Rate=600, Qty=10, Amount=6000},
};
}
}
}
To resolve the dependency, register the service in Program.cs as below.
// Add custom services
builder.Services.AddScoped<IPurchaseOrderService, PurchaseOrderService>();
Configure the versioning: In Program.cs file, add the below code.
var builder = WebApplication.CreateBuilder(args);
// Add API Explorer that provides information about the versions available
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0); // Default API version (v1.0)
options.AssumeDefaultVersionWhenUnspecified = true; // Assume the default version if not specified
options.ReportApiVersions = true; // Report API versions in response headers
options.ApiVersionReader = new UrlSegmentApiVersionReader(); // Use URL segment versioning (e.g., /api/v1/resource)
}).AddApiExplorer(options =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
});
builder.Services.AddSwaggerGen();
var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI(options =>
{
var provider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>();
foreach (var description in provider.ApiVersionDescriptions)
{
options.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant());
}
});
ConfigureSwaggerOptions: Create a folder with the name "OpenApi" and add the ConfigureSwaggerOptions class to configure swagger options.
using Asp.Versioning.ApiExplorer;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace APIVersioingPOC.OpenApi
{
public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions>
{
private readonly IApiVersionDescriptionProvider _provider;
public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider)
{
_provider = provider;
}
public void Configure(SwaggerGenOptions options)
{
foreach (var description in _provider.ApiVersionDescriptions)
{
options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description));
}
// Add token authentication option to pass bearer token
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Description = "Please enter token",
Name = "Authorization",
Type = SecuritySchemeType.Http,
BearerFormat = "JWT",
Scheme = "bearer"
});
// Add security scheme
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] { }
}
});
}
private static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription apiVersionDescription)
{
var info = new OpenApiInfo
{
Title = "API Versioning",
Version = apiVersionDescription.ApiVersion.ToString(),
Description = "Swagger document for API Versioning.",
};
// Add deprecated API description
if (apiVersionDescription.IsDeprecated)
{
info.Description += " This API version has been deprecated.";
}
return info;
}
}
}
Add the code below to the Program.cs
// Add custom services
builder.Services.AddSingleton<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();
Create Controller: Create a controller with the name "PurchaseOrderController". For demo purposes, I have created two versions of the same API endpoint.
using APIVersioingPOC.Service;
using Asp.Versioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace APIVersioingPOC.Controllers
{
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("2.0")]
[Authorize]
public class PurchaseOrderController : ControllerBase
{
private readonly IPurchaseOrderService _purchaseOrderService;
public PurchaseOrderController(IPurchaseOrderService purchaseOrderService)
{
_purchaseOrderService = purchaseOrderService;
}
[HttpGet("GetPurchaseOrders")]
[MapToApiVersion("1.0")]
public IActionResult GetPurchaseOrders()
{
var users = _purchaseOrderService.GetPurchaseOrders();
return Ok(users);
}
[HttpGet("GetPurchaseOrders")]
[MapToApiVersion("2.0")]
public IActionResult GetPurchaseOrdersV2()
{
var purchaseDetails = _purchaseOrderService.GetPurchaseOrders();
return Ok(purchaseDetails);
}
}
}
Let's run the project
For default version V1 you will get the swagger document as below.
When you use the V2 option in the "Select a Definition" dropdown box, the swagger document will appear as seen below.
You can pass the authentication token as below and click on the Authorize button.
In this way, we learned how to implement API versioning and enable authorization in Swagger UI.
Happy Learning!