In a previous post I discussed the approach we opted for at Linn when deciding how to evolve our HTTP APIs without breaking existing clients.
In this post I describe how we implemented that approach using NancyFx (a web framework for .NET inspired by Sinatra).
Jumping In
Most of the work is handled by implementing Nancy’s IResponseProcessor interface.
Firstly we define the media types that this processor will respond to.
application/json to support regular JSON requests
application/vnd.linn.user+json to support requests which do not specify a version
application/vnd.linn.user+json; version=1 to support requests for version 1 of the media type
We then implement the CanProcess method by evaluating the requested media type against those we support, and by looking at the type of the response model.
When I first implemented this I ran into an issue where Nancy was stripping out the parameters from requestedMediaRange before it was handed to the response processor. This was fixed with a PR which also introduced the MediaRange.MatchesWithParameters method used above. The changes are included in Nancy 0.21+.
The Process method is implemented by passing the model to a factory class which returns a DTO (we suffix such classes with ‘Resource’) created specifically for our vendor specific media type.
Putting it Together
Here is the IResponseProcessor in its entirety:
Nancy automatically detects implementations of IResponseProcessor at runtime as part of its content negotiation support. To take advantage of this, we must return a response as follows:
And with that, requests such as:
Receive a response like this:
Introducing a New Version
When a new version of the media type is introduced (and we must continue to support the old one), we implement a new DTO class to represent the new media type, and implement an IResponseProcessor as before:
Finally, we remove the parameterless vendor specific media type from the original processor, since we want requests which do not specify version to receive the most recent version if the media type. We also remove application/json as we want our new IResponseProcessor to handle that instead.
With those changes in place, the following requests:
All receive a response like this:
But a request against the original media type version will receive the same body as before.
Once we no longer need to support the original version, we can simply delete the corresponding processor and DTO.
And that’s all there is to it. If anyone has any suggestions on how this could be improved in any way, please feel free to comment below.