zoukankan      html  css  js  c++  java
  • MVC路由解析---MapRoute

    文章引导

                  MVC路由解析---IgnoreRoute 

                  MVC路由解析---MapRoute 

                  MVC路由解析---UrlRoutingModule  

                  Area的使用

    引言

                   前面我们讲了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 Route MapRoute(this RouteCollection routes, string name, string url, object defaults) => routes.MapRoute(name, url, defaults, null); 

                   我们接着往下走       

                                  public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints) =>routes.MapRoute(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<string, object> dictionary = values as IDictionary<string, object>;
                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<string, object>(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 目录,如下图。

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

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

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

     

     

  • 相关阅读:
    三层框架(原始版)
    Java虚拟机之内存区域
    JDK和JRE的区别
    cookie和session区别与联系
    DAO、Service、Controller及View层级结构梳理
    JavaWeb-四大域对象复习
    Mybatis-实现逆向代理
    Springboot-实现热部署
    排序算法-冒泡排序
    【ERROR 1064 (42000)】MySQL中使用mysqladmin或set修改root密码时提示语法错误
  • 原文地址:https://www.cnblogs.com/xiaowangzi1987/p/9225206.html
Copyright © 2011-2022 走看看