-
Notifications
You must be signed in to change notification settings - Fork 707
Version Interleaving
API versions do not have to be split across different controller classes. A service author might choose to have a controller implement multiple API versions simultaneously. Controller actions can subsequently be mapped to specific API versions. This approach is useful for small version differences, but should be used sparingly to prevent developer confusion and complicate code maintenance. For example:
[ApiVersion( 1.0 )]
[RoutePrefix( "api/helloworld" )]
public class HelloWorldController : ApiController
{
[Route]
public string Get() => "Hello world v1.0!";
}
[ApiVersion( 2.0 )]
[ApiVersion( 3.0 )]
[RoutePrefix( "api/helloworld" )]
public class HelloWorld2Controller : ApiController
{
[Route]
public string Get() => "Hello world v2.0!";
[Route, MapToApiVersion( 3.0 )]
public string GetV3() => "Hello world v3.0!";
}
[ApiVersion( 1.0 )]
[ODataRoutePrefix( "People" )]
public class PeopleController : ODataController
{
[ODataRoute]
public IHttpActionResult Get( ODataQueryOptions<Person> options ) =>
Ok( new[]{ new Person() } );
}
[ApiVersion( 2.0 )]
[ApiVersion( 3.0 )]
[ControllerName( "People" )]
[ODataRoutePrefix( "People" )]
public class People2Controller : ODataController
{
[ODataRoute]
public IHttpActionResult Get( ODataQueryOptions<Person> options ) =>
Ok( new[]{ new Person() } );
[ODataRoute, MapToApiVersion( 3.0 )]
public IHttpActionResult GetV3( ODataQueryOptions<Person> options ) =>
Ok( new[]{ new Person() } );
}
[ApiVersion( 1.0 )]
[ApiController]
[Route( "api/[controller]" )]
public class HelloWorldController : ControllerBase
{
[HttpGet]
public string Get() => "Hello world v1.0!";
}
[ApiVersion( 2.0 )]
[ApiVersion( 3.0 )]
[ApiController]
[Route( "api/helloworld" )]
public class HelloWorld2Controller : ControllerBase
{
[HttpGet]
public string Get() => "Hello world v2.0!";
[HttpGet, MapToApiVersion( 3.0 )]
public string GetV3() => "Hello world v3.0!";
}
[ApiVersion( 1.0 )]
public class PeopleController : ODataController
{
public IActionResult Get( ODataQueryOptions<Person> options ) =>
Ok( new[]{ new Person() } );
}
[ApiVersion( 2.0 )]
[ApiVersion( 3.0 )]
[ControllerName( "People" )]
public class People2Controller : ODataController
{
public IActionResult Get( ODataQueryOptions<Person> options ) =>
Ok( new[]{ new Person() } );
[MapToApiVersion( 3.0 )]
public IActionResult GetV3( ODataQueryOptions<Person> options ) =>
Ok( new[]{ new Person() } );
}
var builder = WebApplication.CreateBuilder( args );
builder.Services.AddProblemDetails();
builder.Services.AddApiVersioning();
var app = builder.Build();
var hello = app.NewVersionedApi();
var v1 = hello.MapGroup( "/helloworld" ).HasApiVersion( 1.0 );
var v2_v3 = hello.MapGroup( "/helloworld" )
.HasApiVersion( 2.0 )
.HasApiVersion( 3.0 );
v1.MapGet( "/", () => "Hello world v1.0!" );
v2_v3.MapGet( "/", () => "Hello world v2.0!" ).MapToApiVersion( 2.0 );
v2_v3.MapGet( "/", () => "Hello world v3.0!" ).MapToApiVersion( 3.0 );
app.Run();
Although not illustrated in this example, it’s important to note that different versions of a service action might have different return values. The effect of the API versioning attribution is that the following requests match different controller and action implementations:
Request URL | Matched Controller | Matched Action |
---|---|---|
/api/helloworld?api-version=1.0 | HelloWorldController | Get |
/api/helloworld?api-version=2.0 | HelloWorld2Controller | Get |
/api/helloworld?api-version=3.0 | HelloWorld2Controller | GetV3 |
/api/People?api-version=1.0 | PeopleController | Get |
/api/People?api-version=2.0 | People2Controller | Get |
/api/People?api-version=3.0 | People2Controller | GetV3 |
It should be reiterated that the defined API version, even for an action, never directly influences routing. When the action matched for a route is ambiguous, the selection process will look for an explicit API version that matches the requested API version. If an explicit match is not found, then the action will be implicitly matched. If two actions are ambiguous by route and API version, then this is a developer mistake and the default behavior is unchanged.
- Home
- Quick Starts
- Version Format
- Version Discovery
- Version Policies
- How to Version Your Service
- API Versioning with OData
- Configuring Your Application
- Error Responses
- API Documentation
- Extensions and Customizations
- Known Limitations
- FAQ
- Examples