带你读开源—ASP.NET_MVC(七)

        我们继续谈细节。

        我们注意到ASP.NETMVC项目都包含一个Global.asax文件,这个文件代码很少,只包括三个方法,即RegisterGlobalFilters、RegisterRoutes、Application_Start。

我们看RegisterRoutes方法的定义(代码段1)。

      

 public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
           routes.MapRoute(
               "Default", // Route name
               "{controller}/{action}/{id}", // URL with parameters
               new { controller = "Home", action = "Index", id =UrlParameter.Optional } // Parameter defaults
           );
        }

代码段 1

        代码段1中的routes.MapRoute语句是路由映射的具体实现,我们在MVC源码中看其定义(代码段2)。

      

 [SuppressMessage("Microsoft.Design","CA1054:UriParametersShouldNotBeStrings", MessageId = "2#",Justification = "This is not a regular URL as it may contain specialrouting characters.")]
       public static Route MapRoute(this RouteCollection routes, string name,string url, object defaults, object constraints, string[] namespaces)
        {
           if (routes == null)
           {
               throw new ArgumentNullException("routes");
           }
           if (url == null)
           {
               throw new ArgumentNullException("url");
           }
 
           Route route = new Route(url, new MvcRouteHandler())
           {
               Defaults = CreateRouteValueDictionaryUncached(defaults),
               Constraints = CreateRouteValueDictionaryUncached(constraints),
               DataTokens = new RouteValueDictionary()
           };
 
           ConstraintValidation.Validate(route);
 
           if ((namespaces != null) && (namespaces.Length > 0))
           {
               route.DataTokens[RouteDataTokenKeys.Namespaces] = namespaces;
           }
 
           routes.Add(name, route);
 
           return route;
        }

代码段 2

        代码段2中route用一个MvcRouteHandler来实例化,即封装了MVC路由处理机制。我们完全可以自定义一个IRouteHandler的实现,例如代码段3:

    publicclass MyRouteHandler : IRouteHandler
    {
       public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
           return new MyHandler();
        }
    }


代码段 3

        代码段3中的MyHandler定义见代码段4。

   

 publicclass MyHandler : IHttpHandler
    {
       public bool IsReusable
        {
           get { return false; }
        }
       public void ProcessRequest(HttpContext context)
        {
            context.Response.Write("HELLO");
        }
    }

代码段 4

        我们现在把代码段1中的routes.MapRoute这句代码,更换为代码段5。

routes.Add("MyRoute", newRoute("My", new MyRouteHandler()));


代码段 5

        好了,现在我们按下F5进行调试,在浏览器地址栏输入http://localhost:48580/My,回车,看见图1了没?浏览器显示我们在代码段4中希望输出的“HELLO”。这里我们自己定义了一个新的路由规则,即当我们访问http://localhost:48580/My的时候,就会调用我们自定义的MyRouteHandler,成就感有木有?有木有?当然,我们自定义的这条路由规则URL只是静态匹配,它非常简单,以至于简单到不能再简单,以至于毫无用途,但我们毕竟通过这个例子,明白了MVC路由系统的大概端倪。当然,代码段5中的静态URL(My)也可以更换成动态URL,比如“{My}/{Love}”,这样当我们访问http://localhost:48580/hello/kitty时,就会调用我们自定义的路由。可见,ASP.NET的路由系统把花括号“{}”中的内容看作一个变量(或者占位符),之间用斜杠“/”分割,并以此来匹配浏览器输入的URL字符串。

图 1

        前面我们谈控制器时,没有提到控制器是如何实例化的,下面我们进入这个话题。MVC框架中的controller由控制器工厂进行实例化(代码段6),而系统预置的控制器工厂是DefaultControllerFactory,它实现了IControllerFactory接口。

    

        // Instantiate the controller andcall Execute
            factory =ControllerBuilder.GetControllerFactory();
            controller = factory.CreateController(RequestContext,controllerName);

