As you may have learnt from some of the older posts, I am a big fan, and a big proponent of attribute routing in ASP.NET Web API.

One of the things that is missing out of the box in Web API’s implementation of attribute routing, is the ability to define global prefixes (i.e. a global “api” that would be prepended to every route) – that would allow you to avoid repeating the same part of the URL over and over again on different resources. However, using the available extensibility points, you can provide that functionality yourself.

Let’s have a look.


ASP.NET Web API allows you to provide a common route prefix for all the routes within a controller via RoutePrefixAttribute. This is obviously very convenient, but unfortunately that attribute, out of the box, cannot be used globally.

Here’s a typical use case per controller:

Applying a route prefix globally

In order to apply route prefix globally, you will need to customize the way ASP.NET Web API creates routes from the route attributes. The way to do that, is to implement a custom IDirectRouteProvider, which can be used to feed custom routes into the Web API pipeline at application startup.

That would typically mean a lot of work though, since you would need to manually deal with everything from A to Z – like manually processing controller and action descriptors. A much easier approach is to subclass the existing default implementation, DirectRouteProvider, and override just the method that deals with route prefixes (GetRoutePrefix). Normally, that method will only recognize route prefixes from the controller level, but we want it to also allow global prefixes.

DirectRouteProvider exposes all of its members as virtual – allowing you to tap into the route creation process at different stages i.e. when reading route prefixes, when reading controller route attributes or when reading action route attributes. This is shown in the next snippet:

Finally, the usage of this provider is as follows – instead of invoking the “regular” version of MapHttpAttributeRoutes method, you need to use the overload that takes in an instance of IDirectRouteProvider:

config.MapHttpAttributeRoutes(new CentralizedPrefixProvider("api"));

In this example, all attribute routes would get “api” prepended to the route template.

In more interesting scenarios, you can also use parameters with CentralizedPrefixProvider – after all, we are just building up a regular route template here. For example, the following set up, defining a global version parameter as part of every route, is perfectly valid too: