Aspnet-api-versioning: Support versioning in route and query parameter

Created on 3 Jul 2020  路  2Comments  路  Source: microsoft/aspnet-api-versioning

Currently in asp net core the following routing schema is supported: https://service.company.xyz/v1/values
However, it would be nice to be able to further specify minor versions with a query parameter or header.

As an example:
The URL to the v1 endpoint of a service would be https://service.company.xyz/v1/values
Now I want to target v1.1 e.g. https://service.company.xyz/v1/endpoint?apiVersion=1.1
Notice /v1/ is still part of the route.

[ApiController]
[ApiVersion("1.0")]
[Produces("application/json")]
[Route("v{version:apiVersion}/[controller]")]
public class ValuesController : ControllerBase
{
    [HttpGet]
    [ProducesResponseType(200)]
    public IActionResult Get() => Ok("values");

    [HttpGet]
    [ApiVersion("1.1")]
    [ProducesResponseType(200)]
    public IActionResult GetV1_1() => Ok("values from v1.1");
}

Is this currently possible? Otherwise I would suggest this as a feature request.

answered question

Most helpful comment

This is not possible - by design.

The API version denotes a contract between the client and server. Technically, API Versioning will allow you use both methods, but the values must be the same; otherwise, things are ambiguous (did you mean 1.0 or 1.1?).

Some people try to think about an API version as a _method overload_, _polymorphism_, or some other programming language concept. The API is HTTP (with its own _methods_) and that's not how it works. An API version _should_ be thought as a media type (e.g. representation over the wire) as opposed to _method calls_, which also why that's the only method that Fielding himself supports.

Despite what a human being might _infer_ by logic from an API version, there is no expressed or implied relationship between API version values. We've been taught that things such as compiled code libraries have a _version_ in a numeric format where values below the _major_ version imply some level of compatibility. This is not possible to express in HTTP or REST. Although you may have used the values 1.0 and 1.1, they might as well be application/json and application/xml. Some service authors presume they can make changes, especially additive changes, and be backward compatible. This too is a fallacy. Unless you own both sides, a server can never assume a client can automatically handle changes to a contract. While clients should employ _Tolerant Readers_, a server cannot guarantee that they do. You may notice that some service teams, such as Azure, now use dates (sometimes referred to as _version group_) instead of numbers. This helps humans understand when an API became effective, how old it is, and prevents assumptions about whether things are backward compatible (ex: is 2020-07-01 compatible with 2020-03-01; maybe, but you'll never assume by the value).

[steps off of soapbox]

If you want 1.1, then why not just have it in your routes? Why the need for two parameters? Supporting 1.1 is as easy as requesting v1.1/values versus v1/values. If it's stable URLs you're worried about, then this is just highlighting another issue with versioning by URL. The URL method has many complications and is the least RESTful of all methods, despite it's popularity. I've written about that many times here and on SO. One of these days, I'll have blog or book up about all of this. Suffice it to say, REST says the URL path is the identifier. The paths v1/values/1 and v1.1/values/1 are invariably the same resource, but with a different _representation_; however, according to REST, these are 2 completely different resources. You can solve the problem and have stable URLs by using any other versioning method. The query string method is the default, but the media type method is the most RESTful. It seems you _might_ be still early in your development. If stable URLs are a concern, consider using the query string method. You'll always have stable URLs a la /values. The client is the one that must append the api-version=1.0 or api-version=1.1 to the query string. After all, how is the server supposed to know which one the client wants?

I hope that helps clear things up and leads you down a path to implement your services.

All 2 comments

This is not possible - by design.

The API version denotes a contract between the client and server. Technically, API Versioning will allow you use both methods, but the values must be the same; otherwise, things are ambiguous (did you mean 1.0 or 1.1?).

Some people try to think about an API version as a _method overload_, _polymorphism_, or some other programming language concept. The API is HTTP (with its own _methods_) and that's not how it works. An API version _should_ be thought as a media type (e.g. representation over the wire) as opposed to _method calls_, which also why that's the only method that Fielding himself supports.

Despite what a human being might _infer_ by logic from an API version, there is no expressed or implied relationship between API version values. We've been taught that things such as compiled code libraries have a _version_ in a numeric format where values below the _major_ version imply some level of compatibility. This is not possible to express in HTTP or REST. Although you may have used the values 1.0 and 1.1, they might as well be application/json and application/xml. Some service authors presume they can make changes, especially additive changes, and be backward compatible. This too is a fallacy. Unless you own both sides, a server can never assume a client can automatically handle changes to a contract. While clients should employ _Tolerant Readers_, a server cannot guarantee that they do. You may notice that some service teams, such as Azure, now use dates (sometimes referred to as _version group_) instead of numbers. This helps humans understand when an API became effective, how old it is, and prevents assumptions about whether things are backward compatible (ex: is 2020-07-01 compatible with 2020-03-01; maybe, but you'll never assume by the value).

[steps off of soapbox]

If you want 1.1, then why not just have it in your routes? Why the need for two parameters? Supporting 1.1 is as easy as requesting v1.1/values versus v1/values. If it's stable URLs you're worried about, then this is just highlighting another issue with versioning by URL. The URL method has many complications and is the least RESTful of all methods, despite it's popularity. I've written about that many times here and on SO. One of these days, I'll have blog or book up about all of this. Suffice it to say, REST says the URL path is the identifier. The paths v1/values/1 and v1.1/values/1 are invariably the same resource, but with a different _representation_; however, according to REST, these are 2 completely different resources. You can solve the problem and have stable URLs by using any other versioning method. The query string method is the default, but the media type method is the most RESTful. It seems you _might_ be still early in your development. If stable URLs are a concern, consider using the query string method. You'll always have stable URLs a la /values. The client is the one that must append the api-version=1.0 or api-version=1.1 to the query string. After all, how is the server supposed to know which one the client wants?

I hope that helps clear things up and leads you down a path to implement your services.

Thanks @commonsensesoftware for your detailed explanation. This indeed cleared some things up for me.

Was this page helpful?
0 / 5 - 0 ratings