手邊ASP.NET MVC專案有個隱藏需求,預計上線不久要推出新版,有一段時間新舊版本並存。有幾個供AJAX呼叫的API性質Controller,希望未來出新版時名稱能沿用,不要弄出BooV1Controller、BooV2Controller這種名字,最好在URL路徑加上v1/v2等就能搞定,例如:

第一版: ~/api/v1/boo/action
第二版: ~/api/v2/boo/action

在ASP.NET MVC專案處理此類路徑問題,我想到兩個選擇: 一個是利用Area機制、另一個則是直接用Route對應。考量Area切割範圍涵蓋Controller、Model及View,我只想區分Controller版本,搬出Area有點殺雞用牛刀,就用路由解決吧!

構想如下,ASP.NET MVC專案在Controllers下新增V1及V2兩個資料夾,其中各放入一個Controller類別,由於Namespace不同(Mvc4Lab.Controllers.V1 vs Mvc4Lab.Controllers.V2),兩個Controller可使用相同命名,都叫BooController。

我們都知道ASP.NET MVC是"以習慣取代配置"(Convention over Configuration),會試圖從URL中找出Controller名稱,主動尋找專案中同名的"***Controller類別"處理該URL請求。當同一專案中出現相同名稱Controller,得額外提供路由線索,ASP.NET MVC才能正確解析。

原以為得費點手腳,沒想到MapRoute()設想很周到,只需在設定路徑時額外傳入Namespace參數(支援字串陣列,限定只在指定命名空間尋找Controller),便能做到不同URL格式使用同名Controller的效果。

在App_Strat/RouteConfig.cs中加兩ApiV1, ApiV2兩則額外路徑,大功告成,就這麼簡單。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
 
namespace Mvc4Lab
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.MapRoute(
                name: "ApiV1",
                url: "api/v1/{controller}/{action}",
                namespaces: new string[] { "Mvc4Lab.Controllers.V1" }
            );
            routes.MapRoute(
                name: "ApiV2",
                url: "api/v2/{controller}/{action}",
                namespaces: new string[] { "Mvc4Lab.Controllers.V2" }
            );
            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", 
                    id = UrlParameter.Optional }
            );
        }
    }
}

不過要提醒,由於ASP.NET MVC下所有的Request都要先進行路由解析再決定處理方式,切忌把路徑設定規則搞得太多太複雜,以免拖累系統效能。


Comments

# by 翹鬍子

To 黑大 那請問兩個名稱相同的controller這樣做之後. 那假使裡面的action名子一樣. 結果要產生view的時候該怎麼做? 小弟我試過直接檢視產生會卡卡

# by Jeffrey

to 翹鬍子, 我想到最直覺的做法為不同版本的View加上V1,V2字尾,例如BlahV1.cshtml、BlahV2.cshtml,在Action指明return View("BlahV1"),有點囉嗦但簡單有效。如果想精巧一點,可以使用父類別繼承或Extension Method,加入一個ViewWithVersion()方法自動由URL中取出V1,V2,配合方法名稱自動組合成BlahV1或BlahV2。如此,Action只需寫return ViewWithVersion()就可依版本自動使用不同的View。

Post a comment