API Versioning in ASP.NET Core
What is API Versioning?
API versioning means creating different versions of an API so that changes don’t break existing users.
API versioning = updating your API without breaking old users
Why API Versioning is needed
When you update an API:
- Old apps may stop working
- Users may get errors
So instead of changing the old API, you create a new version.
API Versioning Dependencies in .NET (ASP.NET Core)
To implement API versioning in ASP.NET Core, you mainly need these NuGet packages:
1. Required Package
- Asp.Versioning.Mvc
This is the core package that enables API versioning.
2. Optional (but recommended)
Asp.Versioning.Mvc.ApiExplorer
Used for:
- Swagger integration
- Viewing versioned APIs clearly
Installation Commands
Using .NET CLI:
dotnet add packageAsp.Versioning.Mvc
dotnet add package Asp.Versioning.Mvc.ApiExplorer
dotnet add package Swashbuckle.AspNetCore
Simple Summary
|
Package |
Purpose |
|
Asp.Versioning.Mvc |
Core API versioning |
|
|
Supports Swagger/version discovery |
|
Swashbuckle.AspNetCore |
Swagger UI |
You must install the first package, others depend on your needs (especially Swagger).
Types of API Versioning in .NET (ASP.NET Core)
In ASP.NET Core (using
Microsoft.AspNetCore.Mvc.Versioning), you can version APIs in a few common ways.
1. URL Path Versioning (Most Common)
Version is included in the URL.
/api/v1/users
/api/v2/users
Pros
- Easy to understand
- Simple to test
Cons
- URL changes for every version
2. Query String Versioning
Version is passed as a query parameter.
/api/users?api-version=1.0
Pros
- Same URL structure
- Easy to try in browser
Cons
- Less clean than URL versioning
3. Header Versioning
Version is sent in request headers.
api-version: 1.0
Pros
- Clean URLs
- Good for enterprise APIs
Cons
- Harder to test manually
4. Media Type (Content Negotiation) Versioning
Version is passed in the Accept header.
Accept: application/json;v=1.0
Pros
- Very flexible
- Keeps URL clean
Cons
- More complex
- Not beginner-friendly
5. Namespace Versioning (Code-level)
Different versions are separated using namespaces.
Controllers.V1
Controllers.V2
Pros
- Clean code organization
- Easy to maintain
Cons
- Not visible to API users directly
Simple Summary
|
Type |
Where version is placed |
|
URL Path |
In URL (/v1/) |
|
Query String |
In query (?api-version=1) |
|
Header |
In request header |
|
Media Type |
In Accept header |
|
Namespace |
In code structure |
Key Point
URL versioning is the most commonly used and easiest to understand.
Project Structure (Versioning with Different Folders & Namespaces)
Controllers
└── v1
└── WeatherForecastController.cs
└── v2
└── WeatherForecastController.cs
This is a clean enterprise-style API versioning approach.
- Each version has:
- Separate folder
- Separate namespace
- Separate controller implementation
· You can support:
|
Version |
URL |
|
Old clients (V1) |
/api/weatherforecast |
|
New clients (V2) |
/api/v2/weatherforecast |
1. Install Required Package
dotnet add package Asp.Versioning.Mvc
Program.cs
using Asp.Versioning;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
// If version not specified -> use V1
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Folder Structure
Controllers
├── v1
│ └── WeatherForecastController.cs
│
└── v2
└── WeatherForecastController.cs
V1 Controller
Controllers/v1/WeatherForecastController.cs
using Asp.Versioning;
using Microsoft.AspNetCore.Mvc;
namespace WebAPIVersioning.Controllers.v1
{
[ApiController]
[ApiVersion("1.0")]
// Old route (without version)
[Route("api/[controller]")]
// Optional: also support /api/v1/weatherforecast
[Route("api/v{version:apiVersion}/[controller]")]
public class WeatherForecastController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Ok(new
{
Version = "V1",
Message = "Old Client API"
});
}
}
}
V2 Controller
Controllers/v2/WeatherForecastController.cs
using Asp.Versioning;
using Microsoft.AspNetCore.Mvc;
namespace WebAPIVersioning.Controllers.v2
{
[ApiController]
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class WeatherForecastController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Ok(new
{
Version = "V2",
Message = "New Version API"
});
}
}
}
Working Endpoints
|
URL |
Version |
|
|
V1 |
|
|
V1 |
|
|
V2 |
How it works
This line:
options.AssumeDefaultVersionWhenUnspecified=true;
means:
If client does not send version,
automatically use default version (V1).
Best Practice
This approach is ideal when:
- Existing production clients already use unversioned URLs
- You cannot break old applications
- You want gradual migration to versioned APIs
Recommended Migration Strategy
|
Stage |
URL |
|
Existing system |
|
|
Introduce V2 |
|
|
Future migration |
Encourage clients to move to |
|
Later |
Deprecate old route |
Optional: Mark V1 as Deprecated
[ApiVersion("1.0",Deprecated=true)]
Then response headers will indicate:
api-deprecated-versions: 1.0
Key Points
|
Feature |
Explanation |
|
Separate folders |
|
|
Separate namespaces |
|
|
Same controller name |
Allowed because namespace differs |
|
Same endpoint name |
Differentiated using API version |
|
Backward compatibility |
Old clients can still use v1 |
Single Controller Versioning (Same Namespace)
In this approach:
- Only ONE controller
- Same namespace
- Multiple API versions inside same controller
· Old clients continue using:
/api/weatherforecast
· New clients use:
/api/v2/weatherforecast
Program.cs
using Asp.Versioning;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddApiVersioning(options =>
{
// Default Version
options.DefaultApiVersion = new ApiVersion(1, 0);
// If no version specified -> use V1
options.AssumeDefaultVersionWhenUnspecified = true;
// Shows supported versions in headers
options.ReportApiVersions = true;
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Controller
Controllers/WeatherForecastController.cs
using Asp.Versioning;
using Microsoft.AspNetCore.Mvc;
namespace WebAPIVersioning.Controllers
{
[ApiController]
// Supported versions
[ApiVersion("1.0")]
[ApiVersion("2.0")]
// Old route
[Route("api/[controller]")]
// Versioned route
[Route("api/v{version:apiVersion}/[controller]")]
public class WeatherForecastController : ControllerBase
{
// ---------------- V1 ----------------
[HttpGet]
[MapToApiVersion("1.0")]
public IActionResult GetV1()
{
return Ok(new
{
Version = "V1",
Message = "Old Client API Response"
});
}
// ---------------- V2 ----------------
[HttpGet]
[MapToApiVersion("2.0")]
public IActionResult GetV2()
{
return Ok(new
{
Version = "V2",
Message = "New API Response"
});
}
}
}
Working Endpoints
|
URL |
Version |
Method |
|
|
V1 |
|
|
|
V1 |
|
|
|
V2 |
|
How Version Resolution Works
Case 1
GET /api/weatherforecast
No version specified.
Because of:
options.AssumeDefaultVersionWhenUnspecified=true;
ASP.NET automatically uses:
Version 1.0
So:
GetV1()
executes.
Case 2
GET /api/v2/weatherforecast
Route contains:
v2
So:
GetV2()
executes.
Important Concept
Even though both methods use:
[HttpGet]
there is NO ambiguity because:
[MapToApiVersion("1.0")]
[MapToApiVersion("2.0")]
helps ASP.NET select the correct method.
When to Use This Approach
Use single-controller versioning when:
- Changes between versions are small
- API is simple
- Logic is mostly shared
- Easier maintenance is preferred
When NOT to Use
Avoid this approach when:
- V1 and V2 are completely different
- Controller becomes too large
- Business logic changes heavily
Then use:
Controllers/v1
Controllers/v2
approach instead.
Best Practice Recommendation
|
Scenario |
Recommended Approach |
|
Small changes between versions |
Single controller |
|
Large breaking changes |
Separate folders/controllers |
|
Enterprise large APIs |
Separate version folders |
|
Existing legacy clients |
Support unversioned V1 route |
Different between [HttpGet(Name = "GetWeatherForecastDetails")] and [HttpGet("GetWeatherForecastDetails")]
This is a very important distinction in ASP.NET Core routing. These two may look similar, but they behave completely differently.
1. [HttpGet("GetWeatherForecastDetails")]
What it does
- Defines the URL path (route template)
[HttpGet("GetWeatherForecastDetails")]
Resulting endpoint
GET /api/weatherforecast/GetWeatherForecastDetails
Meaning
You are explicitly telling ASP.NET:
“Call this method when this specific URL is hit.”
2. [HttpGet(Name
= "GetWeatherForecastDetails")]
What it does
- Assigns a name to the route
- Does NOT change the URL
[HttpGet(Name="GetWeatherForecastDetails")]
Resulting endpoint
GET /api/weatherforecast
Meaning
“This route has an internal name, which I can use later for URL generation.”
3. Side-by-side comparison
|
Feature |
|
|
|
Affects URL? |
Yes |
No |
|
Defines route path? |
Yes |
No |
|
Used for routing incoming request |
Yes |
No |
|
Used for URL generation |
No |
Yes |
|
Required for API to work |
Often yes |
Optional |
4. Real-world example
Using route template
[HttpGet("details")]
publicIActionResultGetDetails()
👉 Call:
GET /api/weatherforecast/details
Using route name
[HttpGet(Name="GetDetails")]
publicIActionResultGet()
👉 Call:
GET /api/weatherforecast
👉 But inside code:
Url.RouteUrl("GetDetails");
5. When to use each
Use [HttpGet("...")] when:
- You want different endpoints
- You need multiple GET methods
- You want meaningful URLs
✔ Most common usage
Use Name = "..." when:
- You need to generate URLs dynamically
- Using:
CreatedAtRoute()Url.RouteUrl()
✔ Advanced / optional usage
6. Common mistake
[HttpGet(Name="details")]
publicIActionResultGetDetails()
❌ This does NOT create:
/api/weatherforecast/details
✔ It still remains:
/api/weatherforecast
7. Simple analogy
[HttpGet("details")]→ Actual address (door location)Name = "details"→ Label in system (used internally)
Final takeaway
If you want to change the URL → use "..."
If you want to give a name → use Name =
"..."
Shall we use both [HttpGet(Name = "GetWeatherForecastDetails")] and [HttpGet("GetWeatherForecastDetails")]
Short answer: Yes, you can use both — and they serve different purposes.
How it works
You can combine them like this:
[HttpGet("GetWeatherForecastDetails",Name="GetWeatherForecastDetails")]
publicIEnumerable<WeatherForecast>GetDetails()
{
...
}
What each part does
|
Part |
Purpose |
|
|
Defines the URL path |
|
|
Assigns an internal route name |
Result
URL to call API
GET /api/weatherforecast/GetWeatherForecastDetails
Internal usage (inside code)
Url.RouteUrl("GetWeatherForecastDetails");
When should you use both?
Use both when:
- You want a custom URL path
- AND you want to reference that route internally
Example scenario:
returnCreatedAtRoute("GetWeatherForecastDetails",new{id=1},data);
Best practice
Usually:
✔ Most common
[HttpGet("details")]
✔ When needed (advanced scenarios)
[HttpGet("details",Name="GetDetailsRoute")]
Important note
- The name does NOT affect routing
- The string parameter DOES affect routing
Common mistake to avoid
[HttpGet(Name="details")]
❌ This does NOT create /details route
Clean recommendation
Use both only if you really need route naming (like in CreatedAtRoute).
Otherwise, keep it simple:
[HttpGet("details")]
Final takeaway
You can use both together
But:
"..."→ controls URLName = "..."→ used internally
What is use of 'Asp.Versioning.Mvc.ApiExplorer' LIBRARY ?
Asp.Versioning.Mvc.ApiExplorer — Purpose
Asp.Versioning.Mvc.ApiExplorer is mainly used for:
- Swagger/OpenAPI integration
- Discovering API versions
- Generating separate Swagger documents for each API version
Without this package:
- API versioning may still work
- But Swagger usually cannot properly display:
- V1
- V2
- Separate endpoints per version
Main Purpose
It helps Swagger understand:
/api/v1/weatherforecast
/api/v2/weatherforecast
are different API versions.
Required Packages
Usually both are installed together:
dotnet add package Asp.Versioning.Mvc
dotnet add package Asp.Versioning.Mvc.ApiExplorer
What happens WITHOUT ApiExplorer
You may see issues like:
- Swagger shows duplicate endpoints
- V1/V2 not separated
- Swagger conflicts
- Missing API versions
- 404 swagger.json errors sometimes
Example problem:
Conflicting method/path combination
because Swagger sees:
GET /api/weatherforecast
GET /api/weatherforecast
and cannot differentiate versions.
What ApiExplorer Adds
It adds version metadata for:
- Swagger
- Endpoint discovery
- API documentation
using Asp.Versioning;
using Asp.Versioning.ApiExplorer;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
})
.AddApiExplorer(options =>
{
// Format: v1, v2
options.GroupNameFormat = "'v'VVV";
// Replaces version in URL
options.SubstituteApiVersionInUrl = true;
});
builder.Services.AddSwaggerGen(options =>
{
var provider = builder.Services.BuildServiceProvider()
.GetRequiredService<Asp.Versioning.ApiExplorer.IApiVersionDescriptionProvider>();
foreach (var description in provider.ApiVersionDescriptions)
{
options.SwaggerDoc(description.GroupName, new Microsoft.OpenApi.Models.OpenApiInfo
{
Title = $"API {description.ApiVersion}",
Version = description.GroupName
});
}
});
AddApiExplorer() — Purpose
.AddApiExplorer(options =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
});
This is mainly used for:
- Swagger
- API documentation
- API version discovery
It helps Swagger correctly understand and display versioned APIs.
1. options.GroupNameFormat
= "'v'VVV"
Purpose
Defines how version names appear in Swagger.
Meaning of 'v'VVV
'v'
= literal character v
VVV
= API version number
Output
|
API Version |
Group Name |
|
1.0 |
v1 |
|
2.0 |
v2 |
|
1.1 |
v1.1 |
Query String, header and Media Type API Versioning.
Program.cs
using Asp.Versioning;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddApiVersioning(options =>
{
// Default API Version
options.DefaultApiVersion = new ApiVersion(1, 0);
// Use default version when version not specified
options.AssumeDefaultVersionWhenUnspecified = true;
// Shows supported/deprecated versions in response headers
options.ReportApiVersions = true;
// Multiple Version Readers
options.ApiVersionReader = ApiVersionReader.Combine(
// Query String Versioning
new QueryStringApiVersionReader("api-version"),
// Header Versioning
new HeaderApiVersionReader("x-api-version"),
// Media Type Versioning
new MediaTypeApiVersionReader("ct-api-version")
);
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Controller
using Asp.Versioning;
using Microsoft.AspNetCore.Mvc;
namespace WebAPIVersioning.Controllers
{
[ApiController]
[ApiVersion("1.0")]
[ApiVersion("2.0")]
[Route("api/[controller]")]
public class WeatherForecastController : ControllerBase
{
// ---------------- Version 1 ----------------
[HttpGet]
[MapToApiVersion("1.0")]
public IActionResult GetV1()
{
return Ok(new
{
Version = "V1",
Message = "Weather Forecast Version 1"
});
}
// ---------------- Version 2 ----------------
[HttpGet]
[MapToApiVersion("2.0")]
public IActionResult GetV2()
{
return Ok(new
{
Version = "V2",
Message = "Weather Forecast Version 2"
});
}
}
}
How to Call APIs
1. Query String Versioning
GET https://localhost:7246/api/weatherforecast?api-version=1.0
GET https://localhost:7246/api/weatherforecast?api-version=2.0
2. Header Versioning
Headers
x-api-version: 1.0
Request
GET https://localhost:7246/api/weatherforecast
3. Media Type Versioning
Headers
Accept: application/json; ct-api-version=2.0
Request
GET https://localhost:7246/api/weatherforecast
What ApiVersionReader.Combine() Does
ApiVersionReader.Combine(...)
allows your API to read version information from multiple locations.
Version Detection Order
The API checks:
1. Query String
2. Header
3. Media Type
until it finds a version.
Example
If request contains:
GET /api/weatherforecast?api-version=2.0
API uses:
Version 2.0
If query string absent, then it checks headers.
Best Practice
|
Versioning Type |
Recommended Usage |
|
URL Versioning |
Public APIs |
|
Header Versioning |
Enterprise/internal APIs |
|
Query String |
Testing/simple APIs |
|
Media Type |
REST mature APIs |
Important Note
If multiple versions are provided in the same request:
- The first matching reader usually wins.
Example:
?api-version=1.0
x-api-version: 2.0
Query string may take priority depending on reader order.
Beginner Level Interview Questions
1. What is API versioning in .NET?
API versioning means managing different versions of an API in a .NET application.
It helps support old and new clients at the same time.
2. Why do we need API versioning?
We need API versioning to avoid breaking existing applications when APIs change.
It helps maintain backward compatibility.
3. What are the different types of API versioning available in ASP.NET Core?
Types of API versioning are URL versioning, Query String versioning, Header versioning, and Media Type versioning.
Each method sends the API version differently.
4. What is URL versioning?
URL versioning means passing the version in the API URL.
Example:
api/v1/products
5. How do you implement API versioning in ASP.NET Core?
Install the API versioning package and configure it in Program.cs.
Example:
builder.Services.AddApiVersioning();
6. Which NuGet package is commonly used for API versioning in .NET?
Common NuGet package:
Asp.Versioning.Mvc
7. What is the purpose of [ApiVersion] attribute?
[ApiVersion] defines the API version supported by a controller or action.
Example:
[ApiVersion("1.0")]
8. What does AssumeDefaultVersionWhenUnspecified mean?
It uses the default API version when the client does not provide any version.
This prevents version missing errors.
9. What is the default API version?
Default API version is the version automatically used when no version is specified.
Example:
DefaultApiVersion=newApiVersion(1,0)
10. What is the purpose of ReportApiVersions?
ReportApiVersions adds supported and deprecated API versions in response headers.
It helps clients know available API versions.
Intermediate Level Interview Questions
1. What is the difference between URL versioning and query string versioning?
URL versioning keeps the version in the route like /api/v1/products.
Query string versioning keeps the version in the URL query like /api/products?api-version=1.0.
URL versioning is more clear and commonly used.
2. How does [MapToApiVersion] work?[MapToApiVersion] is used to map an action method to a specific API version.
It helps when multiple methods exist inside the same controller for different versions.
[MapToApiVersion("2.0")]
publicIActionResultGetV2()
{
returnOk();
}
3. How do you support both versioned and non-versioned routes in the same API?
You can define multiple route attributes in the controller.
One route can include versioning and another can be normal.
[Route("api/v{version:apiVersion}/products")]
[Route("api/products")]
4. What is the purpose of v{version:apiVersion} in routing?
It is used to read the API version from the URL path.version is the route parameter and apiVersion validates it as an API version.
api/v1/products
5. How do you create separate controllers for different API versions?
Create different controller classes for each version.
Use [ApiVersion] attribute and place them in separate folders or namespaces.
[ApiVersion("1.0")]
publicclassProductsController:ControllerBase
{
}
6. What is the difference between Asp.Versioning.Mvc and Microsoft.AspNetCore.Mvc.Versioning?Microsoft.AspNetCore.Mvc.Versioning is the old package name.Asp.Versioning.Mvc is the newer package with updated support and features.
New projects should use Asp.Versioning.Mvc.
7. How do you configure Swagger for API versioning?
Create Swagger documents for each API version.
Use IApiVersionDescriptionProvider to generate separate Swagger endpoints.
options.SwaggerEndpoint("/swagger/v1/swagger.json","v1");
options.SwaggerEndpoint("/swagger/v2/swagger.json","v2");
8. What is the purpose of AddApiExplorer() in API versioning?AddApiExplorer() helps Swagger discover API version details.
It groups endpoints based on versions and generates separate documentation.
builder.Services.AddApiVersioning()
.AddApiExplorer();
9. What does SubstituteApiVersionInUrl = true do?
It replaces the {version} placeholder in routes with the actual API version.
Without this, Swagger may show {version} instead of v1 or v2.
options.SubstituteApiVersionInUrl=true;
10. How do you deprecate an API version in ASP.NET Core?
Use the Deprecated
= true property inside [ApiVersion].
It marks the version as old and Swagger shows it as deprecated.
[ApiVersion("1.0",Deprecated=true)]
Advanced Level Interview Questions
1. How do you implement header-based API versioning in ASP.NET Core?
Header versioning reads the API version from request headers.
You can configure it using HeaderApiVersionReader.
builder.Services.AddApiVersioning(options=>
{
options.ApiVersionReader=
newHeaderApiVersionReader("api-version");
});
2. How do you implement media type API versioning?
Media type versioning reads the version from the Accept header.
It is useful when clients use content negotiation.
builder.Services.AddApiVersioning(options=>
{
options.ApiVersionReader=
newMediaTypeApiVersionReader("version");
});
3. How does API versioning work internally with route constraints?
Route constraints check whether the requested version matches the controller version.apiVersion constraint helps ASP.NET Core select the correct endpoint.
If no match is found, it returns an error response.
4. How do you handle breaking changes between API versions?
Create a new API version instead of modifying the old one.
Keep old endpoints working until clients migrate to the new version.
This avoids breaking existing applications.
5. How do you maintain backward compatibility in large enterprise APIs?
Keep older API versions active for some time.
Avoid removing fields or changing response formats suddenly.
Use proper documentation and deprecation notices.
6. What are the advantages and disadvantages of single-controller versioning vs separate-controller versioning?
Single-controller versioning reduces duplicate code but becomes hard to manage for many versions.
Separate-controller versioning is cleaner and easier to maintain but may increase repeated code.
Large projects usually prefer separate controllers.
7. How do you customize API version selection behavior in ASP.NET Core?
You can create a custom API version selector.
It decides which version should be selected when the client does not provide one.
options.ApiVersionSelector=
newCurrentImplementationApiVersionSelector(options);
8. How would you migrate an existing unversioned API to a versioned API without breaking existing clients?
Keep the old routes working while adding new versioned routes.
Set a default API version for old clients.
Gradually move clients to versioned endpoints.
9. How do you version Minimal APIs in .NET?
Minimal APIs use ApiVersionSet and MapToApiVersion.
You can assign versions directly while mapping endpoints.
app.MapGet("/products", ()=>"v1")
.MapToApiVersion(1.0);
10. What challenges can occur when integrating Swagger with multiple API versions and how do you resolve them?
Common issues are duplicate endpoints and wrong Swagger JSON paths.
Use AddApiExplorer() and create separate Swagger documents for each version.
Enable SubstituteApiVersionInUrl = true to fix version placeholders.
1. How do you design API versioning strategy for microservices?
Each microservice should manage its own API versions independently.
Use clear version naming like v1, v2 and avoid frequent breaking changes.
API Gateway can help route requests to correct versions.
2. How do you handle API versioning in API Gateway?
The API Gateway routes client requests to the correct backend version.
It can expose URLs like /v1/orders and /v2/orders.
This keeps version management centralized.
3. How do you manage versioning in distributed systems?
Services should support backward compatibility during deployments.
Use contract-based communication and avoid sudden response changes.
Version APIs carefully to prevent service failures.
4. How do you test multiple API versions effectively?
Create separate test cases for each API version.
Use automated integration and regression testing.
Swagger and Postman collections also help in testing.
5. How do you monitor usage of deprecated API versions?
Use logging and API monitoring tools to track requests.
Check which clients still use old versions.
Notify users before removing deprecated APIs.
6. How do you secure different API versions with authentication and authorization?
Apply authentication and authorization policies per version if needed.
Older versions may use different security methods.
JWT and OAuth are commonly used for securing APIs.
7. How do you handle database schema changes across API versions?
Avoid breaking database changes immediately.
Use migrations and support both old and new fields during transition.
Mapping layers help manage schema differences.
8. How do you implement API versioning with OData in ASP.NET Core?
OData supports versioning using route configuration.
Each version can expose different OData models and endpoints.
Versioned routes are configured separately.
routeBuilder.MapVersionedODataRoute(
"v1",
"api/v{version:apiVersion}",
model);
9. How do you manage API documentation for multiple versions in large systems?
Create separate Swagger documents for every version.
Clearly mark deprecated APIs in documentation.
Keep release notes updated for clients.
10. How do you version APIs in cloud-native applications using Kubernetes and Azure API Management?
Use API Management to expose multiple API versions.
Kubernetes deployments can run different API versions together.
Traffic routing helps during gradual upgrades.
11. How do you apply semantic versioning in APIs?
Semantic versioning uses Major.Minor.Patch format.
Major version is for breaking changes, minor for new features, and patch for fixes.
Example: v2.1.0.
12. How do you reduce duplicate business logic across API versions?
Keep shared business logic inside services or repositories.
Controllers should only handle version-specific changes.
This reduces repeated code.
13. How do you implement sunset policies for old API versions?
Mark APIs as deprecated before removal.
Send warning headers or documentation notices to clients.
Give enough migration time before disabling old versions.
14. How do you handle SDK/client compatibility when API versions change?
Maintain backward compatibility whenever possible.
Release updated SDK versions for new APIs.
Document all breaking changes clearly.
15. How do caching and CDN behave with different API versions?
Each API version should have separate cache entries.
Version information must be part of the cache key or URL.
This avoids incorrect cached responses.
16. How do you implement version-neutral controllers in ASP.NET Core?
Use [ApiVersionNeutral] for controllers that work for all versions.
These controllers are accessible without version restrictions.
[ApiVersionNeutral]
publicclassHealthController:ControllerBase
{
}
17. How do you support rolling upgrades between API versions?
Run old and new API versions together for some time.
Gradually move traffic to the new version.
This reduces downtime and deployment risk.
18. How do you maintain API contracts in CI/CD pipelines?
Use automated contract and integration tests in the pipeline.
Validate request and response formats before deployment.
Swagger schema validation is also useful.
19. How do you implement API versioning in GraphQL or gRPC services?
GraphQL usually avoids versioning by evolving schemas carefully.
gRPC versioning is commonly done using package names or namespaces.
Breaking changes should be minimized.
20. What are the best practices for enterprise API versioning?
Use consistent versioning strategy across all services.
Avoid unnecessary breaking changes and document everything clearly.
Monitor deprecated APIs and maintain backward compatibility.
Asp.Versioning.Mvc
Used to add API versioning support in ASP.NET Core APIs.
Asp.Versioning.Mvc.ApiExplorer
Helps Swagger separate and display APIs by version.
Swashbuckle.AspNetCore
Used to generate Swagger UI and API documentation.
/api/v1/users
API version is passed in the URL path.
/api/v2/users
Calls version 2 of the same API endpoint.
/api/users?api-version=1.0
API version is passed in the query string.
api-version: 1.0
API version is sent through request headers.
Accept: application/json;v=1.0
API version is sent in the Accept header using media type versioning.
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
// If version not specified -> use V1
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
});
DefaultApiVersion = new ApiVersion(1, 0)
Sets API version 1.0 as the default version.
AssumeDefaultVersionWhenUnspecified = true
Uses the default version if the client does not send a version.
ReportApiVersions = true
Adds supported and deprecated API versions in response headers.
[ApiVersion("1.0")]
Defines that the controller or action belongs to API version 1.0.
[Route("api/[controller]")]
Creates a route without version in the URL.
[Route("api/v{version:apiVersion}/[controller]")]
Creates a route with API version in the URL.
[ApiVersion("1.0", Deprecated = true)]
Marks API version 1.0 as deprecated or old.
[MapToApiVersion("1.0")]
Maps a specific action method to API version 1.0.
[HttpGet(Name = "GetWeatherForecastDetails")]
Gives a name to the endpoint for URL generation.
[HttpGet("GetWeatherForecastDetails")]
Defines the URL path for the endpoint.
Asp.Versioning.Mvc.ApiExplorer
Used by Swagger to discover and group APIs by version.
.AddApiExplorer(options =>
{
// Format: v1, v2
options.GroupNameFormat = "'v'VVV";
// Replaces version in URL
options.SubstituteApiVersionInUrl = true;
});
GroupNameFormat = "'v'VVV"
Formats Swagger version names like v1 and v2.
SubstituteApiVersionInUrl = true
Automatically replaces the version placeholder in routes.
builder.Services.AddSwaggerGen(options =>
{
var provider = builder.Services.BuildServiceProvider()
.GetRequiredService<Asp.Versioning.ApiExplorer.IApiVersionDescriptionProvider>();
foreach (var description in provider.ApiVersionDescriptions)
{
options.SwaggerDoc(description.GroupName, new Microsoft.OpenApi.Models.OpenApiInfo
{
Title = $"API {description.ApiVersion}",
Version = description.GroupName
});
}
});
Creates separate Swagger documents for each API version.
options.ReportApiVersions = true;
Shows supported and deprecated versions in response headers.
options.ApiVersionReader = ApiVersionReader.Combine(
Allows the API to read version information from multiple places.
new QueryStringApiVersionReader("api-version")
Reads API version from the query string.
new HeaderApiVersionReader("x-api-version")
Reads API version from request headers.
new MediaTypeApiVersionReader("ct-api-version")
Reads API version from the media type or Accept header.