網頁

如何依.NET應用程式建置組態產生對應的Prefix前綴路由

如何依.NET應用程式建置組態產生對應的Prefix前綴路由

在將一個 ASP.NET Web API 應用程式進行容器化(Containerization)發現一個問題,原先的 Web API 部署至 IIS 應用程式之下,我們會區分開發區(Develop)、測試區(Staging)、正式區(Production),而原始的請求因為 IIS 應用程式的關係,路由都會被加上應用程式的前綴(Route Prefix),例如:

  • Org:/api/value
  • Dev:/develop/api/value
  • Stg:/staging/api/value
  • Prd:/production/api/value

如同 git 的分支 /{Branch}/api/value,這些路由前綴是 IIS 加上去的,並非 Web API 的路由系統加上去的。也就是說,當我們把 Web API 專案完成容器化成為映像檔之中,路由系統都是固定的預設值

config.MapHttpAttributeRoutes();

config.Routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

如果不能產生對應的 /{Branch}/ 路由,也就是說,當初接此 Web API 的所有用戶端都必須重新修改程式端點接口,這可怎麼辦?或是說,現在我們想依不同的建置組態來產生對應的 Prefix 前綴路由,我想希望把對用戶端的影響減至最小。你或許會想,那從 routeTemplate 下手呢?routeTemplate: "/develop/api/{controller}/{id}" 這就不好了。這在開發區是對的,但怎麼在 merge (pull request, PR) 時自動把 /develop 換成 /staging 呢?

很快就把腦筋動到屬性路由身上,屬性路由的 RoutePrefix 雖然好用,但面臨和 routeTemplate 的一樣的問題。

不過我知道,不論是 ASP.NET MVC 或 ASP.NET Web API 都留下了一個非常好的習慣,那就是擴充點。如果需要客製化屬性路由,那麼可以繼承 DefaultDirectRouteProvider 類別來實作:

public class DefaultDirectRouteProvider : IDirectRouteProvider

DefaultDirectRouteProvider 本身提供許多取得路由條件的方案,這些方法都是設計成可覆寫的,裡面的 GetRoutePrefix 看起來合乎我們的需求,我們參考 strathweb 的版本快速完成我們需所的 CentralizedPrefixProvider 擴充,

public class CentralizedPrefixProvider : DefaultDirectRouteProvider
{
    private readonly string _centralizedPrefix;
    public CentralizedPrefixProvider(string centralizedPrefix)
    {
        _centralizedPrefix = centralizedPrefix;
    }
    protected override string GetRoutePrefix(HttpControllerDescriptor controllerDescriptor)
    {
        var existingPrefix = base.GetRoutePrefix(controllerDescriptor);
        if (existingPrefix == null)
            return _centralizedPrefix;

        return $"{_centralizedPrefix}/{existingPrefix}";
    }
}

我們將 _centralizedPrefix 設置抽離主程式之外,在 WebApiConfig.cs 注冊時在動態注入:

private static readonly string _routeprefix = WebConfigurationManager.AppSettings["RoutePrefix"];
config.MapHttpAttributeRoutes(new CentralizedPrefixProvider(_routeprefix));

現在我們的 Web API 就能依不同建置組態從 Web.config 裡去讀出對應的 Prefix 期望值,就能進行正確的路由組態。但立即延伸一個問題,其中有個 Controller 是拿來進行 Docker 的健康檢查的,例如:/api/healthy,在導入動態指定 Prefix 路由規則後,反而無法正常運作!還好這用個 ~ 就能解決:

[Route("~/api/healthy")]

透過 ~ 覆寫規則就能讓特定路由規則不受其他規則影響。其實相同的問題,我們在 .NET Core 也碰過也處理完成,只不過,因為不是舊應用程式的轉移,所以影響較小。而舊應用程式的轉移,必須考慮的點就比較多些。還好 ASP.NET MVC / ASP.NET Web API 的擴充點真的是個非常好的設計,沒有的就留給開發者自己動手做。

Coding for fun. Docker for everyweher.

沒有留言:

張貼留言

感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。