Scott Hanselman

ASP.NET Core RESTful Web API versioning made easy

November 02, 2016 Comment on this post [29] Posted in ASP.NET Web API | Open Source
Sponsored By

Pic by WoCTechChat used under Creative Commons There's a LOT of interesting and intense arguments that have been made around how you should version your Web API. As soon as you say RESTful it turns into a religious argument where folks may just well quote from the original text. ;)

Regardless of how you personally version your Web APIs, and side-stepping any arguments one way or the other, there's great new repository by Chris Martinez that Jon Galloway turned me on to at https://github.com/Microsoft/aspnet-api-versioning. There's ASP.NET 4.x Web API, ODATA with ASP.NET Web APIs, and now ASP.NET Core 1.x. Fortunately Chris has assembled a nicely factored set of libraries called "ASP.NET API Versioning" that add service API versioning in a very convenient way.

As Chris points out:

The default API versioning configuration is compliant with the versioning semantics outlined by the Microsoft REST Guidelines. There are also a number of customization and extension points available to support transitioning services that may not have supported API versioning in the past or supported API versioning with semantics that are different from the Microsoft REST versioning guidelines.

It's also worth pointing out how great the documentation is given it's mostly a one-contributor project. I'm sure Chris would appreciate your help though, even if you're a first timer.

Chris has NuGet packages for three flavors of Web APIs on ASP.NET:

But you should really clone the repo and check out his excellent samples.

When versioning services there's a few schools of thought and with ASP.NET Core it's super easy to get started:

public void ConfigureServices( IServiceCollection services )
{
services.AddMvc();
services.AddApiVersioning();

// remaining other stuff omitted for brevity
}

Oh, but you already have an API that's not versioned yet?

services.AddApiVersioning(
o =>
{
o.AssumeDefaultVersionWhenUnspecified = true );
o.DefaultApiVersion = new ApiVersion( new DateTime( 2016, 7, 1 ) );
} );

Your versions can look however'd you like them to:

  • /api/foo?api-version=1.0
  • /api/foo?api-version=2.0-Alpha
  • /api/foo?api-version=2015-05-01.3.0
  • /api/v1/foo
  • /api/v2.0-Alpha/foo
  • /api/v2015-05-01.3.0/foo

QueryString Parameter Versioning

I'm not a fan of this one, but here's the general idea:

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

So this means to get 2.0 over 1.0 in another Controller with the same route, you'd go here:

/api/helloworld?api-version=2.0

Also, don't worry, you can use namespaces to have multiple HelloWorldControllers without having to have any numbers in the class names. ;)

URL Path Segment Versioning

This happens to be my first choice (yes I know Headers are "better," more on that later). You put the version in the route like this.

Here we're throwing in a little curveball. There's three versions but just two controllers.

[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!";
}

