區域可以有自己的 Model、 View 、Controller 模組,有獨立的網址路由和主版,提供很大的開發彈性。
為此,我選擇將網站中的其中一部分系統功能用 Areas 開發,但是我在 Backend 開了一個 Api Controller,名稱是 Member,處理會員資訊,如下圖。
我本以為這個 WebApi 的路徑會是 Backend/api/MemberApi/,可是實際路徑 api/MemberApi,後來我想如果別的 Areas 也有建一個 MemberApi,會怎麼樣?
會這樣。
最後就是它不知道該走哪一個路由。
此時我就找 BackendAreaRegistration.cs 這個 Areas 的路由檔案如下:
context.MapRoute( "Backend_default", "Backend/{controller}/{action}/{id}", new { action = "Index", id = UrlParameter.Optional } );
這裡的路由是只對一般的 Controller,後來我 google 了一下解法,參考了這篇 ASP.NET MVC 4 WebAPI. Support Areas in HttpControllerSelector。
1.
新增一個類別 AreaHttpControllerSelector.cs,目前是擺在 App_Start 底下。namespace EnterprisePortal.Infrastructure.Dispatcher { using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Net.Http; using System.Web.Http; using System.Web.Http.Controllers; using System.Web.Http.Dispatcher; public class AreaHttpControllerSelector : DefaultHttpControllerSelector { private const string ControllerSuffix = "Controller"; private const string AreaRouteVariableName = "area"; private readonly HttpConfiguration _configuration; public AreaHttpControllerSelector(HttpConfiguration configuration) : base(configuration) { _configuration = configuration; } private Dictionary<string, Type> _apiControllerTypes; private Dictionary<string, Type> ApiControllerTypes { get { return _apiControllerTypes ?? (_apiControllerTypes = GetControllerTypes()); } } private static Dictionary<string, Type> GetControllerTypes() { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); var types = assemblies.SelectMany(a => a.GetTypes().Where(t => !t.IsAbstract && t.Name.EndsWith(ControllerSuffix) && typeof(IHttpController).IsAssignableFrom(t))) .ToDictionary(t => t.FullName, t => t); return types; } public override HttpControllerDescriptor SelectController(HttpRequestMessage request) { return GetApiController(request) ?? base.SelectController(request); } private static string GetAreaName(HttpRequestMessage request) { var data = request.GetRouteData(); if (!data.Values.ContainsKey(AreaRouteVariableName)) { return null; } return data.Values[AreaRouteVariableName].ToString().ToLower(); } private Type GetControllerTypeByArea(string areaName, string controllerName) { var areaNameToFind = string.Format(".{0}.", areaName.ToLower()); var controllerNameToFind = string.Format(".{0}{1}", controllerName, ControllerSuffix); return ApiControllerTypes.Where(t => t.Key.ToLower().Contains(areaNameToFind) && t.Key.EndsWith(controllerNameToFind, StringComparison.OrdinalIgnoreCase)) .Select(t => t.Value).FirstOrDefault(); } private HttpControllerDescriptor GetApiController(HttpRequestMessage request) { var controllerName = base.GetControllerName(request); var areaName = GetAreaName(request); if (string.IsNullOrEmpty(areaName)) { return null; } var type = GetControllerTypeByArea(areaName, controllerName); if (type == null) { return null; } return new HttpControllerDescriptor(_configuration, controllerName, type); } } }
2.
然後在 Global.asax 引用兩個命名空間。using EnterprisePortal.Infrastructure.Dispatcher; using System.Web.Http.Dispatcher;
最後在 Application_Start() 加上
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerSelector), new AreaHttpControllerSelector(GlobalConfiguration.Configuration)); RouteTable.Routes.MapHttpRoute( name: "AreaApi", routeTemplate: "api/{area}/{controller}/{id}", defaults: new { id = RouteParameter.Optional } );
沒有留言 :
張貼留言