1

I am working on APIs in dotnet core 2.2 and I'd like to version my API. I'm looking for some solutions except:

  1. Routing method (api/v1/controller, api/v2/contoller)
  2. Routing method using APIVersioning package, (api/v{version: apiVersion}/contoller})

I want to know if there is any other solutions in which I don't have to change the route attribute? I might be completely wrong but can I use middleware? Like, map delegate to map the the incoming requests (based on v1, v2 it carries) to its right controller?

I'll highly appreciate any feedback and suggestions.

2 Answers2

2

You can use the APIVersioning package and configure it so it selects the version based on the HTTP Header.

services.AddApiVersioning(c =>
{
   c.ApiVersionReader = new HeaderApiVersionReader("api-version");
}

And then you can use the [ApiVersion] attribute on your controllers.

fiji3300
  • 103
  • 7
  • Hi, thanks for the answer. Sorry I forgot to specifiy that I won't be sharing version information in headers. I want those in the URL itself like https://__/api/v1/endpointname. – Aakash Rana Jun 24 '21 at 09:26
  • I thought you specifically didn't want to put the version in the URL. Could you please elaborate, then? Because I don't really understand what your goal is. If you do want the version in the url, then what's wrong with using the APIVersioning for that? – fiji3300 Jun 24 '21 at 11:09
  • Well I want to put the version in the URL but I didn't want to change the route attribute. I don't think it's possible without that so I'm gonna implement it using the APIVersioning package. Actually my team mate, who used dotnet core years ago was not happy with this approach, changing the route attribute in each controller, though he wanted to use the URL based versioning only. And I'm new dotnet. So, I was exploring and couldn't find a solution that he wanted. Anyway, thanks man! Appreciate your help. – Aakash Rana Jun 24 '21 at 16:17
  • Then you don't have to change the Route attribute. You can just add the [ApiVersion] attribute, and it will handle the routing for you. – fiji3300 Jun 24 '21 at 16:26
  • If I dont change the Route attribute, then how can I use the URL based versioning? I want my endpoints to be like api/v2/_ or api/v3/_. – Aakash Rana Jun 25 '21 at 04:50
  • The package takes care of that – fiji3300 Jun 25 '21 at 06:35
0

Can you use custom middleware - yes; however, be advised that endpoint selection is typically much more involved. The routing system provides extension and customization points, which is exactly what API Versioning does for you. Creating your own versioning solution will be a lot more involved than having to add a route template parameter.

If you're going to version by URL segment, then API Versioning requires that you use the ApiVersionRouteConstraint. The default name is registered as apiVersion, but you can change it via ApiVersioningOptions.RouteConstraintName. The route parameter name itself is user-defined. You can use whatever name you want, but version is common and clear in meaning.

Why is a route constraint required at all? API Versioning needs to resolve an API version from the request, but it has no knowledge or understanding of your route templates. For example, how would ASP.NET know that the route parameter id in values/{id:int} has be an integer without the int constraint? Short answer - it doesn't. The API version works the same way. You can compose the route template however you want and API versioning knows how and where to extract the value using the route constraint. What API versioning absolutely does not do is magic string parsing. This is a very intentional design decision. There is no attempt made by API Versioning to try and auto-magically extract or parse the API version value from the request URL. It's also important to note that the v prefix is very common for URL segment versioning, but it's not part of the API version. The approach of using a route constraint negates the need for API Versioning to worry about a v prefix. You can include it in your route template as a literal, if you want to.

If the issue or concern is having to repeatedly include the API version constraint in your route templates, it really isn't any different than including the api/ prefix in every template (which I presume you are doing). It is fairly easy to remain DRY by using one of the following, which could include the prefix api/v{version:apiVersion} for all API routes:

  1. Extend the RouteAttribute and prepend all templates with the prefix; this is the simplest
  2. Roll your own attribute and implement IRouteTemplateProvider

Ultimately, this requirement is yet another consequence of versioning by URL segment, which is why I don't recommend it. URL segment versioning is the least RESTful of all versioning methods (if you care about that) because it violates the Uniform Interface constraint. All other out-of-the-box supported versioning methods do not have this issue.

For clarity, the out-of-the-box supported methods are:

  • By query string (default)
  • By header
  • By media type (most RESTful)
  • By URL segment
  • Composition of n methods (ex: query string + header)

You can also create your own method by implementing the IApiVersionReader.

Attributes are just one way that API versions can be applied. In other words, you don't have to use the [ApiVersion] attribute if you don't want to. You can also use the conventions fluent API or create your own IControllerConvention. The VersionByNamespaceConvention is an example of such a convention that derives the API version from the containing .NET namespace. The methods by which you can create and map your own conventions are nearly limitless.

Chris Martinez
  • 3,185
  • 12
  • 28