To be clear, you have total control, but the result from the outside is quite clean with /api/v[1|2|3]/helloworld. In fact, you can see with this example where this is more sophisticated than what you can do with routing out of the box. (I know some of you are thinking "meh I'll just make a route table." I think the semantics are much clearer and cleaner this way.

Header Versioning

Or, the hardest way (and the one that a lot of people this is best, but I disagree) is HTTP Headers. Set this up in ConfigureServices for ASP.NET Core:

public void ConfigureServices( IServiceCollection services )
{
services.AddMvc();
services.AddApiVersioning(o => o.ApiVersionReader = new HeaderApiVersionReader("api-version"));
}

When you do HeaderApiVersioning you won't be able to just do a GET in your browser, so I'll use Postman to add the header (or I could use Curl, or WGet, or PowerShell, or a Unit Test):

image

Deprecating

Speaking of semantics, here's a nice one. Let's say an API is going away in the next 6 months.

[ApiVersion( "2.0" )]
[ApiVersion( "1.0", Deprecated = true )]

This advertises that 1.0 is going away soon and that folks should consider 2.0. Where does it advertise this fact? The response headers!

api-supported-versions: 2.0, api-deprecated-versions: 1.0

All in all, this is very useful stuff and I'm happy to add it to my personal toolbox. Should this be built in? I don't know but I sure appreciate that it exists.

SIDE NOTE: There is/was the start of a VersionRoute over in the AspNet.Mvc repo. Maybe these folks need to join forces?

How do YOU version your Web APIs and Services?


Sponsor: Big thanks to Telerik! They recently launched their UI toolset for ASP.NET Core so feel free to check it out or learn more about ASP.NET Core development in their recent whitepaper.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service
November 02, 2016 11:54
Very cool! This is a rather neglected topic and on many projects, developers don't even think about versioning before they need to make a breaking change. Thank you.
November 02, 2016 11:54
Very cool project. You never really got around to explaining what you dislike so much about using headers for versioning though. What's the issue?
November 02, 2016 12:14
Welcome to #Microservices with #Microversioning, all the problems of Distributed software multiplied by Versioning challenges with none of the benefits of either ...

If you need to do microversioning for your rest services you're doing something wrong at a whole different level than writing code/applications.
November 02, 2016 12:14
Oh, this is great. Lovely. The Api versioning is very important.
November 02, 2016 13:53
What about versioning using the Accept HTTP header, so something like this:


Accept: application/vnd.mycompany.myapp-v2+xml
November 02, 2016 15:31
Why on earth would you do different API's in the same project. I won't recommend this at all. I'm saying this is a very bad practice.

November 02, 2016 16:49
@Edward You'll want multiple API versions to support legacy users at the same time as current users. Over time, your API will change, make no mistake. If you can do it without breaking anything, that's brilliant, but rather than just shut out anyone who hasn't updated their app/service/whatever, you can support both old and new while giving users time to migrate.
November 02, 2016 17:02
I'm sorry to be a grumpy old fart here, but this is not cool. Not only has Microsoft just released REST API Guidelines that are clearly not RESTful, but to say it bluntly: Adding a version number to your API is one of the worst REST anti-patterns you can implement in your API.

The man defining REST in chapter 5 of his doctorate dissertation in 2000, Roy Fielding, says that you should not version your web APIs. When did you last see a version number on a website? A REST API is just a web site for users with a limited vocabulary. It should still be a web of links between resources that should be followed, which coincidentally solves the versioning problem.

Hypermedia is the solution to versioning web APIs, not a version number. Can Microsoft please come on board and start communicating correctly about REST? To continue on the REST definition thread, Fielding says:

Hypermedia as the engine of application state is a REST constraint. Not an option. Not an ideal. Hypermedia is a constraint. As in, you either do it or you aren’t doing REST.


So either you're using hypermedia in your API and through that solving the whole version problem or you're not doing REST. So everything Microsoft does related to web APIs that aren't using hypermedia, or as in this case, explicitly recommending an anti-REST pattern by including a version number in the URI, you should really not label it "REST".

REST is not JSON+CRUD over HTTP. It is an architectural pattern. It's an abstract concept. And as with learning a new programming language makes and requires you to think differently than you would in another language, REST requires you to think differently than RPC. Don't assume that your knowledge of RPC can be applied directly to REST, because it can't.

Before arguing with me, I urge you to watch my presentation The REST And Then Some (only 20 minutes!), held at Nordic APIs a short two weeks ago. I hope it can help improve the understanding of hypermedia, how it's not as difficult as it appears and how both the current and future web (programmable or not) is (or at least should be) wholly dependent on it.
November 02, 2016 18:18
This works great to version within a project, but wouldn't it become unwieldy quick, especially if you version anything besides your controllers. Suppose HelloWorldController exposes HelloWorldModel and you need to change/add properties? Now you need HelloWorld2Controller AND HelloWorld2Model. But then you modify the controller, but don't need to change the model, so you have HelloWorld3Controller reference HelloWorld2Model. EEK.

What I've settled on is this: we have a good way to manage versions already. It's called source control. Do a branch or whatever for each version so you can code against it, and publish to the appropriate place. In simple cases, publish to /v1 /v2, /v3 folders etc. In more complicated cases, a reverse proxy like API Management to expose each version from wherever it sits.
November 02, 2016 20:39
Excellent article. Will definitely make use of this. Thanks :)
November 02, 2016 21:02
@Asbjørn I wish there were a plus 1 or thumbs up button on here...
November 02, 2016 21:33
@stephen
No you wil build and release another api on another web app.
And yes api's do offcourse change.
November 03, 2016 3:04
For those that don't have control over their clients, and don't require backwards compatability of their web apis this seems useful. Even if lock step versioned clients are available on all platforms for your web api. There will always be non-well behaved consumers of public web apis, I guess this helps to support them.
November 03, 2016 6:54
What matters is solving the problem for your employer or your client the best way possible, not if the web services are RESTful or if it is a SOAP service. This is why I tell everyone that they should never say that the web service is RESTful because you will get a bunch of guys debating about REST, RESTful, Hypermedia, HATEOAS and a Ph.D. thesis instead of focusing on what actually matters.
November 03, 2016 7:20
Finally we can version Web API.
So, it's a new concept added to my knowledge collection today. :)
November 03, 2016 9:18
That's amazing, have been looking for this solution for months.
Thanks
November 03, 2016 10:19
@lacosaes1: What if, God forbid, learning something new could actually make you improve as a developer and you end up creating better, more self-descriptive and discoverable APIs by it? Just because you're able to enumerate and scoff at a few FLA's does not necessarily mean you've fully understood them.

