ASP.NET MVC 请求流程:Route

1.RouteTable

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

  RouteTable类十分的简单,以下所示this

    public class RouteTable
    {
        private static RouteCollection _instance = new RouteCollection();

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

  如今咱们来看一下运行时的状态url

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

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

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类吧3d

3.RouteBase,Route

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

  RouteBase

  RouteBase是Route类的父类,咱们仍是来看下它的类结构吧orm

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类后,咱们的代码继续向下走blog

    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个类关系图

相关文章
相关标签/搜索