代码段 6

        我们看看DefaultControllerFactory的具体实现(代码段7),这段代码中最核心的语句是【Type controllerType =GetControllerType(requestContext, controllerName);[1] IController controller = GetControllerInstance(requestContext,controllerType); [2]】,这两句的功能是:首先根据路由传递过来的Controller名称得到控制器的类型;再根据控制器类型,创建IController的具体实例。

   

     public virtual IControllerCreateController(RequestContext requestContext, string controllerName)
        {
            if (requestContext == null)
            {
                throw newArgumentNullException("requestContext");
            }
 
            if(String.IsNullOrEmpty(controllerName) &&!requestContext.RouteData.HasDirectRouteMatch())
            {
                throw newArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
            }
 
            Type controllerType =GetControllerType(requestContext, controllerName);
            IController controller =GetControllerInstance(requestContext, controllerType);
            return controller;
        }

代码段 7

        我们跟踪进入语句[1]的具体定义(代码段8),可以看到其中有三句很扎眼的注释【// first search in the current route's namespace collection】、【// then search in the application's default namespace collection】、【// if all else fails, search every namespace】,这些注释清楚的告诉我们MVC框架搜索控制器的顺序。

 

       protected internal virtual TypeGetControllerType(RequestContext requestContext, string controllerName)
        {
            if (requestContext == null)
            {
                throw newArgumentNullException("requestContext");
            }
 
            if(String.IsNullOrEmpty(controllerName) &&
                (requestContext.RouteData ==null || !requestContext.RouteData.HasDirectRouteMatch()))
            {
                throw newArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
            }
 
            RouteData routeData =requestContext.RouteData;
            if (routeData != null &&routeData.HasDirectRouteMatch())
            {
                returnGetControllerTypeFromDirectRoute(routeData);
            }
 
            // first search in the currentroute's namespace collection
            object routeNamespacesObj;
            Type match;
            if(routeData.DataTokens.TryGetValue(RouteDataTokenKeys.Namespaces, outrouteNamespacesObj))
            {
                IEnumerablerouteNamespaces = routeNamespacesObj as IEnumerable;
                if (routeNamespaces != null&& routeNamespaces.Any())
                {
                    HashSetnamespaceHash = new HashSet(routeNamespaces,StringComparer.OrdinalIgnoreCase);
                    match =GetControllerTypeWithinNamespaces(routeData.Route, controllerName,namespaceHash);
 
                    // the UseNamespaceFallbackkey might not exist, in which case its value is implicitly "true"
                    if (match != null ||false.Equals(routeData.DataTokens[RouteDataTokenKeys.UseNamespaceFallback]))
                    {
                        // got a match or theroute requested we stop looking
                        return match;
                    }
                }
            }
 
            // then search in the application'sdefault namespace collection
            if(ControllerBuilder.DefaultNamespaces.Count > 0)
            {
                HashSet namespaceDefaults = newHashSet(ControllerBuilder.DefaultNamespaces,StringComparer.OrdinalIgnoreCase);
                match =GetControllerTypeWithinNamespaces(routeData.Route, controllerName,namespaceDefaults);
                if (match != null)
                {
                    return match;
                }
            }
 
            // if all else fails, search everynamespace
            returnGetControllerTypeWithinNamespaces(routeData.Route, controllerName, null /* namespaces*/);
        }

代码段 8

        这三条注释所作用的语句中都用到了GetControllerTypeWithinNamespaces方法,其实现见代码段9。

   

     private TypeGetControllerTypeWithinNamespaces(RouteBase route, string controllerName,HashSet namespaces)
        {
            // Once the master list ofcontrollers has been created we can quickly index into it
           ControllerTypeCache.EnsureInitialized(BuildManager);
 
            ICollectionmatchingTypes = ControllerTypeCache.GetControllerTypes(controllerName,namespaces);
            switch (matchingTypes.Count)
            {
                case 0:
                    // no matching types
                    return null;
 
                case 1:
                    // single matching type
                    return matchingTypes.First();
 
                default:
                    // multiple matching types
                    throwCreateAmbiguousControllerException(route, controllerName, matchingTypes);
            }
        }

代码段 9

你可能感兴趣的:(带你读开源)