Web API 路由分版

透過設定 Route 指定特定 URL 指向特定 Namespace

最近常被問有沒有辦法像 Facebook API 一樣在 URL 加上 v1、v2 來提供新舊 API,例如

  • domain/api/v1/Values
  • domain/api/v2/Values

好像也不難,建一個新的 Values2Controller,加個 RouteAttribute,輕鬆完成。

只不過新的 API 都要加個 Attribute 實在是麻煩,要是覺得 v1、v2 不好看,想改成 version1、version2,不知道要花多少時間,也失去習慣取代配置的美意。

使用相同的 Controller Name 在 Web API 預設的路由會打架,在看到黑暗執行緒的ASP.NET MVC路由練習-API分版時,發現用 Namesapce 來設定路由也是一個簡便的方法,但可惜 Web API 路由的 MapHttpRoute 不像 MVC 可以直接做 namespaces 設定。

參考一些文章,發現 WebApiContrib 已實作相關功能,使用其中的 NamespaceHttpControllerSelector 來取代預設的 IHttpControllerSelector 就可以了,所以可以擴充 MapHttpRoute 多載新增 namespaces 參數

public static IHttpRoute MapHttpRoute(this HttpRouteCollection routes, string name, string routeTemplate, object defaults, string[] namespaces)
{
    var route = routes.CreateRoute(
        routeTemplate,
        new HttpRouteValueDictionary(defaults),
        null,
        dataTokens: new Dictionary<string, object> { { "Namespaces", namespaces } },
        handler: null
    );

    routes.Add(name, route);

    return route;
}

就可以開始設定 URL 規則指向特定 Namespase 了

public static void Register(HttpConfiguration config)
{
    // Web API 設定和服務

    // Web API 路由
    config.MapHttpAttributeRoutes();
    config.Services.Replace(typeof(IHttpControllerSelector), new NamespaceHttpControllerSelector(config));

    config.Routes.MapHttpRoute(
        name: "ApiV2",
        routeTemplate: "api/v2/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional },
        namespaces: new string[] { "VerApi.Controllers.V2" }
    );

    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/v1/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional },
        namespaces: new string[] { "VerApi.Controllers" }
    );

}

WebApiContrib 可以用 NuGet 安裝,或者直接引用 NamespaceHttpControllerSelector.cs

不過後來一番討論之後,考慮專案性質,在 IIS 上保留舊的站臺,另開一個新的子站也能達到效果,或許接下來會轉向研究 URL Rewrite 吧

參考

https://blog.darkthread.net/blog/aspnet-mvc-versioning-via-routing/

https://stackoverflow.com/questions/9403450/how-do-i-set-the-default-namespaces-in-maphttproute