Click here to Skip to main content
15,884,176 members
Articles / Web Development / ASP.NET

MvcRouteHandler and MvcHandler in ASP.NET MVC Framework

Rate me:
Please Sign up or sign in to vote.
4.90/5 (10 votes)
20 May 2013CPOL5 min read 91.8K   26   4
This article explains the working of MvcRouteHandler and MvcHandler class in detail.

Introduction

Whenever a request is received by MVC, it is the job of the routing engine to match the request URL with the registered routes. After finding the matched route, the route handler for the route is being called. Each route can have its own route handler. A route handler is a class that implements IRouteHandler interface.

IRouteHandler Interface

It defines a contract that a class must implement in order to process a request for a matching route pattern. It exposes a single method, the GetHttpHandler() method, which is responsible for providing the route handler class instance that will process the request. This how the interface looks like:

C#
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// See License.txt in the project root for license information.

public interface IRouteHandler
{
    IHttpHandler GetHttpHandler(RequestContext requestContext);
}

Any route that is added through the MapRoute extension method is handled by the default route handler, that is the MvcRouteHandler class defined in the System.Web.Mvc namespace.

MvcRouteHandler Class

It is the responsibility of the route handler to determine the HTTP handler that will serve the request, by looking at the received RequestContext. The MvcRouteHandler class implementation, as shown here:

C#
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// See License.txt in the project root for license information.

namespace System.Web.Mvc 
{
using System.Web.Routing;
using System.Web.SessionState;

public class MvcRouteHandler : IRouteHandler {
    private IControllerFactory _controllerFactory;

        public MvcRouteHandler() {
        }

        public MvcRouteHandler(IControllerFactory controllerFactory) {
            _controllerFactory = controllerFactory;
        }

        protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext) {
            requestContext.HttpContext.SetSessionStateBehavior(
                           GetSessionStateBehavior(requestContext));
            return new MvcHandler(requestContext);
        }

        protected virtual SessionStateBehavior 
        GetSessionStateBehavior(RequestContext requestContext) {
            string controllerName = 
            (string)requestContext.RouteData.Values["controller"];
            IControllerFactory controllerFactory = _controllerFactory ?? 
                           ControllerBuilder.Current.GetControllerFactory();
            return controllerFactory.GetControllerSessionBehavior
            		(requestContext, controllerName);
        }

        #region IRouteHandler Members

        IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext) {
            return GetHttpHandler(requestContext);
        }

        #endregion
    }
}

It’s straightforward to see that, in the given preceding code, the class implements IRouteHandler interface's GetHttpHandler() method, which does two things: first, it makes a call to the SetSessionStateBehavior() method, for the current Http request, in order to set the session state support that is needed to handle the request, by passing a SessionStateBehavior enumeration value to specify what type of session state behavior applies to the request. In order to get the session state behavior, a call is made to the GetSessionStateBehavior() method, which gets the controller name and finds the controller factory using the ControllerBuilder class. The ControllerBuilder class returns a reference of DefaultControllerFactory class (which is a type of IControllerFactory interface, and under the assumption that we don't have any custom controller factory). The DefaultControllerFactory class GetControllerSessionBehavior() method is called, passing the current RequestContext and the controller type, which in turn returns a SessionStateBehavior enumeration value. Now, after setting the SessionStateBehavior, it proceeds with the creation of an instance of MvcHandler class, which is the default ASP.NET MVC HTTP handler, to serve the request.

The MvcHandler Class

It is the responsibility of MvcHandler class for generating the response for the ongoing request being processed. The MvcHandler class receives information about the ongoing request from the RequestContext passed to its constructor, in the implementation of the GetHttpHandler() method in the MvcRouteHandler class. The MvcHandler class implements three interfaces : IHttpAsyncHandler, IHttpHandler and IRequiresSessionState.

IRequiresSessionState interface, which when implemented specifies that the current HTTP handler requires read and write access to session-state values. This is a marker interface and has no methods in it, to implement.

