白话学习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); }
RouteColleciton 为 ASP.NET 路由操作提供路由的集合,利用其PageMapRoute方法来进行路由注册。
RouteCollectionExtensions 扩展RouteCollection对象,进行ASP.NET MVC路由,MVC要利用其MapRoute方法来进行路由注册。
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); }
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的源代码分析路由系统!!