zoukankan      html  css  js  c++  java
  • 白话学习MVC(四)URL路由

    本节来记录下有关URL路由的知识。

    1、URL路由的是做什么的呢?  
      简单的说:URL路由的功能就是分析请求的URL地址,也就是你在浏览器地址栏输入的地址,它将你请求的地址和我们定义的模版进行比较匹配。通俗的说,就是发来请求的地址和我原来定义的样式是否一样,如果匹配的话就继续执行,例如:将请求的部分信息和当前匹配的路由对象Route(定义的模版)封装到RouteData、确定处理请求的HttpHandler,不明白没关系,之后介绍。

    2、URL路由是MVC特有的吗?
      URL路由系统并不是专属于ASP.NET MVC的,而是直接建立在ASP.NET上。ASP.NET通过路由系统可以实现请求地址和物理文件的分离,因为ASP.NET处理请求的是一个一个实际存在物理文件,例如:Default.aspx.cs。而MVC中处理请求的是某个Controller下的Action。

    3、URL路由在ASP.NET和ASP.NET MVC中使用是一样的吗?
      不一样。在ASP.NET中,使用RouteTable.Routes.MapPageRoute(...)方法(RouteCollection类中的方法)来进行注册路由(),之后由PageRouteHandler来生成一个处理ASP.NET请求的HttpHandler对象,即:.aspx.cs文件;在MVC中,使用RouteTable.Routes.MapRout(...)方法(RouteCollectionExtensions类中的方法)来进行注册路由,之后由MvcRouteHandler来生成一个处理MVC请求的HttpHandler对象MVCHandler。
      HttpHandler泛指那些实现了IHttpHandler接口的类

     当以上的这几个问题和问题的回答,你还有不明白的地方的话,没关系,接着看,看完下面的再回头看这上述的几个问题的。那时候就so easy ...

    进入正文:

      注册URL路由:

    在使用时,我们只需要在的Global.asax文件中注册响应的路由即可。以MVC为例,我们就来看注册一个路由。

            public static void RegisterRoutes(RouteCollection routes)
            {
                routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
                routes.MapRoute(
                    "Default", // 路由名称
                    "{controller}/{action}/{id}", // 带有参数的 URL
                    new { controller = "Home", action = "Index", id = UrlParameter.Optional } // 参数默认值
                );
            }

    MapRoute方法有好多重载,就是为了注册路由提供各种条件。例如:默认路由、指定命名空间、指定域等。

    注意:如果是ASP.NET程序则是利用RouteCollection对象的PageMapRoute方法来进行注册,在PageMapRoute方法中,将实例化一个Route对象并放入路由表中,当实例化Route对象时,其参数中会有一个RouteHandler(PageRouteHandler对象),这个对象的唯一方法GetHttpHandler方法会返回一个Page类(.aspx.cs文件)作为真正处理请求的类;而在MVC中,则是利用RouteCollection的扩展类RouteCollectionExtensions类的MapRoute方法来进行路由注册,跟ASP.NET不同的是,当在MVC中实例化Route对象的时,其参数中的RouteHandler是(MvcRouteHandler),这个对象的唯一方法GetHttpHandler方法返回的是个MVCHandler作为处理MVC请求的类。

    就是这么简单,合理的安排注册之后就可以实现对URL的路由。

    对于如何注册、以及如何安排路由的顺序等,这里也就不再介绍,网上已经有那么那么的多...

      对于本文来讲,了解如何使用URL路由不是目的,而分析URL路由的执行过程才是目的所在。

      分析执行过程

      接下来让我们来分析下执行流程,我们都知道所有发来的请求,都是通过HttpApplication的一系列事件来处理的。我们现在就以HttpApplication的各事件为主线,来分析URL路由的执行流程。

      当请求到达IIS后,经过IIS处理,然后到达程序并读取WebConfig文件中的HttpModule节点,对自定义的HttpModule进行注册,而URL路由系统是通过一个名为UrlRoutingModule的自定义HttpModule实现的,MVC中UrlRoutingModule是注册到HttpApplication的第7个事件PostResolveRequestCache事件中的,即:当执行HttpApplication到第7个事件的时候才执行URL路由的处理。


      之后开始一次执行HttpApplication的各个事件,首先执行Global.asax文件中的Application_Start方法,即:进行路由的注册,也就将定义的URL模版添加的路由表中。接着开始执行已经注册到HttpApplication各事件中的方法,URL路由注册在第7个事件中。其他事件的执行内容请这里

    HttpApplication事件列表
    BeginRequest
    AuthenticateRequest
    PostAuthenticateRequest
    AuthorizeRequest
    PostAuthorizeRequest
    ResolveRequestCache
    PostResolveRequestCache-----执行URL路由
    PostMapRequestHandler
    AcquireRequestState
    PostAcquireRequestState
    PreRequestHandlerExecute
    PostRequestHandlerExecute
    ReleaseRequestState
    PostReleaseRequestState
    UpdateRequestCache
    PostUpdateRequestCache
    LogRequest
    PostLogRequest
    EndRequest

    在UrlRoutingModule中,上述的注册的时候执行的是其Init方法,就是将OnApplicationPostResolveRequestCache方法和OnApplicationPostMapRequestHandler注册到事件中,当事件触发的时候,就会去执行对应的方法。

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

    执行OnApplicationPostResolveRequestCache
      在此方法以及其内部调用的方法中所要完成的工作就是:遍历所有已经注册的所有路由(URL模版),并遍历的调用每个路由对象的GetRouteData方法,对请求的URL和路由进行匹配,如何失败,则返回null,如果匹配成功则返回一个RouteData对象(封装了当前路由信息),RouteData通过自己的一个属性RouteHandler用来获取注册路由时的RouteModule(PageRouteModule或MvcRouteModule),这个RouteHandler对象是在注册路由中实例化Route对象的时候创建的,如果是ASP.NET程序(即:用PageMapRoute方法注册路由),则是PageRouteModule,如果是MVC程序(即:用MapRoute方法注册路由),则是MvcRouteModule。当获取到RouteModule后,要执行相应的唯一方法GetHttphandler方法,最终返回实际处理请求的HttpHandler(.aspx.cs文件或者MVCHandler)。
    注意:所有提到的HttpHandler泛指实现了IHttpHandler接口的类。
                       HttpModule泛指实现了IHttpModule接口的类。
            RouteHandler指的实现了IRouteHandler的MvcRouteHandler类或PageRouteHandler类。

    下面通过反编译来看下代码的执行:
    private void OnApplicationPostResolveRequestCache(object sender, EventArgs e)
    {
        HttpContextBase context
    = new HttpContextWrapper(((HttpApplication) sender).Context);
       
    this.PostResolveRequestCache(context);
    }
    public virtual void PostResolveRequestCache(HttpContextBase context)
    {
      //这里只列举重要的代码
      //遍历路由表中的所有路由,并遍历的调用每个路由的GetRouteData方法,并返回一个封装了当前匹配的路由信息的RouteData对象

        RouteData routeData
    = this.RouteCollection.GetRouteData(context);
      //用来获取处理路由请求的RouteHandler,上面说过了如果是MVC程序则得到是MvcRouteHandler对象,否则是PageRouteHandler

      IRouteHandler routeHandler = routeData.RouteHandler;
      //调用RoutHandler类的唯一方法GetHttpHandler,返回真正处理请求的HttpHandler(.aspx.cs文件或MvcHandler)
        IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
    }
    详细流程可以看看这里

      至此,URL路由系统的功能算是执行完了,他的作用也基本上体现完了。即:添加到HttpApplication的第7个事件中的功能也就收工了,接下来就会依次执行HttpApplicaiton的其他事件。之后就是在HttpApplication的第11-12个事件中,由URL路由系统的到HttpHandler来对请求进行处理!!!

    路由系统已经讲述完毕,为了了解其运行,我觉着再来看看在URL路由系统中重要的类是很有必要的!!
    RouteBase
    Route
    RouteData
    RouteCollection
    RouteCollectionExtensions


    RouteBase是一个抽象类,其中只有两个抽象方法。

     public abstract RouteData GetRouteData(HttpContextBase httpContext);
    public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);


    Route 就是上述提到的定义URL模版,向路由表中注册的路由就是一个Route对象。
        Route类唯一继承并实现RouteBase类,也就是要实现RouteBase中的两个抽象方法,GetRouteData(...)方法就是将请求的URL地址和当前模版匹配,如果成功的话,返回一个封装当前路由信息的RoutData对象。GetVirturlPath(...)方法是生成一个URL,暂时用不到,就不多介绍GetVirturlPath了。
    成员:

        public class Route : RouteBase
        {
    
            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 RouteValueDictionary Constraints { get; set; } public RouteValueDictionary DataTokens { get; set; } public RouteValueDictionary Defaults { get; set; } public IRouteHandler RouteHandler { get; set; } public string Url { get; set; }
            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);
        }
    }

    RouteData 是一个封装路由信息的对象,当路由匹配成功之后,就是由RoueData对象来获取ModuleHandler(PageModuleHandler、MVCModuleHandler),再由这个ModuleHandler来获取处理请求的HttpHandler。
    成员:

    public class RouteData
        {
            public RouteData();
            public RouteData(RouteBase route, IRouteHandler routeHandler);
            public RouteValueDictionary DataTokens { get; }
            public RouteBase Route { get; set; }
            public IRouteHandler RouteHandler { get; set; }
            public RouteValueDictionary Values { get; }
            public string GetRequiredString(string valueName);
        }

    RouteCollecitonASP.NET 路由操作提供路由的集合,利用其PageMapRoute方法来进行路由注册。
    RouteCollectionExtensions 扩展RouteCollection对象,进行ASP.NET MVC路由,MVC要利用其MapRoute方法来进行路由注册。

    RouteCollection
        public class RouteCollection : Collection<RouteBase>
        {
              public RouteCollection(VirtualPathProvider virtualPathProvider);
            public bool RouteExistingFiles { get; set; }
            public RouteBase this[string name] { get; }
            public void Add(string name, RouteBase item);
            protected override void ClearItems();
            public IDisposable GetReadLock();
            public RouteData GetRouteData(HttpContextBase httpContext);
            public VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
            public VirtualPathData GetVirtualPath(RequestContext requestContext, string name, RouteValueDictionary values);
            public IDisposable GetWriteLock();
            [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
               public void Ignore(string url);
            public void Ignore(string url, object constraints);
            protected override void InsertItem(int index, RouteBase item);
            [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
                public Route MapPageRoute(string routeName, string routeUrl, string physicalFile);
            [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
            public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess);
            [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
            public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess, RouteValueDictionary defaults);
            [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
            public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess, RouteValueDictionary defaults, RouteValueDictionary constraints);
            public Route MapPageRoute(string routeName, string routeUrl, string physicalFile, bool checkPhysicalUrlAccess, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens);
            protected override void SetItem(int index, RouteBase item);
        }
    RouteCollectionExtensions
        public static class RouteCollectionExtensions
        {
            public static VirtualPathData GetVirtualPathForArea(this RouteCollection routes, RequestContext requestContext, RouteValueDictionary values);
            public static VirtualPathData GetVirtualPathForArea(this RouteCollection routes, RequestContext requestContext, string name, RouteValueDictionary values);
            public static void IgnoreRoute(this RouteCollection routes, string url);
            public static void IgnoreRoute(this RouteCollection routes, string url, object constraints);
            public static Route MapRoute(this RouteCollection routes, string name, string url);
            public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults);
            public static Route MapRoute(this RouteCollection routes, string name, string url, string[] namespaces);
            public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints);
            public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, string[] namespaces);
            public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints, string[] namespaces);
        }

     本篇只介绍了流程,暂没根据MVC的源代码分析路由系统!!

  • 相关阅读:
    KMP算法代码实现记录
    冒泡,插入,希尔,快速,归并,桶排序,堆排序算法汇总实现
    回溯法个人理解记录(C#八皇后)
    C#创建初始化链表的方式(个人目前写出3种创建的方式)
    算法汇总代表性学习记录
    C#集合去重
    C#获取数组/字符串的k个字符的全部组合
    pl/sql简单执行记录个人学习记录
    oracle为什么尽量不要使用外键的最好理解
    PickerController 添加照片---iOS
  • 原文地址:https://www.cnblogs.com/wupeiqi/p/3069905.html
Copyright © 2011-2022 走看看