Api Versioning in ASP.Net Core

yawo
December 22, 2023

Here are the most common API versioning techniques:

URI versioning: by using a a different domain or URL segment for each version. E.g: site.com/v1/Method
Query string versioning: by using a GET parameter in the URL. E.g: site.com/Method?api-version=1.0
Route versioning: by using a different route. E.g: site.com/Method-v1, site.com/Method-v2
Media Type versioning: using the standard Accept HTTP header to indicate the version like: Accept: application/json;api-version=2.0
Header versioning: using a custom HTTP Header like Accept-Version:2.0

The most used versioning format used is semantic versioning, also known as SemVer which is basically: MAJOR.MINOR.PATCH

MAJOR version when you make incompatible API changes
MINOR version when you add functionality in a backward compatible manner
PATCH version when you make backward compatible bug fixes

URI versioning with ASP.Net Core

Install the required packages:

  • Asp.Versioning.Mvc
  • Asp.Versioning.Mvc.ApiExplorer

Basic Services Configuration:

builder.Services.AddApiVersioning(options => {
    options.ReportApiVersions = true;
    options.ApiVersionReader = new UrlSegmentApiVersionReader();
    options.AssumeDefaultVersionWhenUnspecified = true;
    options.DefaultApiVersion = new ApiVersion(1, 0);
}) // From Package: Asp.Versioning.Mvc
    .AddApiExplorer(options => {
        // version format as "'v'major[.minor][-status]"
        options.GroupNameFormat = "'v'VVV";

        // this option is used when versioning by url segment
        options.SubstituteApiVersionInUrl = true;
    }); // From Package: Asp.Versioning.Mvc.ApiExplorer

Swagger Configuration (Optional)

builder.Services.AddSwaggerGen(options => {
    options.SwaggerDoc(
        "v1",
        new OpenApiInfo { Title = "WeatherList", Version = "v1.0" });
    options.SwaggerDoc(
        "v2",
        new OpenApiInfo { Title = "WeatherList", Version = "v2.0" });
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(options => {
        options.SwaggerEndpoint(
            $"/swagger/v1/swagger.json",
            $"WeatherList v1");
        options.SwaggerEndpoint(
            $"/swagger/v2/swagger.json",
            $"WeatherList v2");
    });
}

Folder-based versioning structure

Controller

using ApiVersionning.Models.v1;
using Asp.Versioning;
using Microsoft.AspNetCore.Mvc;

namespace ApiVersionning.Controllers.v1
{
    [Route("v{version:apiVersion}/[controller]")]
    [ApiController]
    [ApiVersion("1.0")]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };

        private readonly ILogger<WeatherForecastController> _logger;

        public WeatherForecastController(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }

        [HttpGet(Name = "GetWeatherForecast")]
        public IEnumerable<WeatherForecast> Get()
        {
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            })
            .ToArray();
        }
    }
}