Skip to content

Versioning via the URL Path

Chris Martinez edited this page May 12, 2017 · 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.

Note: It is not possible to have a default API version for a URL path segment. For more information and possible solutions to address this scenario, refer to the known limitations.

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 );
configuration.AddApiVersioning();
...

[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

Since the OData implementation uses convention-based routes under the hood, the ApiVersionRouteConstraint is automatically added to all versioned OData routes when needed. The name of the constraint used in prefixes of OData routes must be apiVersion and cannot be changed.

// added to the odata web api configuration in the application setup
var modelBuilder = new VersionedODataModelBuilder( configuration )
{
    ModelConfigurations =
    {
        new PersonModelConfiguration()
    }
};
var models = modelBuilder.GetEdmModels();

configuration.AddApiVersioning();
configuration.MapVersionedODataRoutes( "odata-bypath", "api/v{apiVersion}", models );
...

[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

services.AddApiVersioning();
...

[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