[Web API] Web API 2 深入系列(1) 路由


目錄

  1. ASP.NET 路由

    • 注冊路由

    • 動態映射HttpHandler

  2. WebAPI 路由

    • 注冊路由

    • 調用GetRouteData

  3. 2個路由系統銜接

    • GlobalConfiguration

    • HostedHttpRoute

  4. 補充

路由是進入Web API的第一扇門.目的用於確定Controller名稱、Action名稱、路由參數.

ASP.NET 路由

注冊路由

在ASP.NET中注冊路由的方式:
RouteCollection.MapPageRoute()

添加1個完整的路由:

var defaults = new RouteValueDictionary//路由變量默認值
{
        {"code","010"},
        {"phone","1000000"},
};
var constraints = new RouteValueDictionary//路由變量約束
{
    {"code",@"0\d{2,3}" },
    {"phone",@"\d{7,9}" },
    {"httpMethod",new HttpMethodConstraint("POST") }
};
var dataTokens = new RouteValueDictionary//路由相關參數,不用於處理路由匹配功能
{
    {"defaultCode","北京" },
    {"defaultPhone","北京X電話" }
};
RouteTable.Routes.MapPageRoute("default", "{code}/{phone}", "~/call.aspx", false, defaults, constraints, dataTokens);

獲取RouteCollection

一般通過RouteTable.Routes

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

路由:RouteBase

public abstract class RouteBase
{
  public bool RouteExistingFiles {get; set;} = true;//對現有文件進行路由
  public abstract RouteData GetRouteData(HttpContextBase httpContext);
  public abstract VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values);
}

路由默認實現:Route

public class Route : RouteBase
{
    /// <summary>
    /// 路由約束
    /// </summary>
    public RouteValueDictionary Constraints { get; set; }
    /// <summary>
    /// 路由自定義參數(一般存儲備注說明等)
    /// </summary>
    public RouteValueDictionary DataTokens { get; set; }
    /// <summary>
    /// 路由變量默認值
    /// </summary>
    public RouteValueDictionary Defaults { get; set; }
    /// <summary>
    /// 路由對應處理程序對象
    /// </summary>
    public IRouteHandler RouteHandler { get; set; }
    /// <summary>
    /// 路由模板
    /// </summary>
    public string Url { get; set; }
}

忽略路由:
忽略路由本質是添加一個StopRoutingHandler(返回空的HttpHandler)的路由

routes.Ignore("010/1000001");(路由先注冊,先匹配)
routes.MapPageRoute("default", "{code}/{phone}", "~/call.aspx", false, defaults, constraints, dataTokens);

路由約束:
在上面完整的路由添加Demo中,路由約束有2種方式

  1. 正則表達式字符串
  2. 實現IRouteConstraint接口

動態映射HttpHandler

路由是通過UrlRoutingModule這個HttpModule實現動態攔截.

public virtual void PostResolveRequestCache(HttpContextBase context)
{
    RouteData routeData = RouteTable.Routes.GetRouteData(context);
    IRouteHandler routeHandler = routeData.RouteHandler;
    IHttpHandler httpHandler = routeHandler.GetHttpHandler(new RequestContext(context, routeData));
    context.RemapHandler(httpHandler);
}

WebAPI 路由

注冊路由

在Web API中注冊路由的方式:HttpRouteCollection.MapHttpRoute(),
這里重點看下MapHttpRoute方法內部

public static class HttpRouteCollectionExtensions
{
    public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name, string routeTemplate, object defaults, object constraints, HttpMessageHandler handler)
    {
        IHttpRoute route = routes.CreateRoute(routeTemplate, (IDictionary<string, object>)defaults, (IDictionary<string, object>)constraints, (IDictionary<string, object>)null, handler);
        routes.Add(name, route);
        return route;
    }
}

獲取HttpRouteCollection

在WebAPI中通過HttpConfiguration的Routes屬性

public class HttpConfiguration : IDisposable
{
    public Collection<DelegatingHandler> MessageHandlers { get; }
    public HttpRouteCollection Routes { get; }
    public ConcurrentDictionary<object, object> Properties { get; }
}

WebAPI路由:IHttpRoute

public interface IHttpRoute
{
  string RouteTemplate { get; }
 
  IDictionary<string, object> Defaults { get; }
 
  IDictionary<string, object> Constraints { get; }
 
  IDictionary<string, object> DataTokens { get; }
 
  HttpMessageHandler Handler { get; }
 
  IHttpRouteData GetRouteData(string virtualPathRoot, HttpRequestMessage request);
 
  IHttpVirtualPathData GetVirtualPath(HttpRequestMessage request, IDictionary<string, object> values);
}

WebAPI路由默認實現:HttpRoute
由於IHttpRoute設計的非常全,HttpRoute基本就是實現了IHttpRoute.
(這種設計上比RouteBase要優勢很多)

