Skip to content

API Versioning


Installation

  1. Install required NuGet packages
    dotnet add package Asp.Versioning.Mvc
    dotnet add package Asp.Versioning.Mvc.ApiExplorer
    

Configuration

  1. Configure API Versioning

    • Add this before builder.Services.AddControllers();

      builder.Services.AddApiVersioning(options =>
      {
          options.DefaultApiVersion = new ApiVersion(1, 0);
          options.AssumeDefaultVersionWhenUnspecified = true;
      
          // Report supported API versions in response headers
          options.ReportApiVersions = true;
      
          // Versioning strategy (URL-based)
          options.ApiVersionReader = new UrlSegmentApiVersionReader();
      });
      

    • Optional (recommended): API Explorer for Swagger

      builder.Services.AddApiVersioning()
      .AddApiExplorer(options =>
      {
          options.GroupNameFormat = "'v'VVV";
          options.SubstituteApiVersionInUrl = true;
      });
      


Usage

  • Update Controller
    [ApiController]
    [ApiVersion("1.0")]
    [Route("api/v{version:apiVersion}/account")]
    public sealed class AccountController : ControllerBase
    {
        ...
    }
    

Multiple versioning

[ApiVersion("1.0")]
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/account")]
public sealed class AccountController : ControllerBase
{
    [HttpPost("login")]
    [MapToApiVersion("1.0")]
    public IActionResult LoginV1(...) { }

    [HttpPost("login")]
    [MapToApiVersion("2.0")]
    public IActionResult LoginV2(...) { }
}

Versioning strategies (choose ONE)

  1. URL Segment (recommended)
    /api/v1/account/login
    
Config
options.ApiVersionReader = new UrlSegmentApiVersionReader();
  1. Header-based
    X-API-Version: 1.0
    
Config
options.ApiVersionReader =
    new HeaderApiVersionReader("X-API-Version");
  1. Query string
    /api/account/login?api-version=1.0
    
Config
options.ApiVersionReader =
    new QueryStringApiVersionReader("api-version");

Middleware order (important)

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();


Mark deprecated

  1. Mark API version as deprecated (Controller level)
    [ApiController]
    [ApiVersion("1.0", Deprecated = true)]
    [ApiVersion("2.0")]
    [Route("api/v{version:apiVersion}/account")]
    public sealed class AccountController : ControllerBase
    {
        ...
    }
    
  2. v1 still works
  3. v1 is marked as deprecated
  4. v2 is the current version

  5. Map actions explicitly (recommended)

    [HttpPost("login")]
    [MapToApiVersion("1.0")]
    public IActionResult LoginV1(LoginModel model)
    {
        // old behavior
    }
    
    [HttpPost("login")]
    [MapToApiVersion("2.0")]
    public IActionResult LoginV2(LoginModel model)
    {
        // new behavior
    }
    

  6. Enable API version reporting

    options.ReportApiVersions = true;
    

  7. Resulting response headers for v1 requests

    api-supported-versions: 1.0, 2.0
    api-deprecated-versions: 1.0
    

  8. Optional: Add deprecation warning header (recommended)

  9. Add a filter

    public sealed class ApiDeprecationHeaderFilter : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context) { }
    
        public void OnActionExecuted(ActionExecutedContext context)
        {
            var apiVersion = context.HttpContext.GetRequestedApiVersion();
    
            if (apiVersion?.MajorVersion == 1)
            {
                context.HttpContext.Response.Headers.Add(
                    "Warning",
                    "299 - \"API v1 is deprecated and will be removed in a future release\""
                );
            }
        }
    }
    

  10. Register it globally

    builder.Services.AddControllers(options =>
    {
        options.Filters.Add<ApiDeprecationHeaderFilter>();
    });
    

  11. Optional: Swagger deprecation (if you use Swagger)

    [ApiVersion("1.0", Deprecated = true)]
    

  12. Optional: Hard-block v1 after grace period

  13. Option A — remove v1 mapping: the response=> 404 Unsupported API Version

    // Remove ApiVersion("1.0")
    [ApiVersion("2.0")]
    

  14. Option B — explicit rejection (custom message)

    [HttpPost("login")]
    [MapToApiVersion("1.0")]
    public IActionResult LoginV1()
    {
        return StatusCode(
            StatusCodes.Status410Gone,
            "API v1 has been removed. Please upgrade to v2."
        );
    }
    

Recommended deprecation timeline

Phase Action
Phase 1 Mark deprecated (Deprecated = true)
Phase 2 Add warning headers
Phase 3 Notify consumers
Phase 4 Block with 410 Gone
Phase 5 Remove code