深入分析 ASP.NET Mvc 深入MvcHandler

深入分析 ASP.NET Mvc  深入MvcHandler

 

MvcHandler是一个mvc程序真正开始的地方,因为你可以直接看到并调试它的源码。

MvcHandler的主要代码如下:

protected internal virtual void ProcessRequest(HttpContextBase httpContext) {
            AddVersionHeader(httpContext);

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

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

这个方法的流程可以概括为: 找到Requst中的Controller name, 根据Controller name创建这个Controller, 执行这个Controller中执行被请求的

Action。

具体分析如下:

 

1. 添加Http Header

AddVersionHeader(httpContext);

添加一个Http Header: HTTP/1.1 200 OK   …   X-AspNetMvc-Version: 1.0…

 

2. 从路由表中找到请求的controller的名子

string controllerName = RequestContext.RouteData.GetRequiredString("controller");

获取路由表中的controller name, 在下面的代码中根据这个controller name在缓存中查找到对应的controller类型并生成controller类。

 

3. 返回一个IControllerFactory对象

IControllerFactory factory = ControllerBuilder.GetControllerFactory();

返回一个继承自IControllerFactory接口的类的实例,这里默认返回DefaultControllerFactory类。 ControllerBuilder属性是ControllerBuilder类的一个静态实例,在mvc程序第一次启动时才会执行ControllerBuilder类的默认构造函数, 在这个构造函数将DefaultControllerFactory类的一个实例传入到SetControllerFactory()方法中, 这样做的目地是定义GetControllerFactory()的具体返回类型。ControllerBuilder类的构造函数代码如下:

public ControllerBuilder() {
            SetControllerFactory(new DefaultControllerFactory() {
                ControllerBuilder = this
            });
        }

所以想要改变GetControllerFactory()的默认返回类型的办法就是在执行ControllerBuilder.GetControllerFactory()之前调用ControllerBuilder类中的

SetControllerFactory()方法,这个方法有两个重载

public void SetControllerFactory(IControllerFactory controllerFactory) {
            if (controllerFactory == null) {
                throw new ArgumentNullException("controllerFactory");
            }

            _factoryThunk = () => controllerFactory;
        }

public void SetControllerFactory(Type controllerFactoryType) {
            if (controllerFactoryType == null) {
                throw new ArgumentNullException("controllerFactoryType");
            }
            if (!typeof(IControllerFactory).IsAssignableFrom(controllerFactoryType)) {
                throw new ArgumentException(
                    String.Format(
                        CultureInfo.CurrentUICulture,
                        MvcResources.ControllerBuilder_MissingIControllerFactory,
                        controllerFactoryType),
                    "controllerFactoryType");
            }

            _factoryThunk = delegate() {
                try {
                    return (IControllerFactory)Activator.CreateInstance(controllerFactoryType);
                }
                catch (Exception ex) {
                    throw new InvalidOperationException(
                        String.Format(
                            CultureInfo.CurrentUICulture,
                            MvcResources.ControllerBuilder_ErrorCreatingControllerFactory,
                            controllerFactoryType),
                        ex);
                }
            };
        }

只需要将我们自定义并继承自IControllerFactory接口的类的一个实例或type传入就可以。

 

4. 根据controller Name创建controller对象

IController controller = factory.CreateController(RequestContext, controllerName);

调用DefaultControllerFactory类中的CreateController方法创建controller类。 CreateController(…)方法的具体代码如下:

public virtual IController CreateController(RequestContext requestContext, string controllerName)
        {
            if (requestContext == null)
            {
                throw new ArgumentNullException("requestContext");
            }
            if (String.IsNullOrEmpty(controllerName))
            {
                throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
            }
            RequestContext = requestContext;
            Type controllerType = GetControllerType(controllerName);
            IController controller = GetControllerInstance(controllerType);
            return controller;
        }

这里比较简单,首先执行GetControllerType(controllerName)找到对应的controll type, 再调用GetControllerInstance(controllerType) 反射出具体的controll类,先来看看GetControllerType(…)方法中的代码:

 

protected internal virtual Type GetControllerType(string controllerName)
        {
            if (String.IsNullOrEmpty(controllerName))
            {
                throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
            }

            // first search in the current route's namespace collection
            object routeNamespacesObj;
            Type match;
            if (RequestContext != null && RequestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj))
            {
                IEnumerable<string> routeNamespaces = routeNamespacesObj as IEnumerable<string>;
                if (routeNamespaces != null)
                {
                    HashSet<string> nsHash = new HashSet<string>(routeNamespaces, StringComparer.OrdinalIgnoreCase);
                    match = GetControllerTypeWithinNamespaces(controllerName, nsHash);
                    if (match != null)
                    {
                        return match;
                    }
                }
            }

            // then search in the application's default namespace collection
            HashSet<string> nsDefaults = new HashSet<string>(ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);
            match = GetControllerTypeWithinNamespaces(controllerName, nsDefaults);
            if (match != null)
            {
                return match;
            }

            // if all else fails, search every namespace
            return GetControllerTypeWithinNamespaces(controllerName, null /* namespaces */);
        }

RequestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj)来返回一个namespace的集合, 一开始我对namespace很不理解,现在我明白了它的意思:在程序中不同的namespace下面可能会存在同名的controller,所以这里用namespace区分这些同名的但不同意义的controller。namespace可以在Global.asax.cs的RegisterRoutes(…)方法中指定,比如:

routes.MapRoute(
                "Default",                                              // Route name
                "{controller}/{action}/{id}",                           // URL with parameters
                new { controller = "Home", action = "Index", id = "" },  // Parameter defaults
                new { httpMethod = new HttpMethodConstraint("get", "post") }
                ,new string[]{"Namespace1"}
            );

 

在继续看GetControllerType(…)方法,在GetControllerType(string controllerName) 方法中最终都是通过调用GetControllerTypeWithinNamespaces(…)方法返回controller type的, 具体代码如下:

private Type GetControllerTypeWithinNamespaces(string controllerName, HashSet<string> namespaces)
        {
            // Once the master list of controllers has been created we can quickly index into it
            ControllerTypeCache.EnsureInitialized(BuildManager);

            IList<Type> matchingTypes = ControllerTypeCache.GetControllerTypes(controllerName, namespaces);
            switch (matchingTypes.Count)
            {
                case 0:
                    // no matching types
                    return null;

                case 1:
                    // single matching type
                    return matchingTypes[0];

                default:
                    // multiple matching types
                    // we need to generate an exception containing all the controller types
                    StringBuilder sb = new StringBuilder();
                    foreach (Type matchedType in matchingTypes)
                    {
                        sb.AppendLine();
                        sb.Append(matchedType.FullName);
                    }
                    throw new InvalidOperationException(
                        String.Format(
                            CultureInfo.CurrentUICulture,
                            MvcResources.DefaultControllerFactory_ControllerNameAmbiguous,
                            controllerName, sb));
            }
        }

首先将执行ControllerTypeCache.EnsureInitialized(BuildManager);  他的作用是将程序中所有assembly中所有以Controller结尾的类放在缓存中,看一下

EnsureInitialized(…)方法的代码:

 
    

ControllerBuilder.cs

public void EnsureInitialized( IBuildManager buildManager) { if (_cache == null) { lock (_lockObj) { if (_cache == null) { List< Type> controllerTypes = GetAllControllerTypes(buildManager); var groupedByName = controllerTypes.GroupBy( t => t.Name.Substring(0, t.Name.Length - "Controller".Length), StringComparer.OrdinalIgnoreCase); _cache = groupedByName.ToDictionary( g => g.Key, g => g.ToLookup(t => t.Namespace ?? String.Empty, StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase); } } } }

这是一个具有2级结构的缓存, 以controll name为key, 以Lookup对象为值保存到缓存中,而Lookup的结构是以namespace为key, 以

controller type为值的键值集合,这个2级结构的作用就是上面提到的用来解决不同namespace中同名controller的问题。

 

GetControllerTypeWithinNamespaces(string controllerName, HashSet<string> namespaces) 方法中:

ControllerTypeCache.GetControllerTypes(controllerName, namespaces) 就是去找具有相同controllerName的controller type,不过这里有个问题就是如果没有在

Global中或其它地方提供默认的namespace而不同namespace下存在同名的controller,就导致GetControllerTypes(…)方法返回的controller数量大于1,这时程序会在

switch语句处抛出一个异常,所里一定要注意,尽量不要在不同的namespace中定义同名的controller

当找到一个对应的 controller type后,就将这个type返回给上面的CreateController(RequestContext requestContext, string controllerName) 方法中调用

GetControllerType(controllerName); 方法的地方, 然后再调用GetControllerInstance(controllerType); 方法将方法反射成具体的controller类并返回到ProcessRequest(…)中,并依次执行controller.Execute(RequestContext) –> factory.ReleaseController(controller);   至此整个MvcHandler的流程执行完毕.

 

附上MvcHandler的时序图:

深入分析 ASP.NET Mvc 深入MvcHandler_第1张图片


你可能感兴趣的:(.Net技术)