IHttpHandler interface defines the contract that a class must implement in order to synchronously process HTTP Web requests using HTTP handler. It exposes a single method, i.e., the ProcessRequest() method. IHttpAsyncHandler interface is the asynchronous version of IHttpHandler interface.

This is how the MvcHandler class implements IHttpHandler.

C#
// Copyright (c) Microsoft Open Technologies, Inc.
// All rights reserved. See License.txt in the project root for license information.

void IHttpHandler.ProcessRequest(HttpContext httpContext) 
{
    ProcessRequest(httpContext);
}

protected virtual void ProcessRequest(HttpContext httpContext) 
{
    HttpContextBase iHttpContext = new HttpContextWrapper(httpContext);
                ProcessRequest(iHttpContext);
}

protected internal virtual void ProcessRequest(HttpContextBase httpContext) {
    SecurityUtil.ProcessInApplicationTrust(() => {
        IController controller;
        IControllerFactory factory;
        ProcessRequestInit(httpContext, out controller, out factory);

    try
    {
        controller.Execute(RequestContext);
    }
    finally
    {
        factory.ReleaseController(controller);
    }
    });
}

The ProcessRequest() method converts the HttpContext into a more generic container—the HttpContextWrapper class. HttpContextWrapper class is a wrapper for HttpContext and extends HttpContextBase class. Now the question is why are we using HttpContextWrapper instead of HttpContext. The reason is that the HttpContext class has no base class and it's not virtual also, so it's not possible to use it in unit testing. Hence, we use HttpContextBase class, which is abstract and consists of the same members as the HttpContext class. The HttpContextBase class enables one to create derived classes that are similar to the HttpContext class, that can be used for unit testing.

After getting HttpContextBase a call is made to the internal version of ProcessRequest() method, passing HttpContextBase parameter to it. The call is then further to ProcessRequestInit(), which is responsible for extracting controllers name from RouteData collection and request controller factory to create corresponding controller.

C#
// Copyright (c) Microsoft Open Technologies, Inc.
// All rights reserved. See License.txt in the project root for license information.

internal static readonly string MvcVersion = GetMvcVersionString();
public static readonly string MvcVersionHeaderName = "X-AspNetMvc-Version";

internal ControllerBuilder ControllerBuilder {
    get {
        if (_controllerBuilder == null) {
                _controllerBuilder = ControllerBuilder.Current;
        }
        return _controllerBuilder;
    }
    set {
        _controllerBuilder = value;
    }
}

private void ProcessRequestInit(HttpContextBase httpContext, 
             out IController controller, out IControllerFactory factory) {

    // If request validation has already been enabled, make it lazy.
    // This allows attributes like [HttpPost] (which looks
    // at Request.Form) to work correctly without triggering full validation.
    bool? isRequestValidationEnabled = 
       ValidationUtility.IsValidationEnabled(HttpContext.Current);
    if (isRequestValidationEnabled == true) {
        ValidationUtility.EnableDynamicValidation(HttpContext.Current);
    }

    AddVersionHeader(httpContext);
    RemoveOptionalRoutingParameters();

    // Get the controller type
    string controllerName = RequestContext.RouteData.GetRequiredString("controller");

    // Instantiate the controller and call Execute
    factory = ControllerBuilder.GetControllerFactory();
    controller = factory.CreateController(RequestContext, controllerName);
    if (controller == null) {
        throw new InvalidOperationException(
        String.Format(
            CultureInfo.CurrentCulture,
            MvcResources.ControllerBuilder_FactoryReturnedNull,
            factory.GetType(),
            controllerName));
    }
}

protected internal virtual void AddVersionHeader(HttpContextBase httpContext) {
    if (!DisableMvcResponseHeader) {
        httpContext.Response.AppendHeader(MvcVersionHeaderName, MvcVersion);
    }
}

