白话学习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的源代码分析路由系统!!

 

 

 

 

posted @ 2013-05-09 20:42  武沛齐  阅读(954)  评论(0编辑  收藏  举报