Skip to content

Versioning via the URL Path

Chris Martinez edited this page Jul 28, 2016 · 9 revisions

An alternate, but common, method of API versioning is to use a URL path segment. This approach does not allow implicitly matching the initial, default API version of a service; therefore, all API versions must be explicitly declared. In addition, the API version value specified for the URL segment must still conform to the version format.

ASP.NET Web API

// added to the web api configuration in the application setup
var constraintResolver = new DefaultInlineConstraintResolver()
{
    ConstraintMap =
    {
        ["apiVersion"] = typeof( ApiVersionRouteConstraint )
    }
};
configuration.MapHttpAttributeRoutes( constraintResolver );
...

[ApiVersion( "1.0" )]
[Route( "api/v{version:apiVersion}/helloworld" )]
public class HelloWorldController : ApiController
{
    public string Get() => "Hello world!";
}

[ApiVersion( "2.0" )]
[ApiVersion( "3.0" )]
[Route( "api/v{version:apiVersion}/helloworld" )]
public class HelloWorld2Controller : ApiController
{
    public string Get() => "Hello world v2!";

    [MapToApiVersion( "3.0" )]
    public string GetV3() => "Hello world v3!";
}

ASP.NET Web API and OData v4.0

// added to the odata web api configuration in the application setup
var modelBuilder = new VersionedODataModelBuilder( configuration )
{
    ModelConfigurations =
    {
        new PersonModelConfiguration()
    }
};
var models = modelBuilder.GetEdmModels();
var v1 = new ApiVersion( 1, 0 );
var v2 = new ApiVersion( 2, 0 );
var v3 = new ApiVersion( 3, 0 );
var model1 = models.Single( m => m.GetAnnotationValue<ApiVersionAnnotation>( m ).ApiVersion == v1 );
var model2 = models.Single( m => m.GetAnnotationValue<ApiVersionAnnotation>( m ).ApiVersion == v2 );
var model3 = models.Single( m => m.GetAnnotationValue<ApiVersionAnnotation>( m ).ApiVersion == v3 );

configuration.MapVersionedODataRoute( "odata-v1", "v1", model1, v1 );
configuration.MapVersionedODataRoute( "odata-v2", "v2", model2, v2 );
configuration.MapVersionedODataRoute( "odata-v3", "v3", model3, v3 );
...

[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() } );
}

ASP.NET Core

[ApiVersion( "1.0" )]
[Route( "api/v{version:apiVersion}/[controller]" )]
public class HelloWorldController : Controller
{
    public string Get() => "Hello world!";
}

[ApiVersion( "2.0" )]
[ApiVersion( "3.0" )]
[Route( "api/v{version:apiVersion}/helloworld" )]
public class HelloWorld2Controller : Controller
{
    [HttpGet]
    public string Get() => "Hello world v2!";

    [HttpGet, MapToApiVersion( "3.0" )]
    public string GetV3() => "Hello world v3!";
}

The effect of the API version attribution is that the following requests match different controller implementations:

Request URL Matched Controller Matched Action
/api/v1/helloworld HelloWorldController Get
/api/v2/helloworld HelloWorld2Controller Get
/api/v3/helloworld HelloWorld2Controller GetV3
/api/v1/People PeopleController Get
/api/v2/People People2Controller Get
/api/v3/People People2Controller GetV3
Clone this wiki locally