博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
MVC路由解析---MapRoute
阅读量:5068 次
发布时间:2019-06-12

本文共 8985 字,大约阅读时间需要 29 分钟。

文章引导

               

               

                

             

引言

               前面我们讲了IgnoreRoute

               现在我们讲讲核心的MapRoute,还是提前准备Reflection工具,若是没准备,可以看“”MVC路由深入详解1---IgnoreRoute”中的System.Web.dll源码

一.RouteCollection

               我们来看看RouteCollection.MapRoute,截图如下:

                        

               相信大家看到了RouteCollectionExtensions是一个静态类,是对RouteCollection的扩展(关于扩展方法的大家可以百度,此处不做详细描述)。好家伙,我们看看这个扩展方法走向何方(这个时候就是Reflection发挥作用的时候了)。

               引用“”MVC路由深入详解1---IgnoreRoute”中的内容,看看MapRoute的参数传入的是什么:

                              name:       "Default"

                              url:           "{controller}/{action}/{id}"

                              defaults:   new { controller = "Home", action = "Index", id = UrlParameter.Optional }  --->这是个匿名类型

 

               我们看看扩展方法去了哪里:

                              public static (this routes, name, url, defaults) => routes.(name, url, defaults, null); 

               我们接着往下走       

                              public static (this routes, name, url, defaults, constraints) =>routes.(name, url, defaults, constraints, null);

               接着

public Route MapRoute(string name,string url,object defaults,object constraints=null,string[] param=null)        {            if(url==null)            {                throw new ArgumentNullException("url");            }            Route route = new Route(url, new MvcRouteHandler())            {                Defaults=CreateRouteValueDictionaryUncached(defaults),                Constraints=CreateRouteValueDictionaryUncached(constraints),                DataTokens=new System.Web.Routing.RouteValueDictionary()            };            ConstraintValidation.Validate(route);            if ((param != null) && (param.Length > 0))            {                route.DataTokens["Namespaces"] = param;            }            Add(name, route);            return route;        }

                上面新建了一个Route,Route就是一条具体的路由 ,new Route(url, new MvcRouteHandler())传入规则url和new MvcRouteHandler()。

二.CreateRouteValueDictionaryUncached      

private static System.Web.Routing.RouteValueDictionary CreateRouteValueDictionaryUncached(object values)        {            IDictionary
dictionary = values as IDictionary
; if (dictionary != null) { return new System.Web.Routing.RouteValueDictionary(dictionary); } return System.Web.WebPages.TypeHelper.ObjectToDictionaryUncached(values); }          

三.MvcRouteHandler

               我们继续拆解MvcRouteHandler

public class MvcRouteHandler: System.Web.Routing.IRouteHandler    {        System.Web.Mvc.IControllerFactory _controllerFactory;        public MvcRouteHandler() { }        public MvcRouteHandler(System.Web.Mvc.IControllerFactory controllFactory)        {            _controllerFactory = controllFactory;        }        protected virtual IHttpHandler GetHttpHandler(System.Web.Routing.RequestContext requestContext)        {            //SetSessionStateBehavior:在派生类重写时,设置支持HTTP请求所必须的会话状态行为的类型 requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext)); return new MvcHandler(requestContext); } IHttpHandler System.Web.Routing.IRouteHandler.GetHttpHandler(System.Web.Routing.RequestContext requestContext) { return GetHttpHandler(requestContext); } protected virtual System.Web.SessionState.SessionStateBehavior GetSessionStateBehavior(System.Web.Routing.RequestContext requestContext) { string str = (string)requestContext.RouteData.Values["controller"]; if (string.IsNullOrEmpty(str)) { throw new InvalidOperationException(System.Web.Mvc.Properties.MvcResources.MvcRouteHandler_RouteValuesHasNoController); } IControllerFactory factory = _controllerFactory ?? ControllerBuilder.Current.GetControllerFactory(); return factory.GetControllerSessionBehavior(requestContext, str); } }

四.RouteValueDictionary

             RouteValueDictionary是对Dictionary<string,object>进行包装,下面是RouteValueDictionary拆解   

public RouteValueDictionary(object values){    this._dictionary = new Dictionary
(StringComparer.OrdinalIgnoreCase); this.AddValues(values);}private void AddValues(object values){ if (values != null) { foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(values)) { object obj2 = descriptor.GetValue(values); this.Add(descriptor.Name, obj2); } }}

             我们来看看效果

                

五.Add(name,route)

