Using Configuration to Define Web API Routes

In most Web API projects, endpoints are usually defined one of two ways: by adding routes to WebApiConfig or by using attribute routing on the API controller actions. I’m not the biggest fan of attribute routing (a post for another time), so I tend to put all my routes in WebApiConfig. Plus, the APIs I build tend to need additional defaults that require on-the-fly changes in a production environment, such as logging API requests.

For instance, I might not want to log API requests for every endpoint by default, just some of them. However, there might be a scenario where I need to troubleshoot API calls, which starts with enabling logging for endpoints not already being logged. But I don’t want to have to rebuild and redeploy my API just to enable logging. I want to be able to “flip a switch” in a config file, capture some log data, then flip the switch back when I have what I need.

Typical WebApiConfig

Here is a sample set of API routes that deal with products. Straightforward enough, but notice the “logging” flag on each route defaults (assume I have a handler in place to check this flag on each request). Logging is enabled for the POST, PUT, and DELETE, but is disabled for the GET methods:

But let’s say that for whatever reason, I need to enable logging for those GET methods. To do so in this manner, I would have to modify the WebApiConfig code, rebuild the API project, then redeploy to production. Not only that, I would have to do that *again* as soon as I want to turn logging back off for those routes. Not ideal.

A Configuration Solution

So what if we took those routes defined in WebApiConfig and moved them into a config file? This is what that solution would look like.

1. The Routes.config File

In the root of your Web API project, create a new file named Routes.config. This file will contain all route definitions for your API, including any custom route defaults. Using the same product routes as above, here’s an example:

2. Create Routes Config Section Handler

To read and parse the Routes.config file, you need a custom configuration section handler, which requires the following 3 classes:

  • Route.cs – Holds route data for each individual route.
  • RoutesConfig.cs – Contains the list of routes.
  • RoutesConfigSectionHandler.cs – The implementation for the custom config section handler.

3. Register the Config Section Handler in Web.config

Now you need to register the custom config section handler in the Web.config using the configSource attribute, as shown below:

4. Modify Application_Start to Read Routes Config

With all that in place, modify the Application_Start method in global.asax to do 2 things:

  • Read the routes config section.
  • Add the RoutesConfig object to the GlobalConfiguration properties. We do this so that we can grab the routes in the WebApiConfig.Register method.

5. Modify WebApiConfig

And finally, the last thing to do is to modify the WebApiConfig to pull the RoutesConfig object from the GlobalConfiguration properties and iterate over the routes to add them to the Web API route table. So now your WebApiConfig will look something like this:

Summary

To recap:

  • Create a Routes.config file that contains all route definitions for the API, including any custom route defaults.
  • Implement a custom config section handler to parse the Routes.config file.
  • Add the custom config section handler to the Web.config file, using the configSource attribute..
  • Update Application_Start to read the routes section and store the routes config in the properties of GlobalConfiguration.
  • Modify WebApiConfig to grab the routes from the GlobalConfiguration properties and add the routes to the route table.

So there you have it. In the scenario I described above, this approach allows me to enable/disable logging for each API endpoint on-the-fly without having to do the rebuild/redeploy dance. It’s a beautiful thing.

Featured Image: Some rights reserved by ** Lucky Cavey **

6 comments

  1. steve says:
  2. I would not recommend any of the approaches in this article.

    I would only recommend attribute routing. Your API is strongly coupled to your routes, there is absolutely no reason to place this information anywhere except directly with the controller. In fact that’s all a controller is supposed to do, be a thin route-driver. Where does routing belong? With the router-driver.

    if you need to configure your routes via configuration I think you need to step back and ask yourself why in the world you’re doing this. Then stop.

    • In my opinion, attribute routing becomes untenable as the number of routes in your API increases. I prefer seeing all of my routes defined in one spot because it allows my eyes to quickly recognize code patterns that I might not otherwise see in attribute routing.

      But apparently you’re missing the point of the article. I have things I want to be able to flip a switch for in production without the need to rebuild and redeploy my API. A configuration-based approach solves this requirement, attribute routing does not.

      If that’s something you don’t have to worry about, by all means, continue on with your love of attribute routing.

  3. I thought this was interesting and decided to test this out. Although it seemed to work to set the routes, it didn’t properly set the HTTP Verb. Even though in my Routes.config I set the method=”GET”, when I tried that route, I got the following error:

    The requested resource does not support http method ‘GET’.

    I had to manually add the AcceptVerbs(“GET”) attribute to the Controller method for it to work.

Leave a Reply

Your email address will not be published. Required fields are marked *