調用GetRouteData

與ASP.NET稍微不同的是

  1. 直接對所有請求進行路由 而不再判斷是否文件存在.
  2. 添加一個VirtualPathRoot的過濾
public class HttpRouteCollection
{
    public virtual IHttpRouteData GetRouteData(HttpRequestMessage request)
    {
        string virtualPathRoot = request.GetRequestContext().VirtualPathRoot;
        IHttpRouteData routeData = this._collection[index].GetRouteData(virtualPathRoot, request);
        return routeData;
    }
}

2個路由系統銜接

Web API提供了一套自身的路由系統,所以不依賴於ASP.NET.

寄宿方式有多種.如果以WebHost的方式.本質上還是走的ASP.NET路由處理.

GlobalConfiguration

WebAPI的配置在HttpConfiguration中提供.

在WebHost中,定義了GlobalConfiguration用來創建HttpConfiguration

public static class GlobalConfiguration
{
public static HttpConfiguration Configuration = GlobalConfiguration.CreateConfiguration();//創建HttpConfiguration

public static HttpMessageHandler DefaultHandler = GlobalConfiguration.CreateDefaultHandler();//創建HttpRoutingDispatcher(WebAPI管道尾部HttpMessageHandler)

public static HttpServer DefaultServer = GlobalConfiguration.CreateDefaultServer();//創建HttpServer(WebAPI管道開頭HttpMessageHandler)

public static void Configure(Action<HttpConfiguration> configurationCallback)//提供方便配置路由
{
    configurationCallback(GlobalConfiguration.Configuration);
}

private static Lazy<HttpConfiguration> CreateConfiguration()
{
    return new Lazy<HttpConfiguration>((Func<HttpConfiguration>) (() =>
    {
        //這里用HostedHttpRouteCollection實現HttpRouteCollection
        return new HttpConfiguration((HttpRouteCollection) new HostedHttpRouteCollection(RouteTable.Routes));
    }));
}

}

HostedHttpRoute

在GlobalConfiguration中創建的HttpConfiguration對象是用HostedHttpRouteCollection作為參數
這里我覺得有必要看下CreateRoute和Add方法

internal class HostedHttpRouteCollection : HttpRouteCollection
{
    //真正維護的路由集合
    private readonly RouteCollection _routeCollection;

    //創建路由 MapHttpRoute會調用該方法
    public override IHttpRoute CreateRoute(string uriTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens, HttpMessageHandler handler)
    {
      return (IHttpRoute) new HostedHttpRoute(uriTemplate, defaults, constraints, dataTokens, handler);
    }

    public override void Add(string name, IHttpRoute route)
    {
        //只是添加到內部的routeCollection中
        //route.ToRoute() => route.OriginalRoute;
      this._routeCollection.Add(name, (RouteBase) route.ToRoute());
    }

    public override IHttpRouteData GetRouteData(HttpRequestMessage request)
    {
        //通過調用內部的Route的GetRouteData
        RouteData routeData = this._routeCollection.GetRouteData(httpContextBase);
        return (IHttpRouteData)new HostedHttpRouteData(routeData);
    }
}

而HostedHttpRoute在WebHost中
相當於用HttpRoute的身 卻提供了真正的Route

internal class HostedHttpRoute : IHttpRoute
{
    //ASP.NET Route
    internal Route OriginalRoute { get; private set; }

    public HostedHttpRoute(string uriTemplate, IDictionary<string, object> defaults, IDictionary<string, object> constraints, IDictionary<string, object> dataTokens, HttpMessageHandler handler)
    {
        //內部的OriginalRoute實際為HttpWebRoute
      this.OriginalRoute = (Route) new HttpWebRoute(uriTemplate, defaults1, constraints1, dataTokens1, <strong>(IRouteHandler) HttpControllerRouteHandler.Instance</strong>, (IHttpRoute) this);
      this.Handler = handler;
    }
}

從HostedHttpRoute構造函數中 我們看到了真正的Route為HttpWebRoute 且RouteHandler為HttpControllerRouteHandler.Instance

public class HttpControllerRouteHandler : IRouteHandler
{
    public static HttpControllerRouteHandler Instance{ get { return new HttpControllerRouteHandler(); } }
 
    protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        return (IHttpHandler) new HttpControllerHandler(requestContext.RouteData);
    }
}

補充

冗余的設計:

  • HttpRoute中Handler的HttpMessageHandler,未發現有任何地方使用到.

  • HttpRoute和Route中DataTokens,建議直接取消.

備注:

  • 文章中的代碼並非完整,一般是經過自己精簡后的.

  • 本篇內容使用MarkDown語法編輯

首發地址:http://neverc.cnblogs.com/p/5933752.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM