ASP.NET MVC - 路由系统
ASP.NET MVC的请求URL不再对应于传统ASP.NET程序的ASPX文件物理地址,而是把请求映射到一个控制器(Controller)类的方法(Action)上,Controller、Action再加上参数构成ASP.Net MVC请求的Url。下面我们来看下路由系统的主要对象。
UrlRoutingModule
ASP.NET MVC框架的路由实质是从传统ASP.NET管道扩展HttpModule而来,这个模块正是UrlRoutingModule。通过反编译可以看到UrlRoutingModule注册了HttpApplication对象的PostResolveRequestCache事件而最终调用PostResolveRequestCache(HttpContextBase context),就是在这个事件中ASP.Net MVC完成了路由的主要工作,首先RouteCollection 通过调用GetRouteData 方法依次调用集合中Route对象的GetRouteData方法,直到找到一个与Url相匹配的Route对象并返回RouteData,然后通过RouteData得到RouteHandler。在构造完RequestContext对象后调用RouteHandler的GetHttpHandler方法最终得到一个HttpHandler。下面列出了主要代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public virtual void PostResolveRequestCache(HttpContextBase context) { RouteData routeData = this .RouteCollection.GetRouteData(context); IRouteHandler routeHandler = routeData.RouteHandler; RequestContext requestContext = new RequestContext(context, routeData); context.Request.RequestContext = requestContext; IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); if (!(httpHandler is UrlAuthFailureHandler)) { context.RemapHandler(httpHandler); return ; } } |
Route
Route类继承于RouteBase,Constraints、DataTookens、Defaults三个属性分别保存了Url的约束,控制器的命名空间和URL默认值。RouteHandler属性默认是通过构造函数传入的MVCRouteHandler。GetRouteData(HttpContextBase httpContext)方法继承于RouteBase,如果URL匹配则返回RouteData对象。另外,ASP.NET MVC 5.0的Route对象多出了一个于RouteCollection的RouteExistingFiles同名属性,这两个对象中的属性协同控制是否对已存在文件使用路由。
1 2 3 4 5 6 7 8 9 10 11 12 |
public class Route: RouteBase { public RouteValueDictionary Constraints { get ; set ; } public RouteValueDictionary DataTokens { get ; set ; } public RouteValueDictionary Defaults { get ; set ; } public IRouteHandler RouteHandler { get ; set ; } 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) } |
RouteBase
RouteBase的两个抽象方法负责一进一出,GetRouteData方法用来从URL获取RouteData,GetVirtualPath方法则用在通过路由数据生成URL的场景。RouteData对象可以说是对RouteBase和RouteHandler的封装,它存储了路由所需的所有信息。
1 2 3 4 5 6 |
public abstract class RouteBase { public abstract RouteData GetRouteData(HttpContextBase httpContext); public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values); } |
RouteCollection
RouteCollection是RouteBase对象的集合,它做为RouteTable类的静态属性Routes而存在,在应用程序Application_Start事件中所有的路由将注册到这个集合中。RouteExistingFiles属性指示路由操作是否应处理与现有文件匹配的 URL,其默认值是false,这意味着ASP.NET MVC也可以与传统aspx页面集成工作,特别是正在从WebPage迁移到MVC的项目。我们打开一个ASP.NET MVC 的示例项目可以看到路由是如何注册到路由表的。
1 2 3 4 5 6 7 8 9 10 |
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute( "{resource}.axd/{*pathInfo}" ); routes.MapRoute( name: "Default" , url: "{controller}/{action}/{id}" , defaults: new { controller = "Home" , action = "Index" , id = UrlParameter.Optional } ); } |
在RouteCollectionExtensions类中MapRoute方法有多个重载,下面列出最后一个方法的主要代码,我们可以看到Route对象是如何被构造的并且知道Route类的各个属性存储了什么。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public static class RouteCollectionExtensions { 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, object defaults, object constraints) 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, string [] namespaces) public static Route MapRoute( this RouteCollection routes, string name, string url, object defaults, object constraints, string [] namespaces) { Route route = new Route(url, new MvcRouteHandler()) { Defaults = RouteCollectionExtensions.CreateRouteValueDictionary(defaults), Constraints = RouteCollectionExtensions.CreateRouteValueDictionary(constraints), DataTokens = new RouteValueDictionary() }; if (namespaces != null && namespaces.Length > 0) { route.DataTokens[ "Namespaces" ] = namespaces; } routes.Add(name, route); return route; } } |
IRouteConstraint
对URL约束我们通过RouteCollection类扩展方法MapRoute的参数constraints传入,以示例项目{controller}/{action}/{id}的URL为例,如果我们想要限制"id"只能为数字,那么我们会像下面这样传入constraints对象。
1 |
routes.MapRoute( "Product" , "Product/{productId}" , new {controller= "Product" , action= "Details" }, new {id = @"\d+" }); |
实际上我们不仅可以传入正则表达式也可以传入一个实现了IRouteConstraint接口的对象,这个接口有且只有唯一个Match方法,它的签名如下。parameterName对应了属性的名字,在里为"id",这个方法中我们可以拿到HttpContext、Route和RouteValueDictionary这几个重要对象。在Route对象的ProcessConstraint方法中如果传入的是一个实现了IRouteConstraint方法的对象,会调中它的Match方法,我们可以实现这个接口来处理更复杂的约束。
1 2 3 4 |
public interface IRouteConstraint { bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection); } |
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。