MVC系统学习1—MVC执行流程

      用MVC来做开发也有一段时间了,但是感觉一直没入门,就徘徊在似懂非懂的层次,和去年刚毕业学习WebForm时一样,当时通过张子阳老兄的几篇文章,明白了请求处理流程,页面生命周期才真正明白了WebForm的强大。由于MVC的学习资料比较少,牛人的技术博客也只是讲一些基础的而已。因此决定通过Asp.Net MVC源码来学习,由于是开源的,也不用Reflector作为辅助工具。首先还是明白下MVC的请求处理流程。有参考了MSDN上面的文章(http://msdn.microsoft.com/zh-cn/library/dd381612.aspx)

      当应用程序第一次接受请求的时候,在Global.asax文件中,Route对象会添加到RouteTable对象中。RegisterRoutes函数就是我们实现的路由注册函数。

protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            RegisterRoutes(RouteTable.Routes);
        }
  public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                "Default",
                "{controller}/{action}/{id}",
                new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }

      通过MVC源码在了解上面这一步的大致实现过程 。在RegisterRoutes函数中,通过调用MapRoute(扩展方法)来实现向RouteCollection添加Route对象。最后调用的是下面的方法。

View Code
1 //TODO:添加路由的方法
2   public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces) {
3 if (routes == null) {
4 throw new ArgumentNullException("routes");
5 }
6 if (url == null) {
7 throw new ArgumentNullException("url");
8 }
9
10 Route route = new Route(url, new MvcRouteHandler()) {
11 Defaults = new RouteValueDictionary(defaults),
12 Constraints = new RouteValueDictionary(constraints),
13 DataTokens = new RouteValueDictionary()
14 };
15
16 if ((namespaces != null) && (namespaces.Length > 0)) {
17 route.DataTokens["Namespaces"] = namespaces;
18 }
19
20 routes.Add(name, route);
21
22 return route;
23 }
      可以发现上面的方法实现创建一个Route对像,并将其添加到RouteCollection,然后将其返回。接下来了解下Route类。Route继承自RouteBase类.其有几个比较重要的属性。

       上面这前三个成员的类型是RouteValueDictionary,而上面的函数中,是调用下面的这个函数来实现向Route的Defaults等添加数据。如注释,就是将controller,action,id添加到Defaults的键中,而将Home,Index加入到相应的键对应的值。而对于RouteHandler的作用等下再讲。这样就完成了一个Route对象的创建并且添加到RouteCollection中,而这个RouteCollection也就是RouteTable的成员Routes.

 private void AddValues(object values)
        {
            if (values != null)
            {
                //这里分解出 new { controller = "Home", action = "Index", id = UrlParameter.Optional }
                //然后填充到字典里面
                foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(values))
                {
                    object obj2 = descriptor.GetValue(values);
                    this.Add(descriptor.Name, obj2);
                }
            }
        }

      在Asp.Net管道中执行MVC请求的HttpModule是UrlRoutingModule模块。UrlRoutingModule使用RouteTable 集合中第一个匹配的 Route 对象来创建 RouteData 对象,然后使用所创建的对象创建 RequestContext 对象。UrlRoutingModule里面有一个RouteCollection属性,其就是通过RouteTable.Routes来实现赋值的。

public System.Web.Routing.RouteCollection RouteCollection
        {
            get
            {
                //TODO:使用RouteTable的Routes成员来初始化routeCollection
                if (this._routeCollection == null)
                {
                    this._routeCollection = RouteTable.Routes;
                }
                return this._routeCollection;
            }
            set
            {
                this._routeCollection = value;
            }
        }

       在UrlRoutingModule中,注册了两个HttpApplication公开的事件。其中是在OnApplicationPostResolveRequestCache方法里面实现路由匹配,因为其里面又调用了一个this.PostResolveRequestCache(context)方法,而在这个方法里面的第一句就是 RouteData routeData = this.RouteCollection.GetRouteData(context)。根据请求上下文来获取RouteData。路由匹配的奥秘貌似就在这里。

 protected virtual void Init(HttpApplication application)
        {
            application.PostResolveRequestCache += new EventHandler(this.OnApplicationPostResolveRequestCache);
            application.PostMapRequestHandler += new EventHandler(this.OnApplicationPostMapRequestHandler);
        }

        this.RouteCollection.GetRouteData(context)方法里面最后又这么几句代码。通过遍历当前的RouteCollection来实现RouteData的获取,如果获取到则立即返回。这里其实遍历的单个对象不是RouteBase而是Route,而Route继承自RouteBase类,并且重写了RouteBase的抽象函数GetRouteData(为什么这么做,待研究?)。而路由匹配的实现也是在Route类里重写的GetRouteData方法。

 foreach (RouteBase base2 in this)
                {
                    RouteData routeData = base2.GetRouteData(httpContext);
                    if (routeData != null)
                    {
                        return routeData;
                    }
                }

     Route的GetRouteData方法

View Code
1 public override RouteData GetRouteData(HttpContextBase httpContext)
2 {
3 //获取请求的虚拟路径
4   string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
5 //TODO:接下来进行路径的匹配
6   RouteValueDictionary values = this._parsedRoute.Match(virtualPath, this.Defaults);
7 if (values == null)
8 {
9 return null;
10 }
11 RouteData data = new RouteData(this, this.RouteHandler);
12 //如果不通过约束则返回空
13   if (!this.ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest))
14 {
15 return null;
16 }
17 foreach (KeyValuePair<string, object> pair in values)
18 {
19 data.Values.Add(pair.Key, pair.Value);
20 }
21 if (this.DataTokens != null)
22 {
23 foreach (KeyValuePair<string, object> pair2 in this.DataTokens)
24 {
25 data.DataTokens[pair2.Key] = pair2.Value;
26 }
27 }
28 return data;
29 }

     最后是在这个方法下面调用ParsedRoute的Match进行路由匹配。

posted @ 2011-03-29 16:01  雁北飞  阅读(555)  评论(0编辑  收藏  举报