Before you get too far into the nuts and bolts of your ASP.NET Web API, or any other API, you should spend some time considering how you are going to version the application. Inevitably, you will introduce breaking changes that will need to be managed in some way. A good versioning strategy allows you to introduce these breaking changes without immediately breaking the application for you API consumers. They should have the opportunity to opt into the new version of the API and not be forced into upgrading immediately for fear of having the application break if they don’t. This allows them to decide whether it make sense to upgrade at all, gives them time to budget (resources and money) for the upgrade and to schedule that actual upgrade implementation.
There are several ways to version an API, each with their own advantages and disadvantages. What I’ve learned in doing this for a while now is that no matter which way you go, someone is going to have a porblem with it. Either it is too hard to consume, too academic, too easy to get wrong or a myriad of things. My advice is to consider all of the options and choose one based on your needs – the anticipated amount of change, the makeup of you consumers, the desired ease of testing or any other characteristics that are of particular importance for your API.
In this post, I will describe a handful of the versioning strategies that I’ve come across and give my take on their advantages and disadvatages. I’ll leave it up to you to decide which is best for your particular scenario.
HTTP GET: https://www.doesnotcompile.com/api/v2/something/foo
In this strategy, you simply include the API version in the URL for a resource. This way, when you want to access a different version of the API or resource, you simply change the version number in the URL. This is very easy to do manually and simple to implement programmatically. The caller is explicitly requesting a version of the API by including that information in the request URL.
There is an obvious problem with this strategy though – the URL should represent the entity and shouldn’t change because of a version. The URL is supposed to be the “address” of that resource and the “address” shouldn’t change because the version number was bumped up. I agree with this to a certain extent. The URL should identify the resource that I’m accessing, not a version of that resource. That being said, I don’t necessarily believe that this is a deal breaker. I usually opt for this versioning strategy because it is very explicit, easy to manage via branching and deployment strategy, doesn’t introduce complexity into the source code and is easy to test and send out links that others can just click on.
HTTP GET: https://www.doesnotcompile.com/api/something/foo?version=2
This strategy is very similar to the URL versioning strategy in that it requires that the caller include the version number in the URL of the resource. The difference is that the version number is included as a query-string parameter instead of in the URL path. This means that something in the back-end needs to interpret this version number and route the request to the appropriate version of the API to process it. This can be done either programmatically over using one of the API gateways I talk about below. This makes it just a little more difficult to implement programmatically, but it retains the same advantages that the URL versioning strategy has.
I don’t usually choose this strategy because I have the same philosophical objects to it that I do for URL versioning and it is a lot more involved to implement on the back-end. Usually, this means writing some code that uses the version number to route to the appropriate controller or action. If I’m going to include the version in the resources URL, I would rather use the URL versioning strategy above and manage the routing of the request to a particular version using my branching and deployment strategy.
Custom Request Header
HTTP GET: https://www.doesnotcompile.com/api/something/foo api-version: 2
This strategy requires that the caller include a custom “api-version” header with every request to tell the server what version of the API they are wanting to access. Unlike, the previous strategies, this implementation does not affect the resource’s “address” – you access a resource using the same URL in all versions of the API. Instead, you make the version part of the metadata that is sent with the request and rely on the back-end server to route the request appropriately. The problem with request headers is that they are not really a semantic way of describing the resource. Some would argue that the HTTP specification provides other mechanisms for describing how we want the resource represented and we should not be reproducing it.
One problem with this approach is that, like the version query-string parameter, it requires that the back-end somehow interpret the value and route the request to the appropriate controller or action. There are several established ways using the builtin extensibility of the ASP.NET Web API. The problem for me is that, in the end, they all involve the maintenance of different versions of the controller/action side-by-side in the code.
In addition to the code issues, links to an API with this type of versioning strategy are not as easily shared or tested. You have to carefully construct a request with the custom version header in order to access a specific version of the API. This means that, if I want to share a link to an API endpoint, I have to include some instructions on how to construct the request and possibly some screenshots of how to do using a tool like PostMan.
HTTP GET: https://www.doesnotcompile.com/api/something/foo Accept: application/vnd.doesnotcompile.v2+json
This strategy is very similar to the custom header strategy described before except that it uses the HTTP protocol’s built in Accept header to describe the version of the API that the caller is wanting to access. The idea is that the HTTP specification already provides this header to describe other aspects of the expected results and the version should is just another component of this.
Just like the custom header strategy, there are several ways to implement this on the back-end. This strategy shares the same disadvantages that I listed for the custom header strategy as well.
There are many API Management offerings out there that offer a wide range of features that are useful for managing APIs. Some of these management applications are really powerful and allow you to do a ton of really sophisticated operations on top of your APIs. All of these applications act as a gateway between the API consumers and the actual API(s) that service the incoming request. Some provide the ability to route to a version of an API based on different characteristics of the incoming request. This, along with the other features that they offer, make API management applications a very appealing option for versioning an API – especially if your looking at also doing things like throttling access, chunking results, automatically downshifting HTTPS and generating and managing API keys. Here are some API management providers that you can consider.
NGINX – Allows you to perform URL rewrite operations based on different aspects of the incoming request. For example, you could rewrite the request to a different version of the API based on the callers IP address.
Akana (formerly SOA Software) – You can route requests based on message content, headers, identity and other factors
As you’ve seen, there are a lot of options for versioning an ASP.NET Web API. Each one has their advantages and shortcomings. In the end, what matters most isn’t the implementation that you go with. Instead, it’s that you’ve implemented some versioning strategy in your API. Without one, you will inevitably cause your consumers issues when you introduce some sort of breaking change. It is better to spend a small amount of time up front implementing a versioning strategy than to have to deal with tricky upgrade roll-outs and angry customers later on.
For my part, I almost always chose the URL versioning strategy. It doesn’t muddle up the code with versioning logic and can be easily handled through branching and deployment strategies. Fore the most part, it’s implementation details are out of the developer’s way.
Post Footer automatically generated by Add Post Footer Plugin for wordpress.