private static string GetMvcVersionString() {
    // DevDiv 216459:
    // This code originally used Assembly.GetName(),
    // but that requires FileIOPermission, which isn't granted in
    // medium trust. However, Assembly.FullName *is* accessible in medium trust.
    return new AssemblyName(typeof(MvcHandler).Assembly.FullName).Version.ToString(2);
}

The ProcessRequestInit(), receives the HttpContextBase object containing Http specific information and references of IController and IControllerFactory type, which will be holding the created copies of controller and controller factory objects at the end. It starts with, adding the header information to the response.

Then, in order to get the controller factory, which is responsible for the creation of instances of any controller class, the ControllerBuilder class is used. The reference to ControllerBuilder property in ProcessRequestInit() method, it returns an instance of the DefaultControllerFactory class, if there is no custom controller factory. After getting the instance of the controller factory, it is then used to instantiate a controller, by making a call to CreateController() method of the DefaultControllerFactory class. You can find more about DefaultControllerFactory class over here.

After getting the instance of the controller, the Execute() method is called for this controller. The execute method is responsible for invoking the action method whose name matches the action value in the route data. To know more about the working of Execute() method, refer to my previous post.

After the completion of the Execute() method, the ReleaseController() method is called, which is present in the DefaultControllerFactory class, in order to dispose the controller instance.

ControllerBuilder Class

The ControllerBuilder is a singleton class responsible for producing an instance of type IControllerFactory. The ControllerBuillder class has two constructors, a default one and another parametrized one.

C#
 // Copyright (c) Microsoft Open Technologies, Inc.
// All rights reserved. See License.txt in the project root for license information. 

        private Func<IControllerFactory> _factoryThunk = () => null;
        private static ControllerBuilder _instance = new ControllerBuilder();
        private IResolver<IControllerFactory> _serviceResolver;

        public ControllerBuilder()
            : this(null) {
        }

        internal ControllerBuilder(IResolver<IControllerFactory> serviceResolver) {
            _serviceResolver = serviceResolver ?? 
            new SingleServiceResolver<IControllerFactory>(
                () => _factoryThunk(),
                 new DefaultControllerFactory { ControllerBuilder = this },
                "ControllerBuilder.GetControllerFactory"
            );
        }

        public static ControllerBuilder Current {
            get {
                return _instance;
            }
        }

        [SuppressMessage("Microsoft.Design", 
        "CA1024:UsePropertiesWhereAppropriate", Justification = 
        "Calling method multiple times might return different objects.")]
        public IControllerFactory GetControllerFactory() {
            return _serviceResolver.Current;
        }

public HashSet<string> DefaultNamespaces {
            get {
                return _namespaces;
            }
        } 

The default constructor calls constructor with parameter, passing null as argument, saying that SingleServiceResolver object has to be used as default service resolver, which gets saved in the _serviceResolver. Now whenever we need a reference to the ControllerBuilder object, we get it through the Current property. In order to get the created instance of the DefaultControllerFactory class outside this class, the GetControllerFactory() method is used.

ControllerBuilder class also exposes DefaultNamespaces property which is used to define the default namespaces at the start of the application. Whenever default namespaces are provided using this property, DefaultControllerFactory uses these namespaces to find a controller type.

Apart from the above implementations, ControllerBuilder class also exposes SetControllerFactory() method which acts as a setter method and allows you to change default controller factory with custom one.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Mindfire Solutions
India India
Software developer, working at Mindfire Solutions, having hands on experience in both Windows and web application using C#, ASP.NET, ASP.NET MVC.

Comments and Discussions

 
QuestionRouting Pin
Member 1399761924-Oct-18 22:33
Member 1399761924-Oct-18 22:33 
QuestionMy Vote 5 Pin
ganesh1610-May-17 16:45
ganesh1610-May-17 16:45 
GeneralMy vote of 5 Pin
Hitesh0720-Dec-14 0:20
Hitesh0720-Dec-14 0:20 
QuestionMvc Query for image Handler Pin
Member 108655994-Dec-14 0:09
Member 108655994-Dec-14 0:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.