How to document an ASP.NET WebApi that uses content type versioning
I guess we all agree that we should let our APIs evolve naturally, but sometimes versioning is in place. If we skip URL versioning - as it really is the same resource, just another representation, what we are left with are different media types. Github does this for their API, using the following media type.
application/vnd.github.v3+json
Another option might be to use the parameter part of the media type, which would look like this
application/vnd.linn.user+json; version=1
Media-type versioning is easily implemented in ASP.NET using a IHttpRouteConstraint implementation and a custom class inheriting from RouteFactoryAttribute.
This is all good and works fine until you wish to document your API. Which is sort of a really good idea if anyone other than you are going to use it. Documenting your web API in ASP.NET is easy, you can either add the Microsoft ASP.NET Web API 2.2 Help Page
nuget package or use a project such as Swashbuckle which generates Swagger documentation. However, this fails when it comes to media type versioning. The problem lies within the implementation of IApiExplorer which uses the routes route template as a unique filter and remove duplicates, completely ignoring any route constraints which may exist.
Unfortunately the ApiExplorer implementation is a black box and relies on sealed and internal classes in its process of creating the ApiDescriptions. After a little bit of tinkering I ended up with an implementation of IApiExplorer which works as a decorator around the existing one. After calling ApiDescriptions
on the existing ApiExplorer it will iterate all the ApiDescriptions, find anyone with route constraints, re-scan the routing table for similar route templates, use reflection to call PopulateActionDescriptions
on the existing ApiExplorer and add those to the output.
To use this implementation of the ApiExplorer you need to wire it up in the Services registration. I've done this with the following piece of code
var apiExplorer = config.Services.GetApiExplorer(); config.Services.Replace(typeof(IApiExplorer), new VersionedApiExplorer<VersionConstraint>(apiExplorer, config));
Voila, you now have a complete set of ApiDescriptions.
The next thing you need to do is to verify that your use of those versioned ApiDescriptions actually does take route constraints into account. For the WebApi Help Page you should start with modifying the GetFriendlyId method in the ApiDescriptionExtension class to include the version number.