Implementing versioned Web API 2 controllers with attribute routing

I’m working on a Web API 2 project that needs to support multiple API versions. I found that you need a custom controller selector to handle different versions since the default routing ignores namespaces and only matches controller names.

My setup works fine for basic routing, but I’m having issues when combining it with attribute routing.

Here are the route attributes I’m using:

[Route("api/v1/products/{category}/{subcategory}")]
public IHttpActionResult GetProducts(string category, string subcategory)
{
    // Implementation here
}

[Route("api/v1/customer/findbyemail")]
public IHttpActionResult FindCustomer()
{
    // Accepts query: [email protected]
    return Ok();
}

The version number changes between controllers (v1, v2, etc). The problem seems to be in the custom controller selector’s GetRouteVariable method when trying to extract the namespace and controller information from attribute routes.

Has anyone successfully implemented API versioning with Web API 2 attribute routing? What approach did you use to make the custom controller selector work with route attributes?

Any suggestions would be really helpful. Thanks!

Had this exact headache last year! Ditched the custom controller selector and switched to header-based versioning. Just check the Accept or X-API-Version header in your action methods. Way cleaner than parsing route attributes and you don’t have to mess with the routing pipeline.

I encountered a similar issue when implementing versioning in our API using attribute routing. The default controller selector struggles with parsing version information due to how the routing is processed. What worked for me was adopting a hybrid strategy where the version is included directly in the route template alongside a custom route constraint. This constraint not only validates the version parameter but also leverages dependency injection to provide the necessary version context to your controllers. Additionally, consider using action filters for any version-specific logic, as this helps keep your routing more streamlined while effectively addressing versioning needs. If you seek a more automated solution, you may want to explore the Microsoft.AspNet.WebApi.Versioning package, though be prepared to adjust some of your existing routes.

We hit this exact routing nightmare during a major API refactor. The problem is attribute routes get compiled before your custom controller selector can intercept the version parameter. Don’t fight the selector - move your version logic into a custom route convention that runs during attribute route discovery instead. This convention parses the version from your route template and maps it to the right controller namespace before the default selector even kicks in. Register it in WebApiConfig during your MapHttpAttributeRoutes call. The convention extracts version info straight from the route template and handles controller resolution way more reliably than trying to override the selector afterward. You keep your existing attribute routing structure and get proper versioning.