Advocating API versioning under the "REST" label is analogous to pushing ALGOL as a "Functional Programming Language". While API versioning and ALGOL have their place in the history of programming, they do not fit under those labels and to be quite honest; shouldn't be advocated for at all (anymore).
November 03, 2016 12:34
What about graphql? Are there plans for asp.net core support?
November 03, 2016 12:51
@Asbjørn Ulsberg

Hypermedia:

https://msdn.microsoft.com/en-us/magazine/jj883957.aspx

Hmmm this seems fine if your API is returning visuals.
What if your API is purely actions and raw data such as an object represented as JSON. Then in v2 of an action you might require an extra parameter to be specified - hypermedia (at least in the above explaination) doesn't explain how that would be solved.
November 03, 2016 13:30
Hi Scott,

Thanks for this article.
Yet, is there anyway to handle this versioning using branches in source control, instead of duplicating code in the same project ?

For example, I would like to handle several DLL with same name, namespace, but different versions (got from my TFS platform)...

Thanks
November 03, 2016 13:51
For most changes to an API, you are actually versioning an endpoint, not the whole API and it's much better to have your version number at the end of the url, than at the start. e.g.


[Route( "api/v1/{controller}/{action}/v{version:endpointVersion}")]


I would save the number at the start of the path for situations which involve a change to the APIs conventions, and in those cases you will probably want an entirely separate MVC/WebApi pipeline as you won't want to share global action filters, model binders, docs, SDK generator etc.
November 03, 2016 16:58
Excellent package! Configuration between one and another type of versioning is very easy and flexible. Definitely recommend! Thanks for introducing me into this Scott!
Tom
November 03, 2016 17:01
@Emmanuel Roullé
Better learn git for source control. Please. Even the whole .net team is not using TFS anymore.
November 04, 2016 19:23
A lot of great comments here already about whats the "correct" (if any exists?) way to version a web api. Worth to read through most of them. Thanks for the article Scott.
November 05, 2016 20:19
@Asbjørn Ulsberg:

"What if, God forbid, learning something new could actually make you improve as a developer and you end up creating better, more self-descriptive and discoverable APIs by it?"

Of course you can end up with a better API. Engineering is all about trade-offs.

"Just because you're able to enumerate and scoff at a few FLA's does not necessarily mean you've fully understood them."

You can fully understand them and also understand that they are not silver bullets that by definition are going to produce a better product or service. Again, is all about trade-offs for the particular use case.

"Advocating API versioning under the "REST" label is analogous to pushing ALGOL as a "Functional Programming Language". While API versioning and ALGOL have their place in the history of programming, they do not fit under those labels and to be quite honest; shouldn't be advocated for at all (anymore)."

You are right. I didn't argue against that. What I'm arguing is that it doesn't matter if a service is RESTful or not. That's why I said, and I'm quoting myself:

"What matters is solving the problem for your employer or your client the best way possible"

A lot of times building a web service that is really RESTful is not the best way possible from all the possible approaches you can use. This is engineering, not blind following.
November 09, 2016 20:07
Just plugged it in to my .NET core project and it works great. Added a route prefix convention based on this http://www.strathweb.com/2016/06/global-route-prefix-with-asp-net-core-mvc-revisited/ so I didn't have to the routes on all of my controllers to include "v{version:apiVersion}".
tom
November 13, 2016 15:31
Scott, this is not adrrdddd at you in particular, or with this article, although some of the language is unhelpful. However, about the company you are part of, some of the stuff Microsoft does and promotes in the API sphere continues to be, 10 years after WCF rest, actively missing the point and actively confusing the market and implementers, and misdirecting developers. I'm glad others have pointed the work some of us do to try and compensate, and provide alternatives, butit's disheartening that 10 years on, we're back to where we were with the spin machine.
November 21, 2016 12:44
Whowww!! amazing job Scott!! Great news!
November 25, 2016 21:12
Why should I expose something technical like the version of a service/api to the URL?

Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.