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

我们继续谈细节。
我们注意到ASP.NET MVC项目都包含一个Global.asax文件,这个文件代码很少,只包括三个方法,即RegisterGlobalFilters、RegisterRoutes、Application_Start。
我们看RegisterRoutes方法的定义(代码段1)。

C# code

?

1
2
3
4
5
6
7
8
9
        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)。

CSS code

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  [SuppressMessage("Microsoft.Design""CA1054:UriParametersShouldNotBeStrings", MessageId = "2#", Justification = "This is not a regular URL as it may contain special routing 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:

C# code

?

1
2
3
4
5
6
7
 public class MyRouteHandler : IRouteHandler
    {
        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            return new MyHandler();
        }
    }

 

代码段 3

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

C# code

?

1
2
3
4
5
6
7
8
9
10
11
 public class MyHandler : IHttpHandler
    {
        public bool IsReusable
        {
            get return false; }
        }
        public void ProcessRequest(HttpContext context)
        {
            context.Response.Write("HELLO");
        }
    }

 

代码段 4

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

C# code

?

1
routes.Add("MyRoute"new Route("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字符串。
带你读开源—ASP.NET_MVC(七)

图 1

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

C# code

?

1
2
3
   // Instantiate the controller and call 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的具体实例。

C# code

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 public virtual IController CreateController(RequestContext requestContext, string controllerName)
        {
            if (requestContext == null)
            {
                throw new ArgumentNullException("requestContext");
            }
            if (String.IsNullOrEmpty(controllerName) && !requestContext.RouteData.HasDirectRouteMatch())
            {
                throw new ArgumentException(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框架搜索控制器的顺序。

C# code

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
  protected internal virtual Type GetControllerType(RequestContext requestContext, string controllerName)
        {
            if (requestContext == null)
            {
                throw new ArgumentNullException("requestContext");
            }
            if (String.IsNullOrEmpty(controllerName) &&
                (requestContext.RouteData == null || !requestContext.RouteData.HasDirectRouteMatch()))
            {
                throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
            }
            RouteData routeData = requestContext.RouteData;
            if (routeData != null && routeData.HasDirectRouteMatch())
            {
                return GetControllerTypeFromDirectRoute(routeData);
            }
            // first search in the current route's namespace collection
            object routeNamespacesObj;
            Type match;
            if (routeData.DataTokens.TryGetValue(RouteDataTokenKeys.Namespaces, out routeNamespacesObj))
            {
                IEnumerable<string> routeNamespaces = routeNamespacesObj as IEnumerable<string>;
                if (routeNamespaces != null && routeNamespaces.Any())
                {
                    HashSet<string> namespaceHash = new HashSet<string>(routeNamespaces, StringComparer.OrdinalIgnoreCase);
                    match = GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaceHash);
                    // the UseNamespaceFallback key might not exist, in which case its value is implicitly "true"
                    if (match != null || false.Equals(routeData.DataTokens[RouteDataTokenKeys.UseNamespaceFallback]))
                    {
                        // got a match or the route requested we stop looking
                        return match;
                    }
                }
            }
            // then search in the application's default namespace collection
            if (ControllerBuilder.DefaultNamespaces.Count > 0)
            {
                HashSet<string> namespaceDefaults = new HashSet<string>(ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);
                match = GetControllerTypeWithinNamespaces(routeData.Route, controllerName, namespaceDefaults);
                if (match != null)
                {
                    return match;
                }
            }
            // if all else fails, search every namespace
            return GetControllerTypeWithinNamespaces(routeData.Route, controllerName, null /* namespaces */);
        }

 

代码段 8

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

C# code

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 private Type GetControllerTypeWithinNamespaces(RouteBase route, string controllerName, HashSet<string> namespaces)
        {
            // Once the master list of controllers has been created we can quickly index into it
            ControllerTypeCache.EnsureInitialized(BuildManager);
            ICollection<Type> matchingTypes = 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
                    throw CreateAmbiguousControllerException(route, controllerName, matchingTypes);
            }
        }

 

代码段 9
xiaotmh
  • 版权声明: 本文源自 CSDN CKAOS, 于7个月前,由整理发表,共 8128字。
  • 原文链接:点此查看原文

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: