zoukankan      html  css  js  c++  java
  • ASP.NET MVC 请求流程:Route

    1.RouteTable

      RouteTable翻译过来的意思就是路由表,一个Web应用程序具有一个全局的路由表,该路由表通过System.Web.Routiing.RouteTable的静态只读属性Routes表示,该类型返回一个类型为System.Web.Routingg.RouteCollection的集合。

      RouteTable类十分的简单,如下所示

        public class RouteTable
        {
            private static RouteCollection _instance = new RouteCollection();
    
            //返回一个静态只读的RouteCollection类型实例
            public static RouteCollection Routes
            {
                get
                {
                    return RouteTable._instance;
                }
            }
            public RouteTable()
            {
            }
        }

      现在我们来看一下运行时的状态

        public class MvcApplication : System.Web.HttpApplication
        {
            protected void Application_Start()
            {
                AreaRegistration.RegisterAllAreas();
                RouteConfig.RegisterRoutes(RouteTable.Routes); //断点1
            }
        }

      下图就是当断点处于断点1时,RouteTable的状态 ,我们在这里可以很清楚的看到现在Routes属性所包含的路由条数为0,不急我们继续向下走。

    2.RouteCollection

      看到名称就不难猜到,这个应该是表示路由集合,我们先来看看这个类里面有什么新奇玩意。

    public class RouteCollection : Collection<RouteBase>
        {
            //其余省略
    
            //是否添加首尾斜杠.默认值为 false.
            public bool AppendTrailingSlash { get; set; }
            //是否将 URL 转换为小写.默认值为 false.
            public bool LowercaseUrls { get; set; }
            //是否应处理与现有文件匹配的 URL.默认值为 false.
            public bool RouteExistingFiles { get; set; }
            //获取路由信息
            public RouteData GetRouteData(HttpContextBase httpContext);
            //获取虚拟路径信息
            public VirtualPathData GetVirtualPath(RequestContext requestContext, string name, RouteValueDictionary values);
            //忽略路由URL和相关约束
            public void Ignore(string url, object constraints);
            //添加路由
            public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens);
    
        }

      稍微了解了这个类是用来干什么的,那么我们就要接着上面的程序向下走了,当然先介绍以下RouteBase和Route类吧

    3.RouteBase,Route

      在上图中我们看到了RouteBase,Route类,来说一下它们是什么吧。

      RouteBase

      RouteBase是Route类的父类,我们还是来看下它的类结构吧

    public abstract class RouteBase
        {
            private bool _routeExistingFiles = true;
    
            // 指示 ASP.NET 路由操作是否应处理与物理文件匹配的 URL,这里默认是True,即可以使用WebForm方式请求物理文件,但是在MSDN中描述
            //这个属性的默认值为False
            public bool RouteExistingFiles
            {
                get
                {
                    return this._routeExistingFiles;
                }
                set
                {
                    this._routeExistingFiles = value;
                }
            }
    
            // 获取路由信息,保存在RouteData中
            public abstract RouteData GetRouteData(HttpContextBase httpContext);
    
            // 获取虚拟路径信息,保存在VirtualPathData中
            public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
        }
    View Code

      RouteURL模版模式的路由匹配规则就定义在Route中,看下类结构吧

    public class Route : RouteBase
        {
            // 省略代码.....
            private string _url;
            private ParsedRoute _parsedRoute;
            private const string HttpMethodParameterName = "httpMethod";
            // 存储路由约束
            public RouteValueDictionary Constraints
            {
                get;
                set;
            }
            // 存储额外变量,但不会参与针对请求地址的匹配工作,比如Namespaces
            public RouteValueDictionary DataTokens
            {
                get;
                set;
            }
            // 存储为路由变量定义的默认值
            public RouteValueDictionary Defaults
            {
                get;
                set;
            }
            // 路由处理对象
            public IRouteHandler RouteHandler
            {
                get;
                set;
            }
            // URL模版
            public string Url
            {
                get
                {
                    return this._url ?? string.Empty;
                }
                set
                {
                    this._parsedRoute = RouteParser.Parse(value);
                    this._url = value;
                }
            }
            public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler)
            {
                this.Url = url;
                this.Defaults = defaults;
                this.Constraints = constraints;
                this.DataTokens = dataTokens;
                this.RouteHandler = routeHandler;
            }
            // 重写父类方法
            public override RouteData GetRouteData(HttpContextBase httpContext)
            {
                string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) + httpContext.Request.PathInfo;
                RouteValueDictionary routeValueDictionary = this._parsedRoute.Match(virtualPath, this.Defaults);
                if (routeValueDictionary == null)
                {
                    return null;
                }
                RouteData routeData = new RouteData(this, this.RouteHandler);
                if (!this.ProcessConstraints(httpContext, routeValueDictionary, RouteDirection.IncomingRequest))
                {
                    return null;
                }
                foreach (KeyValuePair<string, object> current in routeValueDictionary)
                {
                    routeData.Values.Add(current.Key, current.Value);
                }
                if (this.DataTokens != null)
                {
                    foreach (KeyValuePair<string, object> current2 in this.DataTokens)
                    {
                        routeData.DataTokens[current2.Key] = current2.Value;
                    }
                }
                return routeData;
            }
            // 重写父类方法
            public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
            {
                BoundUrl boundUrl = this._parsedRoute.Bind(requestContext.RouteData.Values, values, this.Defaults, this.Constraints);
                if (boundUrl == null)
                {
                    return null;
                }
                if (!this.ProcessConstraints(requestContext.HttpContext, boundUrl.Values, RouteDirection.UrlGeneration))
                {
                    return null;
                }
                VirtualPathData virtualPathData = new VirtualPathData(this, boundUrl.Url);
                if (this.DataTokens != null)
                {
                    foreach (KeyValuePair<string, object> current in this.DataTokens)
                    {
                        virtualPathData.DataTokens[current.Key] = current.Value;
                    }
                }
                return virtualPathData;
            }
            // 验证参数值是否与该参数的约束匹配
            protected virtual bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
            {
                IRouteConstraint routeConstraint = constraint as IRouteConstraint;
                if (routeConstraint != null)
                {
                    return routeConstraint.Match(httpContext, this, parameterName, values, routeDirection);
                }
                string text = constraint as string;
                if (text == null)
                {
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("Route_ValidationMustBeStringOrCustomConstraint"), new object[]
                    {
                        parameterName,
                        this.Url
                    }));
                }
                object value;
                values.TryGetValue(parameterName, out value);
                string input = Convert.ToString(value, CultureInfo.InvariantCulture);
                string pattern = "^(" + text + ")$";
                return Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
            }
        }
    View Code

      介绍完RouteBase和Route类后,我们的代码继续向下走

        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
                constraints: new { controller = "^H.*" },
                namespaces: new[] { "SimpleMVC" }
            );
        }

      看上面一段代码,我们发现RouteCollection实例有两个方法,但是System.Web.Routing.RouteCollection类中并没有这两个方法,那这个怎么实现的呢?

      我们在IgnoreRoute上转到定义看下,发现我们跳转到了System.Web.Mvc.RouteCollectionExtensions这个路由集合扩展类了,在看下这个方法

        public static void IgnoreRoute(this RouteCollection routes, string url)
        {
            routes.IgnoreRoute(url, null);
        }

      一看恍然大悟,原来是通过扩展方法,感叹下扩展方法原来是可以这么用的。

      好了,那么routes.MapRoute也肯定是通过扩展方法注入的。那我们就看下route.MaoRoute是实现的。

        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");
            }
            // MvcRouteHandler 是请求进入时使用MVC路由关键
            Route route = new Route(url, new MvcRouteHandler())
            {
                // 存储为路由变量定义的默认值
                Defaults = RouteCollectionExtensions.CreateRouteValueDictionaryUncached(defaults),
                // 存储路由约束
                Constraints = RouteCollectionExtensions.CreateRouteValueDictionaryUncached(constraints),
                // 存储额外变量,但不会参与针对请求地址的匹配工作,比如Namespaces
                DataTokens = new RouteValueDictionary()
            };
            ConstraintValidation.Validate(route);
            if (namespaces != null && namespaces.Length > 0)
            {
               route.DataTokens["Namespaces"] = namespaces;
            }
            // 向RouteCollection中添加路由
            routes.Add(name, route);
            // 返回该路由
            return route;
        }

      好了,我们大概已经了解这两个扩展方法的作用了,下面我们来看看它们在运行时的状态

        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
                constraints: new { controller = "^H.*" },
                namespaces: new[] { "SimpleMVC" }
            );// 断点2
        }

      我们看看当断点停留在断点2处时,类里面的状态是怎样的?如下图

      我们可以很清楚的看到RouteCollection实例包含两条由规则,这两条路由规则都是继承自System.Web.Routing.RouteBase,第一条是我们定义为忽略的路由,类型是System.Web.Mvc.RouteCollectionExtensions.IgnoreRouteInternal,该类型继承子System.Web.Routing.Route,第二条则是我们定义的有效的路由,类型是System.Web.Routing.Route。

      我们在深入看下第二条有效的路由信息

      通过上图,可以非常明显的看出,哪些数据存储到了哪些属性里面,可以有个直观的理解。

    4.关系

      好了,我们的程序需要继续向下走,执行完RegisterRoutes方法后,我们又回到了Application_Start方法。

            protected void Application_Start()
            {
                AreaRegistration.RegisterAllAreas();
                RouteConfig.RegisterRoutes(RouteTable.Routes); // 断点1
            } // 断点3

      我们在断点3出看下各类的状态,总结下RouteTable,RouteCollection,RouteBase,Route4个类之间的关系,如下图所示

      下面是一张RouteTable,RouteCollection,RouteBase,Route4个类关系图

      

      

  • 相关阅读:
    菜农大叔抢楼
    实验室博客
    VS2008加入QT
    9G关于新唐M0的ISP的要点
    内部函数和外部函数
    51串口通信
    一个三位整数反向后输出
    C++重载函数定义和用法
    博客记录
    C语言练习笔记更新
  • 原文地址:https://www.cnblogs.com/zxj159/p/4112606.html
Copyright © 2011-2022 走看看