zoukankan      html  css  js  c++  java
  • 简单的mvc之三:灵活的路由(上)

      在上一篇关于管线的随笔中已经提及了管线,通过对管线的分析,我们可以得到下面几个结论:路由系统由URLRoutingModule模块实现,它订阅了PostResolvRequestCache事件;路由系统通过查阅路由并尽可能的通过RemapHandler方法,确定excuteHandler阶段执行的IHttphandler。这一篇随笔想详细谢谢路由的定义、注册和导航的具体过程。

      路由系统的导航过程定义于URLRoutingModule,具体实现如下:

     1 public virtual void PostResolveRequestCache(HttpContextBase context)
     2 {
     3     RouteData routeData = this.RouteCollection.GetRouteData(context);
     4     if (routeData != null)
     5     {
     6         IRouteHandler routeHandler = routeData.RouteHandler;
     7         if (routeHandler == null)
     8         {
     9             throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString("UrlRoutingModule_NoRouteHandler"), new object[0]));
    10         }
    11         if (!(routeHandler is StopRoutingHandler))
    12         {
    13             RequestContext requestContext = new RequestContext(context, routeData);
    14             context.Request.RequestContext = requestContext;
    15             IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
    16             if (httpHandler == null)
    17             {
    18                 throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, SR.GetString("UrlRoutingModule_NoHttpHandler"), new object[] { routeHandler.GetType() }));
    19             }
    20             if (httpHandler is UrlAuthFailureHandler)
    21             {
    22                 if (!FormsAuthenticationModule.FormsAuthRequired)
    23                 {
    24                     throw new HttpException(0x191, SR.GetString("Assess_Denied_Description3"));
    25                 }
    26                 UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
    27             }
    28             else
    29             {
    30                 context.RemapHandler(httpHandler);
    31             }
    32         }
    33     }
    34 }
    35 
    36  
    37 
    38  
    View Code

    代码中可以很明显看出导航逻辑:通过全局全局路由表Routeable.Routes.GetRouteData()方法获得RouteData,然后通过RouteData.GetHttphandler()获得IRoutehandler,最后通过HttpContext.RemapHandler()注册IRouteHandler.GetHttpHandler()得到的IhttpHandler。代码中的出现的StopRoutingHandler后面再说。

      路由表的切入口在全局静态类RouteTable.Routes,注册的过程放在HttpApplication的Appliation_Start方法中,这个方法如同静态构造函数一般,只启动一次,所以所有需要初始化的全局对象都可以放在这个方法中。vs建立的项目中,Route表的注册被分离出来,放在了App_Start文件夹中,使代码更加清晰。例如以下代码所示,3个路由被注册:

     1 public static void RegisterRoutes(RouteCollection routes) {
     2     routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
     3 
     4     routes.MapRoute(
     5         name: "Default",
     6         url: "{controller}/{action}/{id}",
     7         defaults: new { controller = "Self", action = "Index", id = UrlParameter.Optional }
     8     );
     9 
    10     routes.MapRoute(
    11         name: "CatchAll",
    12         url: "{*input}",
    13         defaults: new { controller = "Home", action = "index" }
    14     );
    15 }

    上面的代码中使用的IgnoreRoute()与MapRoute()皆为扩展方法,用来简化路由注册过程,前者初始化一个Route对象,或者初始化一个IgnoreRouteInternal(继承于Route,私有封闭)对象,然后使用RouteTable.Routes.Add()方法添加如注册表。通过如下代码,可以清楚的看到注册的route:

     1 public void Routes() {
     2     foreach (var routeBase in RouteTable.Routes) {
     3         var route = routeBase as Route;
     4         if (route != null) {
     5             Response.Write(String.Format("{1,26}  {0}<br/>",
     6                 route.GetType().ToString(), route.Url
     7             ).Replace(" ","&nbsp;"));
     8         } else {
     9             Response.Write(String.Format("other:{0}<br/>", routeBase.GetType()));
    10         }
    11     }
    12 }

    得到的结果如下:

    从图中我们可以看到后3个路由正是我们已经注册的3个路由,至于第一个路由,实际上是托管于asp.net的web api应用路由,这个后面再说。同时图中的第二列的Type也佐证了上面所说的不同的Route类说法。

      那么Route类的具体实现呢?首先我们看看Route的继承树:

      

    看着很熟悉,因为集成树中的类在上述的路由图中已经全部出现,整个树的基类是RouteBase,也就是图中高亮的那个,Routes中的集合类型也是RouteBase,下面是RouteBase的定义:

     1 [TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
     2 public abstract class RouteBase
     3 {
     4     // Fields
     5     private bool _routeExistingFiles;
     6 
     7     // Methods
     8     protected RouteBase();
     9     public abstract RouteData GetRouteData(HttpContextBase httpContext);
    10     public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
    11 
    12     // Properties
    13     public bool RouteExistingFiles { [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] get; [TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] set; }
    14 }

    这个一个抽象类,核心方法就是URLRouting中出现过的GetRouteData()方法,还有一个很重要的属性—RouteExistingFiles,这个属性一般用来实现对.aspx兼容支持。RouteBase最常用也是最重要的子类便是Route,代码如下:

     1 [TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
     2 public class Route : RouteBase
     3 {
     4     // Fields
     5     private ParsedRoute _parsedRoute;
     6     private string _url;
     7     private const string HttpMethodParameterName = "httpMethod";
     8 
     9     // Methods
    10     public Route(string url, IRouteHandler routeHandler);
    11     public Route(string url, RouteValueDictionary defaults, IRouteHandler routeHandler);
    12     public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, IRouteHandler routeHandler);
    13     public Route(string url, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens, IRouteHandler routeHandler);
    14     public override RouteData GetRouteData(HttpContextBase httpContext);
    15     public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
    16     protected virtual bool ProcessConstraint(HttpContextBase httpContext, object constraint, string parameterName, RouteValueDictionary values, RouteDirection routeDirection);
    17     private bool ProcessConstraints(HttpContextBase httpContext, RouteValueDictionary values, RouteDirection routeDirection);
    18 
    19     // Properties
    20     public RouteValueDictionary Constraints { get; [CompilerGenerated, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] set; }
    21     public RouteValueDictionary DataTokens { [CompilerGenerated, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] get; [CompilerGenerated, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] set; }
    22     public RouteValueDictionary Defaults { [CompilerGenerated, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] get; [CompilerGenerated, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] set; }
    23     public IRouteHandler RouteHandler { [CompilerGenerated, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] get; [CompilerGenerated, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")] set; }
    24     public string Url { get; set; }
    25 }

    相较于RouteBase,Route有5个属性,其中Url,Constraints用来过滤进入请求进行匹配,RouteHandler用来返回IHttpHandler,DataTokens与Defaults则应用于后面的控制器查找,激活与数据传递。这些就以后在细节补充中说这里就不散开了。

      从上面的Route的定义结合最前面说的导航过程,RouteBase->IRouteHandler->IhttpHandler的过程清晰可见。在前面曾经搁置的类StopRoutingHandler就属于IRoutehandler,而这个RouteHandler并不是由Route类获得的,而是前面出现的IgnoreRoute获得的,相关代码:

    1 public IgnoreRouteInternal(string url) : base(url, new StopRoutingHandler())
    2 {
    3 }

    到这里IgnoreRoute()的方法的作用就真正明了了,和MapRoute()方法想反,它定义的不是对某一类路径的拦截,而是放弃对某一类路径的拦截,以便留给后面的管线过程处理,这个管线过程就是后面要说的ExcuteHandler过程(内部,没有公开事件),具体到文中注册的路由可以看到它放弃的是对*.axd类地址的拦截。但是注册的好处是直接跳过,提高效率。

      本来想一次说完的,写到这里发现还有很多没写到,比如全局类型RouteTable等,那就分上下篇吧。最后留个问题:两个路由,一个的Url定义为{input},一个定义为{*input},二者有什么区别?

  • 相关阅读:
    正经学C#_循环[do while,while,for]:[c#入门经典]
    Vs 控件错位 右侧资源管理器文件夹点击也不管用,显示异常
    asp.net core 获取当前请求的url
    在实体对象中访问导航属性里的属性值出现异常“There is already an open DataReader associated with this Command which must be
    用orchard core和asp.net core 3.0 快速搭建博客,解决iis 部署https无法登录后台问题
    System.Data.Entity.Core.EntityCommandExecution The data reader is incompatible with the specified
    初探Java设计模式3:行为型模式(策略,观察者等)
    MySQL教程77-CROSS JOIN 交叉连接
    MySQL教程76-HAVING 过滤分组
    MySQL教程75-使用GROUP BY分组查询
  • 原文地址:https://www.cnblogs.com/wangjieas/p/3311802.html
Copyright © 2011-2022 走看看