POCO controllers in ASP.NET vNext

One of the very cool features of the vNext of ASP.NET MVC (a unified framework set to succeed MVC, Web API and Web Pages) is the ability use POCO classes as controllers. No base class, no interface to implement, 100% convention.

Let’s look a little bit into that.

POCO controllers in ASP.NET vNext

By default, as long as your class is public, non-abstract, has a Controller suffix and is defined in an assembly that references any of the MVC assemblies (Microsoft.AspNet.Mvc.Core, Microsoft.AspNet.Mvc etc), it will be discovered as a valid controller. An example is shown below.

Of course you cannot do much with just a POCO – since you typically need access to things like current HTTP request, View Data or current Principal. However, this is not a problem at all – POCO controllers support convention-based property injection. This is achieved through DefaultControllerFactory, which looks for the following properties:

  • ActionContext and injects an instance of ActionContext there
  • ViewData, and injects an instance of ViewDataDictionary there
  • Url, and injects an instance of IUrlHelper there

You can see the source code here.

So we could modify our controller accordingly:

And boom, just like that you have all the contextual information you might need – for example, HttpRequest and Principal are both hanging off ActionContext.

In addition to that, the helpers, such as the aforementioned IUrlHelper or IActionResultHelper, can also be constructor injected, since ASP.NET vNext has dependency injection all the way through.

Ditching the controller suffix

Now, what if you wanted to go a step further and free yourself from the mandatory Controller suffix? Well, it’s actually not that difficult to do. Inspection whether a given Type qualifies as a controller happens inside DefaultActionDiscoveryConventions, the default IActionDiscoveryConventions implementation, inside of the IsController method. So you could either implement that interface yourself, or extend the DefaultActionDiscoveryConventions, which exposes all of its methods as virtual.

In the following example we extend the base logic with our new extra suffix – “Endpoint”.

We could now change our class to

And it will work just fine – the only problem is that it will be publicly available as myapi.com/dummyendpoint – as the logic of stripping away the Controller suffix when exposing HTTP endpoints is embedded into ControllerDescriptor and handles only – surpise, surpise – Controller suffix.

We could remedy that by creating a custom ControllerDescriptorFactory – a factory class responsible for taking TypeInfo (desribing a controller) and producing an instance of a ControllerDescriptor out of it. The Name on the ControllerDescriptor will determine how a given endpoint is publicly visible. Unfortunately at this point ControllerDescriptor has private setters, so cannot be sublassed, and the default ControllerDescriptorFactory has no virtual members, so cannot be customized – but we can work around that with a bit of reflection.

So effectively we say, if our controller ends with “Endpoint”, we are going to strip that away.

All that’s left now is to register the new services against ServiceCollection used by the IBuilder. YOu have to do that after registering MVC, otherwise, MVC will overwrite your registrations.

And now we can go all crazy with our custom endpoints. So now not only we go full POCO, but we even don’t need the mandatory “Controller” suffix anymore.