Customizing controller discovery in ASP.NET Web API - StrathWeb

Strath

August 19th, 2013

Customizing controller discovery in ASP.NET Web API

One of the useful configuration features of ASP.NET Web API is that it allows you to be explicit about the assemblies into which it will look in order to discover controller types.

This is especially useful if you have assemblies residing outside of the bin folder, or if you are doing self hosting, and the controllers assemblies are not automatically loaded into the current AppDomain.

There are several hooks in the pipeline that you can plug into to achieve this goal. Let’s explore them, discussing the pros and cons of using any of these.

Custom IAssembliesResolver

The broadest reaching option, and first hook in the pipeline is IAssembliesResolver, with its default implementation, DefaultAssembliesResolver. Actually, we already discussed this option on the blog in the past, when Web API was still in RC version

The interface let’s you specify a list of assemblies which Web API should use to discover controller types. The interface definition is very straight forward, and the default implementation it comes with, simply looks into the AppDomain.CurrentDomain.

Since all assemblies copied into the bin folder (when hosting on IIS) are loaded into the AppDomain, normally it’s enough to just copy your external assembly there.

However, in case you want to load DLL(s) from a different path (perhaps a shared network drive or a different pre-approved location) or you are self-hosting, and you don’t have the help of magical bin folder, you can easily add your assembly to the collection:

Then, obviously, you need to register the resolver against your configuration:

In this particular case we didn’t even implement the interface directly, but rather extended its default implementation. Since we add our source to the assembly source provided by default, this resolver looks both in to the AppDomain.Current and into our DLL path.

Once this solution is in place, Web API, whenever it will try to obtain list of assemblies to resolve controllers (at any point in the pipeline), will resort to this custom implementation.

Custom IHttpControllerTypeResolver

A bit deeper in the pipeline lies IHttpControllerTypeResolver which is responsible of taking in assemblies resolved by IAssembliesResolver and discover the types matching the predefined controller definition.

Due to such design, you can actually bypass implementing IAssembliesResolver altogether, and bunch up both assembly discovery and type discovery in a single place. This is useful if you wish to modify the rules which Web API uses to discover controller types.

The default rule set for a type to be discovered as valid API controller is as follows:

  • – implements IHttpController (or inherits from ApiController)
  • – is a public class
  • – is a non-abstract class
  • – has a “Controller” suffix

These rules are represented by the following method, found on DefaultHttpControllerTypeResolver.

You could implement your own rules on top of that. An example implementation could extend the default DefaultHttpControllerTypeResolver as follows:

Notice that DefaultHttpControllerTypeResolver takes in a predicate defining rules to be used to discover controllers. In our example, we drop the HasValidControllerName(t) check – and require all controllers to derive from an imaginary MyBaseApiController. This is a neat way to force all the developers in our team to inherit from it when they develop their HTTP endpoints.

Another interesting thing worth mentioning here, is that later on, in an internal class responsible for caching controllers, Web API will perform another crucial check – whether the assembly from which controllers are being is discovered is dynamic or not – and if it is, it will not be processed.

This is very important if you wish to emit assemblies dynamically that would contain controller types. If you do that, this is not an extensibility point for you and you have to dig a step deeper.

Custom IHttpControllerSelector

Finally, even deeper into the pipeline, you can find & implement your own IHttpControllerSelector.

There are many reasons of doing that, mainly if, as the name suggests, you want to introduce a custom mechanism of action selection. This is quite useful if you wish to override the default action selection mechanism or introduce some versioning mechanism into your API, and dispatch correct version of the action/controller based on the incoming request.

However, there are some specific cases where using IHttpControllerSelector for controller type discovery makes sense. IHttpControllerSelector runs after the cache of controllers has been established (in IHttpControllerTypeResolver). Therefore, implementing your own type discovery logic at this level, allows you to bypass any caching mechanism present at the earlier stages.

While this has little production-environment value (as it’s very inefficient), it can be sometimes useful during development.

It obviously needs to be registered against the HttpConfiguration too:

In this implementation, we load the assembly manually, and then resolve the controller types by looking through all ExportedTypes. Doing it at this level, will force the assembly to be reloaded and rescanned at every request, which now allows us to freely recompile that assembly and see the changes reflected upon next request, as there is no controller caching in place anymore.

As briefly mentioned before, if you use dynamic assemblies (generated i.e. using CodeDom or Roslyn) this is the route you have to take – because otherwise Web API will ignore your dynamic assemblies.

For more information on the topic of dynamic compilation I encourage you read one of the older posts about Roslyn and Web API.

Be Sociable, Share!

  • Sergueev Eugene

    one more solution, use web.config probing section
    http://msdn.microsoft.com/en-us/library/823z9h8w.aspx

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1424

  • Pingback: Dew Drop – August 20, 2013 (#1,607) | Alvin Ashcraft's Morning Dew

  • Jarrod Albert

    I know this is an old post, but I have what I hope is a quick question. Have you had any issues with the custom IHttpControllerSelector option once WebApi 2 was released? We have code that works in VS2012 WebApi v1, but once we upgrade to VS2013 and WebApi v2 it starts failing with Multiple actions were found that match the request:

    Thank you for all the information you post on WebApi.

  • Massimo Zerbini

    Thanks for your article, it is very useful.

    There is a typing error in the class MyAssembliesResolver:
    the code should be assemblies.Add(controllersAssembly) instead of baseAssemblies.Add(controllersAssembly);