             我们来看看这个Add方法:   

public void Add(string name, RouteBase item)        {            if (item == null)            {                throw new ArgumentNullException("item");            }            if (!string.IsNullOrEmpty(name) && this._namedMap.ContainsKey(name))            {                object[] args = new object[] { name };                //throw new ArgumentException(string.Format(CultureInfo.CurrentUICulture, System.Web.SR.GetString("RouteCollection_DuplicateName"), args), "name");            }            base.Add(item);            if (!string.IsNullOrEmpty(name))            {                this._namedMap[name] = item;            }        }

 

             里面做了重复路由名称验证,Add方法的第二个参数是RouteBase,我们看看MapRoute方法里传入给Add方法的参数是Route。Route是继承于RouteBase,RouteBase是一个抽象类,这个类是为继承类服务的,里面定义了GetRouteData和GetVirtualPath两个抽象方法。

             GetRouteData:解析请求url,提取数据,如:/home/index 得到:controller/home,action/index提取得到的数据会包装成RouteData

             GetVirtualPath:生成URL

六.RouteBase Route    

public abstract class RouteBase{    // Methods    protected RouteBase();    public abstract RouteData GetRouteData(HttpContextBase httpContext);    public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);}

 

public class Route : RouteBase{    // Fields    private ParsedRoute _parsedRoute;    private string _url;    private const string HttpMethodParameterName = "httpMethod";    // Methods    public Route(string url, IRouteHandler routeHandler);    public Route(string url, RouteValueDictionary defaults, IRouteHandler routeHandler);    public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler);    public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler);    public override RouteData GetRouteData(HttpContextBase httpContext);    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);    protected virtual bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection routeDirection);    private bool ProcessConstraints(HttpContextBase httpContext, RouteValueDictionary values, RouteDirection routeDirection);    // Properties    public RouteValueDictionary Constraints { get; set; }    public RouteValueDictionary DataTokens { get; set; }    public RouteValueDictionary Defaults { get; set; }    public IRouteHandler RouteHandler { get; set; }    public string Url { get; set; }}

 

              Route添加了几个属性

                          Constraints:保存约束规则,最终保存为RouteValueDictionary

                          DataTokens:附加参数,指定controller的空间命名也放在这里。

                          Defaults:保存规则的默认值

                          url:规则URL

             我们来看看GetRouteData,我们看看RouteData routeData=RouteCollection.GetRouteData(context)。

public RouteData GetRouteData(HttpContextBase httpContext){    if (httpContext == null)    {        throw new ArgumentNullException("httpContext");    }    if (httpContext.Request == null)    {        throw new ArgumentException(RoutingResources.RouteTable_ContextMissingRequest, "httpContext");    }    if (!this.RouteExistingFiles)    {        string appRelativeCurrentExecutionFilePath = httpContext.Request.AppRelativeCurrentExecutionFilePath;        if (((appRelativeCurrentExecutionFilePath != "~/") && (this._vpp != null)) && (this._vpp.FileExists(appRelativeCurrentExecutionFilePath) || this._vpp.DirectoryExists(appRelativeCurrentExecutionFilePath)))        {            return null;        }    }    using (this.GetReadLock())    {        foreach (RouteBase base2 in this)        {            RouteData routeData = base2.GetRouteData(httpContext);            if (routeData != null)            {                return routeData;            }        }    }    return null;}

 

             上述代码中最后通过递归遍历自己所有的路由规则,分别调用我们所有注册在RouteTable.Routes--->RouteCollection。

             

             我们还注意到上面有这么一段: 

if (!this.RouteExistingFiles)
    {
        string appRelativeCurrentExecutionFilePath = httpContext.Request.AppRelativeCurrentExecutionFilePath;
        if (((appRelativeCurrentExecutionFilePath != "~/") && (this._vpp != null)) && (this._vpp.FileExists(appRelativeCurrentExecutionFilePath) || this._vpp.DirectoryExists(appRelativeCurrentExecutionFilePath)))
        {
            return null;
        }
    }

             RouteCollection 有这么一个属性RouteExistingFiles.当为false时,就检测请求的路径地址是否己经存在文件或目录,如果存在,则直接不走路由了,直接返回null,默认就是false,我们可以实验一下。当然这里是忽略了根目录的,不然默认我们 http://www.xxx.com/ 也不能访问了。

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
                new string[] { "MVCTest.Controllers" }
            );
        }

             这里是默认的路由注册,按理说我们访问 home 时,会去到 home controller 的 index,但是我们在在项目里加一个 home 目录,如下图。

             我们再访问: 我们发现,无法找到该资源,也就是检测到home这个目录存在时,就不走路由了。

 

            为尊重原创,本文的编写参考了以下博文和文章:

                       程序园: http://www.cnblogs.com/lindaohui/archive/2012/08/31/2664047.html

 

 

 

 

转载于:https://www.cnblogs.com/xiaowangzi1987/p/9225206.html

你可能感兴趣的文章
2014025630《嵌入式程序设计》第一周学习总结
查看>>
java.lang.OutOfMemoryError: Java heap space的解决方法
查看>>
SQL Server开发的二十一条军规
查看>>
HTML layout高仿QQ GUI
查看>>
软工课总结
查看>>
build.gradle 中compileSdkVersion,minSdkVersion,targetSdkVersion,buildToolsVersion的意思
查看>>
安装docker
查看>>
Jmeter VS LR参数取值方式和迭代方式
查看>>
Android 媒体键监听以及模拟媒体键盘的实现 demo
查看>>
面试题收集-腾讯
查看>>
【2019/5/18】周进度报告
查看>>
获取随机数
查看>>
block
查看>>
plsql 输出当月的所有日期
查看>>
[学习笔记]分块
查看>>
Visual Studio 2017 ASP.NET Core开发
查看>>
java多线程学习(两)——创建一个线程
查看>>
VirtualBox安装及使用说明和虚拟机安装XP系统图文教程
查看>>
DropdownList绑定的两种方法
查看>>
Oracle Enterprise Linux 64-bit下安装apache-tomcat-7.0.53步骤
查看>>