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 argumentwhere 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:
- ASP.NET Web API - Adds service API versioning to your Web API applications
- ASP.NET Web API and OData - Adds service API versioning to your Web API applications using OData v4.0
- ASP.NET Core - Adds service API versioning to your ASP.NET Core applications
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):
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!
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?
No comments:
Post a Comment