Join me on a journey through the ASP.NET pipeline in search of Episerver. Part three: Dude, where’s my controller?

This is the third and final part of the “Join me on a journey through the ASP.NET pipeline in search of Episerver“-series. And, I can see you how may be puzzled that we’re at road’s end, as I’ve been using the Indiana Jones movies as reference. But, no, there’s not four Indy movies as far as I’m concerned. Only the three.

In search of Episerver: The Basics, ContentRoute
In search of Episerver: Route selection, ISegment
In search of Episerver: Route handler, Controller Factory (this blog post)

We left off in the previous post in this series knowing how Episerver maps the friendly URL (FURL) to an ASP.NET route using implementations of the Episerver ISegment interface. We’ve now found Episerver in the sense that we know how an Episerver ContentRoute route, which inherits the base ASP.NET Route class, is selected by the ASP.NET framework.

As explained in the first blog post in this series, ASP.NET Routing with Episerver, the basics, there’s two parts to ASP.NET routing: the route itself and the associated route handler. When a route is added to the route table a route handler must be associated with the route. The route handler serves a single, but important, purpose: returning a HTTP handler to process the request.

From an ASP.NET perspective the execution of the HTTP handler associated with a route is the entry point into either the ASP.NET MVC framework or ASP.NET Web Forms. This is something Episerver leverages in order to support both ASP.NET MVC and ASP.NET Web Forms as we’ll discover.

Let’s pull pack the curtain on how Episerver goes about this. Or, as the title says: dude, where’s my controller?

The MultiplexingRouteHandler

Let’s start off by investigating how a route, and specifically a route handler, relates to the Episerver ContentRoute named pages and how that ties into ASP.NET. We’ll do so by creating an IInitializableModule and hooking up to the RoutesRegistered event in the EPiServer.Global class in order to debug the selected route handler.

Adding a breakpoint in the Global_RoutesRegistered method and inspecting the RoutesRegistrationEventArgs parameter (e), we get ahold of the routes registered and their associated route handlers. We’re, as stated earlier, primarily interested in the registered route handler for the pages route.

pages-route-handler

Hey, look at that! The pages ContentRoute has a RouteHandler associated with it and it appears to be of type MultiplexingRouteHandler. If this had been vanilla ASP.NET MVC we would have seen a MvcRouteHandler.

As you remember the route handler, defined by the IRouteHandler interface, performs a simple, but important task: to return a IHttpHandler to serve our request. If we look at the ASP.NET MVC source code for its MvcRouteHandler class the implementation of the GetHttpHandler method looks like this.

As we can see the ASP.NET MVC framework always returns a MvcHandler in its route handler. In turn we can assume that the Episerver MultiplexingRouteHandler does something similar.

The key difference, however, between vanilla ASP.NET MVC and Episerver is that Episerver extends ASP.NET routing to support both ASP.NET MVC and ASP.NET Web Forms. In the sample code above, provided by the ASP.NET MVC framework, the IHttpHandler returned by the route handler MvcRouteHandler, is of type MvcHandler, which only supports the ASP.NET MVC framework.

Before we go any further down the rabbit’s hole you should know that multiplexing is Episerver-speak for multiple and fallback. You may already have come across it with the MultiplexingMembershipProvider and the MultiplexingRoleProvider which are built and used by Episerver for authentication and authorization, being able to leverage both SQL- and Windows authentication and authorization (or a mix!).

The MultiplexingRouteHandler is where Episerver decides if the request should be served by ASP.NET MVC or ASP.NET Web Forms, since both ASP.NET MVC and ASP.NET Web Forms ultimately leverages a HTTP handler to serve the request.

Let’s have a closer look at this by implementing our own route handler and replacing the default Episerver MultiplexingRouteHandler. We’ll do this by inheriting from the MultiplexingRouteHandler and telling the IoC container to use our implementation instead.

There’s not much going on in the sample code above that is our custom route handler, we simply want to be able to hit a breakpoint and see what the base implementation of the MultiplexingRouteHandler returns. Though, first we also need to register our custom implementation with the IoC container. Since we’re rocking the sample Alloy project we can do this in the DependencyResolverInitialization class.

Let’s fire up the Alloy site and see what we get when the GetRouteHandler method of the MultiplexingRouteHandler class is called and our breakpoint in the MyMultiplexingRouteHandler class is hit.

routehandler-mvcroutehandler

Aha! For the start page, since we’re using ASP.NET MVC for our Alloy site, we get a MvcRouteHandler. Now, you might ask, wasn’t the job of the route handler to return a HTTP handler? And you’re right — it’s what Episerver does. Immediately after Episerver has found a suitable route handler for the request, the MultiplexingRouteHandler calls into the GetHttpHandler method of the selected route handler, returning a suitable HTTP handler to serve our request.

This might seem a little confusing, as if we’ve now left Episerver and gone vanilla ASP.NET MVC. In fact, there’s some additional magic going on under the covers in the MultiplexingRouteHandler. It’s where Episerver calls into the TemplateResolver to find a suitable template to render the request. These values are added to the route data dictionary to be used further down the pipeline when creating our controller.

Last stop, the controller factory!

The final stop we’re going to make is at the controller factory, which is used to create a suitable controller to serve our request. By default, ASP.NET MVC looks at the controller name and tries to find a suitable class which matches the controller name. With Episerver, however, we’re also able to register multiple controllers with the same model, and using them in different context.

As mentioned before, when the MultiplexingRouteHandler executes, it identifies a suitable template for the request using the TemplateResolver. If a template is found, and the type of the template is ASP.NET MVC, then Episerver adds the controller name to the route data dictionary — which is default ASP.NET MVC behavior — but Episerver also adds the template type. The template type is added to the data tokens of the route data dictionary, which is ignored by ASP.NET MVC, but is something leveraged by Episerver as we reach the Episerver ControllerTypeControllerFactory controller factory, which extends the ASP.NET MVC DefaultControllerFactory implementation.

Let’s examine the inner workings of the Episerver controller factory by rolling our own custom implementation. We inherit from the base Episerver implementation ControllerTypeControllerFactory and override the GetControllerType method which the ASP.NET MVC framework will call into to get the type of controller that should be created.

And the output will look like this for the Alloy sample project start page.

startpagecontroller

As we can see we get the type StartPageController in both our sample code and the base Episerver controller factory. Success!

The key ingredient of the Episerver ControllerTypeControllerFactory is that it inherits from the DefaultControllerFactory of the ASP.NET MVC framework and overrides GetControllerType method. This allows for Episerver to tell the ASP.NET MVC framework to use a controller of a specific type by looking at what’s been added to the route data dictionary.

Let’s, for the last time, dive into some ASP.NET MVC source to see where the overriden GetControllerType method gets called.

As you can see, in the much important CreateController method, just before the GetControllerInstance method is called, ASP.NET MVC will call the GetControllerType method, which in our case will return StartPageController. And that, dude, is where your controller is at.

We’ve arrived!

We’ve finally arrived at the point where a controller is selected and created to process our request. I hope I leave you armed with knowledge of just how Episerver leverages the ASP.NET, and to some extent, the ASP.NET MVC framework in order to do its magic.

Thanks for reading!

More

In search of Episerver: The Basics, ContentRoute
In search of Episerver: Route selection, ISegment
In search of Episerver: Route handler, Controller Factory (this blog post)

daniel
daniel
Developer
Recommended Posts
Contact Us

We're not around right now. But you can send us an email and we'll get back to you, asap.

Not readable? Change text. captcha txt

Start typing and press Enter to search