[MVC]ASP.NET MVC Preview 3 流程分析 - 4.Router

我们先了解一下 System.Web.Routing 中的几个类型。

  • UrlRoutingModule : IHttpModule : 入口点。订阅 HttpApplication 相关事件,修改 HttpContext.Handler (IHttpHandler),使得 MvcHandler 得以执行。
  • Route : RouteBase : 路径转向规则,包括路径匹配表达式(Url)、参数默认值(Defaults)、请求处理程序(RouteHandler) 等。
  • RouteCollection : Collection<RouteBase> : 存储所有的 Route 规则的集合,提供依据上下文获取动态路由数据的方法。
  • RouteTable : 持有全局 RouteCollection 实例引用的一个辅助类。
  • RouteData : 依据当前上下文进行解析的动态请求路由信息,包括从请求参数或路由规则中提取的 Controller Name、Action Name、Action Method Parameter 等。
  • IRouteHandler : Route 中请求处理程序的包装接口。

要使用 Routing,除了在配置文件中添加 UrlRoutingModule 外,我们还得在 Global.asax.cs 中注册相应的路由规则(Route)。

public class GlobalApplication : System.Web.HttpApplication
{
  public static void RegisterRoutes(RouteCollection routes)
  {
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.MapRoute(
      "Test",
      "Home/Test/{x}/{y}",
      new { controller = "Home", action = "Test", x = 100, y = 200 }
    );
    routes.MapRoute(
      "Default", // Route name
      "{controller}/{action}/{id}", // URL with parameters
      new { controller = "Home", action = "Index", id = "" } // Parameter defaults
    );
  }
  protected void Application_Start()
  {
    RegisterRoutes(RouteTable.Routes);
  }
}

System.Web.Mvc 额外提供了几个扩展方法来简化我们注册 Route 规则。

public static class RouteCollectionExtensions
{
  public static void IgnoreRoute(this RouteCollection routes, string url)
  {
    routes.IgnoreRoute(url, null);
  }
  public static void IgnoreRoute(this RouteCollection routes, string url, object constraints)
  {
    // … 省略部分代码 …
    Route route = new Route(url, new StopRoutingHandler())
    {
      Constraints = new RouteValueDictionary(constraints)
    };
    routes.Add(route);
  }
  public static void MapRoute(this RouteCollection routes, string name, string url)
  {
    routes.MapRoute(name, url, null, null);
  }
  public static void MapRoute(this RouteCollection routes, string name, string url, object defaults)
  {
    routes.MapRoute(name, url, defaults, null);
  }
  public static void MapRoute(this RouteCollection routes, string name, string url, object defaults, object constraints)
  {
    // … 省略部分代码 …
    Route route = new Route(url, new MvcRouteHandler())
    {
      Defaults = new RouteValueDictionary(defaults),
      Constraints = new RouteValueDictionary(constraints)
    };
    if (string.IsNullOrEmpty(name))
    {
      routes.Add(route);
    }
    else
    {
      routes.Add(name, route);
    }
  }
}

有一点需要注意,System.Web.Routing 通过循环查找的方式来匹配路由规则,因为我们要按以下顺序注册路由规则。
(1) 忽略规则 (IgnoreRoute)。
(2) 具体匹配规则。
(3) 通用匹配规则。
RouteTable.Routes 实际上指向一个应用程序域中唯一的 RouteCollection 实例。

public class RouteTable
{
  // Fields
  private static RouteCollection _instance = new RouteCollection();
  // Properties
  public static RouteCollection Routes
  {
    get { return _instance; }
  }
}

RouteCollection 除了保存所有路由规则 (Route) 外,还有几个重要的方法。

public class RouteCollection : Collection<RouteBase>
{
  // Methods
  public void Add(string name, RouteBase item);
  public RouteData GetRouteData(HttpContextBase httpContext);
  public VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
  public VirtualPathData GetVirtualPath(RequestContext requestContext, string name, RouteValueDictionary values);
  // Properties
  public RouteBase this[string name] { get; }
  public bool RouteExistingFiles { get; set; }
}

GetRouteData() 通过当前请求上下文,找出最合适的路由规则(Route),并生成当前执行环境所需的动态路由数据(RouteData)。

public class RouteCollection : Collection<RouteBase>
{
  public RouteData GetRouteData(HttpContextBase httpContext)
  {
    // … 省略部分代码 …
    using (this.GetReadLock())
    {
      foreach (RouteBase base2 in this)
      {
        RouteData routeData = base2.GetRouteData(httpContext);
        if (routeData != null)
        {
          return routeData;
        }
      }
    }
    return null;
  }
}

循环调用搜索规则的 GetRouteData 方法来获取动态路由数据,这就是上面我们提到要注意路由规则注册顺序的原因。

public class Route : RouteBase
{
  public override RouteData GetRouteData(HttpContextBase httpContext)
  {
    string virtualPath = httpContext.Request.AppRelativeCurrentExecutionFilePath.Substring(2) +
      httpContext.Request.PathInfo;
    RouteValueDictionary values = this._parsedRoute.Match(virtualPath, this.Defaults);
    if (values == null)
    {
      return null;
    }
    RouteData data = new RouteData(this, this.RouteHandler);
    if (!this.ProcessConstraints(httpContext, values, RouteDirection.IncomingRequest))
    {
      return null;
    }
    foreach (KeyValuePair<string, object> pair in values)
    {
      data.Values.Add(pair.Key, pair.Value);
    }
    if (this.DataTokens != null)
    {
      foreach (KeyValuePair<string, object> pair2 in this.DataTokens)
      {
        data.DataTokens[pair2.Key] = pair2.Value;
      }
    
      return data;
    }
  }
}

Route.GetRouteData() 首先调用 ParsedRoute.Match() 来检查路径是否匹配,接下来使用 ProcessConstraints() 检查相应的约束规则,如果这些检查都得以通过,则生成最终的动态路由数据。RouteCollection.GetRouteData() 在获取该结果后终止循环。
IRouteHandler 的作用是返回一个特定 IHttpHandler,比如 MvcHandler,当然也可以是 "System.Web.UI.Page : IHttpHandler"。

public interface IRouteHandler
{
  IHttpHandler GetHttpHandler(RequestContext requestContext);
}
public class MvcRouteHandler : IRouteHandler
{
  protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
  {
    return new MvcHandler(requestContext);
  }
  IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
  {
    return this.GetHttpHandler(requestContext);
  }
}

UrlRoutingModule 的执行顺序,可参考《ASP.NET MVC Preview 2 – 流程分析 (1)》
Route 的相关语法规则,可参考《ASP.NET MVC Preview 3 Release 》(ScottGu 中文版)。

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