In my last post I referred to Route Hijacking. This is a term used by Umbraco and enables you to let Umbraco find your custom controllers based on the RenderMvcController rather than returning the regular RenderMvcController. But I don’t need and don’t want the RenderMvcController so this post explains how you can create your own ControllerFactory which Umbraco will use.
To get a basic understanding of Hijacking Umbraco routes you should read the details on our.umbraco.org: http://our.umbraco.org/documentation/Reference/Mvc/custom-controllers
The way this hijacking works does not reflect how I want to develop my Umbraco applications. And if I think about it for a while I think it would be awesome to be able to create Generic Controllers based on Glass Type models following a convention where the closest type to the current documents type is used.
I want the convention to follow 2 steps:
This way I could create a PageController for all my pages which would look like:
And if I feel a pageType deserves it’s own controller then I could add something like:
For this to work I need to add 3 things to my project:
The easiest step is to create a base controller class. To be able to use it in Umbraco MVC we need to implement Controller as well as IRenderMvcController, ending up with something like:
This is the trickiest part and there is certainly room for improvement, but this works :)
Inheriting from the UmbracoControllerFactory class, I have overridden 3 methods:
I want to use this factory only if I can resolve a GlassType based on the current content’s DocumentTypeAlias and if no SurfaceController is found. Using an extension method where I can get an UmbracoTypeConfiguration based on a string I can determine if the current page has a Glass Type.
Now this is where the magic starts to happen. I collect a list of Glass Types based on the current DocumentTypeAlias. Then I get all the GlassControllers which implement on of the types and sort them based on the index in the Glass Types collection and returning the first. This gives me the most suitable Controller if there is any.
After that magic has happened I create an instance of the controller and manipulate the RouteData so Mvc knows the controller to use, later on we know what method to look for and let Umbraco know not to interfere.
I actually already use the custom ActionInvoker in the CreateController method of the Factory because it is an property on a Mvc Controller. The class inherits from Mvc’s ControllerActionInvoker and I override the FindAction method.
In this method I try to get the method using the given actionName parameter (set to the DocumentTypeAlias set in the CreateController method) and if that fails then I try to find an Index method.
Is that it?
Well, not exactly. The only thing left to do for this to work is letting Umbraco know our ControllerFactory should be taken into consideration. For this I have extended the UmbracoApplication class and in the OnApplicationStarting method I insert my custom ControllerFactory as first to consider before using it’s own factories.
Then set your Global.asax file to use the extended Application class and you’re good to go. I am currently using this and am very satisfied with the results so far. I get very clean projects and feel it is much closer to an actual Mvc application.