給ASP.net Web API的Controller分類

來源:互聯網
上載者:User

這篇文章講述了RESTFul Web API的設計思路:《對RESTful Web API的理解與設計思路》

接著這篇文章講述了如何建立一個“純淨”的ASP.net Web API應用程式:《ASP.net Web API的模組依賴關係》

本文就講述如何在這個“純淨”的應用程式上添加些內容。其實關於ASP.net Web API的文章已經不少,但很少講述如何來給Controller分類的,為什麼需要分類?你想如果一個項目稍微大點,Controller一多起來,而所有的Controller都通過“api/controllername”這個URI來訪問,那勢必帶來混亂,你的Controller可能有重名,或覺得結構層次不清晰等。

也許你馬上想到了:Area!對,ASP.net MVC不是提供了Area功能嗎?有了Area我們就可以把功能劃分好並很好地組織起來了。遺憾的是:Area真的是MVC的功能,而不是Web API的功能,Web API並不直接支援Area,解決方案是存在的,請參考這篇文章:《ASP.NET MVC 4 RC: Getting WebApi and Areas to play nicely》,你可以按照它提供的方法去做,就能解決這個問題,但這次我打算用自己的方法,因為我建立的是純粹的Web API應用程式,沒有網頁輸出的那種,並且我希望有更加簡便的結構。這是我設計的Web API:

這個例子恐怕不是很貼近實際,但比較好理解,我設計的這個“公司入口網站”提供的介面分成兩大塊,一是“聯絡我們”,二是“產品介紹”,而“產品介紹”裡面又細分為兩類,一是“辦公產品”,二是“遊戲產品”,簡單起見,所有Controller都是預設建立的Web API Controller,沒有更具體的功能。

我把所有的URI和Controller列成一張表:

Controller名稱 命名空間 URI
AdviseController WebApiRouteDemo.Controllers.ContactUs /apix/ContactUs/Advise
ProductFeedbackController WebApiRouteDemo.Controllers.ContactUs /apix/ContactUs/ProductFeedback
FinancialController WebApiRouteDemo.Controllers.Products.Enterprise /apixx/Products/Enterprise/Financial
OfficeController WebApiRouteDemo.Controllers.Products.Enterprise /apixx/Products/Enterprise/Office
PuzController WebApiRouteDemo.Controllers.Products.Game /apixx/Products/Game/Puz
RpgController WebApiRouteDemo.Controllers.Products.Game /apixx/Products/Game/Rpg
RtsController WebApiRouteDemo.Controllers.Products.Game /apixx/Products/Game/Rts
AllProductsController WebApiRouteDemo.Controllers.Products /apix/Products/AllProducts
AboutController WebApiRouteDemo.Controllers /api/About

URI的首碼“api”、“apix”和“apixx”看起來有些怪異,我想不出什麼更好的辦法來設計這個URI路由了,只好用這種最笨的方法來區分不同的URI路由規則,apixx表示接下來會帶上Area(大類)和Category(小類),apix表示接下來會帶上Area,而沒有Category,而api則表示接下來直接是controller,沒有Area和Category。這是我的路由規則定義:

            config.Routes.MapHttpRoute("AreaCategoryApi", "apixx/{area}/{category}/{controller}/{id}",new {id = RouteParameter.Optional});            config.Routes.MapHttpRoute("AreaApi", "apix/{area}/{controller}/{id}", new {id = RouteParameter.Optional});            config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}", new {id = RouteParameter.Optional});

那麼,Web API架構究竟如何最終找到它所需要的Controller呢?其實是根據名稱來的,預設情況下,但現在我們除了根據名稱,還要根據命名空間,所以你注意到我上面列出的命名空間正好反映了這個Controller所在的位置和它的功能層次。改變預設的選擇Controller的行為需要我們重新寫一個類來實現IHttpControllerSelector介面,預設的類是“DefaultHttpControllerSelector”,我們可以以它為父類:

    public class ClassifiedHttpControllerSelector : DefaultHttpControllerSelector    {        private const string AREA_ROUTE_VARIABLE_NAME = "area";        private const string CATEGORY_ROUTE_VARIABLE_NAME = "category";        private const string THE_FIX_CONTROLLER_FOLDER_NAME = "Controllers";        private readonly HttpConfiguration m_configuration;        private readonly Lazy<ConcurrentDictionary<string, Type>> m_apiControllerTypes;        public ClassifiedHttpControllerSelector(HttpConfiguration configuration) : base(configuration)        {            m_configuration = configuration;            m_apiControllerTypes = new Lazy<ConcurrentDictionary<string, Type>>(GetAllControllerTypes);        }        public override HttpControllerDescriptor SelectController(HttpRequestMessage request)        {            return GetApiController(request);        }        private static string GetRouteValueByName(HttpRequestMessage request, string strRouteName)        {            IHttpRouteData data = request.GetRouteData();            if (data.Values.ContainsKey(strRouteName))            {                return data.Values[strRouteName] as string;            }            return null;        }        private static ConcurrentDictionary<string, Type> GetAllControllerTypes()        {            Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();            Dictionary<string, Type> types = assemblies.SelectMany(a => a.GetTypes().Where(t => !t.IsAbstract && t.Name.EndsWith(ControllerSuffix, StringComparison.OrdinalIgnoreCase) && typeof (IHttpController).IsAssignableFrom(t))).ToDictionary(t => t.FullName, t => t);            return new ConcurrentDictionary<string, Type>(types);        }        private HttpControllerDescriptor GetApiController(HttpRequestMessage request)        {            string strAreaName = GetRouteValueByName(request, AREA_ROUTE_VARIABLE_NAME);            string strCategoryName = GetRouteValueByName(request, CATEGORY_ROUTE_VARIABLE_NAME);            string strControllerName = GetControllerName(request);            Type type;            try            {                type = GetControllerType(strAreaName, strCategoryName, strControllerName);            }            catch (Exception)            {                return null;            }            return new HttpControllerDescriptor(m_configuration, strControllerName, type);        }        private Type GetControllerType(string areaName, string categoryName, string controllerName)        {            IEnumerable<KeyValuePair<string, Type>> query = m_apiControllerTypes.Value.AsEnumerable();            string strControllerSearchingName;            if (string.IsNullOrEmpty(areaName))            {                strControllerSearchingName = THE_FIX_CONTROLLER_FOLDER_NAME + "." + controllerName;            }            else            {                if (string.IsNullOrEmpty(categoryName))                {                    strControllerSearchingName = THE_FIX_CONTROLLER_FOLDER_NAME + "." + areaName + "." + controllerName;                }                else                {                    strControllerSearchingName = THE_FIX_CONTROLLER_FOLDER_NAME + "." + areaName + "." + categoryName + "." + controllerName;                }            }            return query.Where(x => x.Key.IndexOf(strControllerSearchingName, StringComparison.OrdinalIgnoreCase) != -1).Select(x => x.Value).Single();        }    }

上面這段代碼很大程度上是從《ASP.NET MVC 4 RC: Getting WebApi and Areas to play nicely》來的,其作用就是根據路由參數(參數中有“Area”、“Category”和“Controller”)從程式集中尋找到對應的Controller。

這樣我們就完成了對Controller的分類,你也可以調整路由規則設計出更好的出來,不過別忘了來跟我分享下。

最後慣例,附帶上:完整代碼

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.