API Versioning in ASP.NET Core
Learn about different strategies for API versioning in ASP.NET Core.
API Versioning in ASP.NET Core
A comprehensive guide to implementing API versioning strategies in ASP.NET Core applications.
Why API Versioning Matters
As your API evolves, you will change data shapes, endpoints, or behavior. Without versioning, you risk breaking existing clients. Versioning gives you a controlled way to introduce changes while continuing to support older clients.
Common Versioning Strategies
1. URL Path Versioning
In URL path versioning, the version number is included in the API endpoint URL. This approach is explicit and makes it clear which version of the API is being accessed.
GET /api/v1/users
GET /api/v2/users
Pros:
- Discoverability: The API version is immediately visible in the URL
- Simplicity: Straightforward to implement and test
- Cache-friendly: Different versions have different URLs
Cons:
- URL proliferation: Multiple versions mean multiple endpoints
- Resource duplication: Similar logic across versions
2. Header Versioning
Header versioning uses HTTP headers to specify the API version, typically through an X-API-Version header or custom media types.
GET /api/users
X-API-Version: 2
Pros:
- Clean URLs: Same URL for all versions
- Flexibility: Clients can negotiate versions without changing URLs
- RESTful: Follows HTTP standards for content negotiation
Cons:
- Less discoverable: Version not visible in the URL
- Harder to test manually (requires headers)
3. Query Parameter Versioning
The version is passed as a query parameter in the request.
GET /api/users?v=2
Pros:
- Simple to implement
- Easy to test in browsers
Cons:
- Can cause caching issues
- Less RESTful approach
- Less explicit in code
4. Content Negotiation (Media Type)
Use custom Accept header media types to specify the version.
GET /api/users
Accept: application/vnd.myapp.v2+json
Pros:
- Highly RESTful
- Follows HTTP standards
- Clean URLs
Cons:
- More complex to implement
- Less discoverable for developers
Implementation in ASP.NET Core
Using API Versioning NuGet Package
The easiest approach is to use the Asp.Versioning.Mvc NuGet package.
dotnet add package Asp.Versioning.Mvc
Setup in Program.cs
using Asp.Versioning;
var builder = WebApplicationBuilder.CreateBuilder(args);
// Add API versioning
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
options.ReportApiVersions = true;
options.AssumeDefaultVersionWhenUnspecified = true;
options.ApiVersionReader = ApiVersionReader.Combine(
new UrlSegmentApiVersionReader(),
new HeaderApiVersionReader("X-API-Version")
);
})
.AddApiExplorer(options =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
});
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();
Version by URL Path
Create controllers with the [ApiVersion] attribute:
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
public class UsersController : ControllerBase
{
[HttpGet]
public IActionResult GetUsers()
{
return Ok(new[]
{
new { id = 1, name = "Alice" },
new { id = 2, name = "Bob" }
});
}
}
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("2.0")]
public class UsersV2Controller : ControllerBase
{
[HttpGet]
public IActionResult GetUsers()
{
return Ok(new[]
{
new { id = 1, name = "Alice", email = "alice@example.com", roles = new[] { "Admin" } },
new { id = 2, name = "Bob", email = "bob@example.com", roles = new[] { "User" } }
});
}
}
Version by Header
[ApiController]
[Route("api/[controller]")]
[ApiVersion("1.0")]
[ApiVersion("2.0")]
public class ProductsController : ControllerBase
{
[HttpGet]
public IActionResult GetProducts()
{
var apiVersion = HttpContext.GetRequestedApiVersion();
if (apiVersion?.MajorVersion == 2)
{
return Ok(new[]
{
new { id = 1, name = "Product A", price = 29.99m, stock = 100 },
new { id = 2, name = "Product B", price = 49.99m, stock = 50 }
});
}
return Ok(new[]
{
new { id = 1, name = "Product A", price = 29.99m },
new { id = 2, name = "Product B", price = 49.99m }
});
}
}
Deprecating API Versions
Use the Deprecated property to mark versions as no longer supported:
[ApiVersion("1.0", Deprecated = true)]
[ApiVersion("2.0")]
public class OrdersController : ControllerBase
{
// Implementation
}
Migration Strategies
- Announce Early: Give clients advance notice before deprecating versions
- Support Window: Keep old versions alive for 6-12 months after deprecation
- Gradual Migration: Provide migration guides for clients
- Sunset Headers: Include deprecation headers in responses:
Response.Headers.Add("Sunset", "Sun, 01 Jan 2025 00:00:00 GMT");
Response.Headers.Add("Deprecated", "true");
- Monitor Usage: Track which API versions clients are using
- Clear Communication: Document breaking changes clearly
Best Practices
- Be consistent: Choose one versioning strategy and stick with it
- Version early: Start with versioning from day one
- Avoid minor versions in URLs: Use major versions for breaking changes
- Document thoroughly: Explain differences between versions
- Minimize duplication: Use inheritance or composition to share logic
- Monitor deprecated versions: Know when clients have migrated
- Test all versions: Ensure each version works as expected
Conclusion
API versioning is essential for maintaining backward compatibility while evolving your API. ASP.NET Core provides excellent tooling through the Asp.Versioning NuGet package to implement any versioning strategy effectively.
Choose the strategy that best fits your use case:
- Use URL path versioning for maximum clarity and simplicity
- Use header versioning for RESTful APIs with stable URLs
- Use media type negotiation for highly RESTful implementations
Plan your versioning strategy early and communicate deprecations clearly to your clients.