默認狀況下,微軟提供的MVC框架模板中,WebAPI路由是不支持Namespace參數的。這致使一些比較大型的項目,沒法把WebApi分離到單獨的類庫中。 html
本文將提供解決該問題的方案。 api
微軟官方曾經給出過一個關於WebAPI支持Namespace的擴展,其主要內容就是自定義實現了IHttpControllerSelector接口,經過路由配置時替換掉MVC中自帶的DefaultHttpControllerSelector達到WebAPI支持Namespace的目的。可是通過個人測試,效果並很差。這就就不貼微軟官方的源碼了。 框架
下面我介紹一下個人解決方案。 ide
首先看一下我自定義的目錄結構,以下圖: 測試
首先定義一個類,名字能夠隨意,我這裏命名爲ZhuSirNamespaceHttpControllerSelector,他繼承自MVC框架默認的DefaultHttpControllerSelector類,該繼承類主要目的是在請求URI到達WebAPI路由時檢索咱們指定的命名空間的WebAPI控制器。下面是ZhuSirNamespaceHttpControllerSelector類的源代碼,可直接複製到你本身的項目中就能夠用: this
/// <summary> /// 擴展自DefaultHttpControllerSelector類的控制器選擇器,目前在用 /// </summary> public class ZhuSirNamespaceHttpControllerSelector : DefaultHttpControllerSelector { private const string NamespaceRouteVariableName = "namespaces"; private readonly HttpConfiguration _configuration; private readonly Lazy<ConcurrentDictionary<string, Type>> _apiControllerCache; public ZhuSirNamespaceHttpControllerSelector(HttpConfiguration configuration) : base(configuration) { _configuration = configuration; _apiControllerCache = new Lazy<ConcurrentDictionary<string, Type>>( new Func<ConcurrentDictionary<string, Type>>(InitializeApiControllerCache)); } private ConcurrentDictionary<string, Type> InitializeApiControllerCache() { IAssembliesResolver assembliesResolver = this._configuration.Services.GetAssembliesResolver(); var types = this._configuration.Services.GetHttpControllerTypeResolver() .GetControllerTypes(assembliesResolver).ToDictionary(t => t.FullName, t => t); return new ConcurrentDictionary<string, Type>(types); } public IEnumerable<string> GetControllerFullName(HttpRequestMessage request, string controllerName) { object namespaceName; var data = request.GetRouteData(); IEnumerable<string> keys = _apiControllerCache.Value.ToDictionary<KeyValuePair<string, Type>, string, Type>(t => t.Key, t => t.Value, StringComparer.CurrentCultureIgnoreCase).Keys.ToList(); if (!data.Values.TryGetValue(NamespaceRouteVariableName, out namespaceName)) { return from k in keys where k.EndsWith(string.Format(".{0}{1}", controllerName, DefaultHttpControllerSelector.ControllerSuffix), StringComparison.CurrentCultureIgnoreCase) select k; } string[] namespaces = (string[])namespaceName; return from n in namespaces join k in keys on string.Format("{0}.{1}{2}", n, controllerName, DefaultHttpControllerSelector.ControllerSuffix).ToLower() equals k.ToLower() select k; } public override HttpControllerDescriptor SelectController(HttpRequestMessage request) { Type type; if (request == null) { throw new ArgumentNullException("request"); } string controllerName = this.GetControllerName(request); if (string.IsNullOrEmpty(controllerName)) { throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound, string.Format("沒法經過API路由匹配到您所請求的URI '{0}'", new object[] { request.RequestUri }))); } IEnumerable<string> fullNames = GetControllerFullName(request, controllerName); if (fullNames.Count() == 0) { throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound, string.Format("沒法經過API路由匹配到您所請求的URI '{0}'", new object[] { request.RequestUri }))); } if (this._apiControllerCache.Value.TryGetValue(fullNames.First(), out type)) { return new HttpControllerDescriptor(_configuration, controllerName, type); } throw new HttpResponseException(request.CreateErrorResponse(HttpStatusCode.NotFound, string.Format("沒法經過API路由匹配到您所請求的URI '{0}'", new object[] { request.RequestUri }))); } }
第二步,須要咱們配置WebAPI路由設置,添加{ namespaces }片斷變量,同時也能夠直接爲其設置默認值,而後替換掉原MVC框架中的DefaultHttpControllerSelector選額器爲咱們以前擴展的ZhuSirNamespaceHttpControllerSelector選額器。這裏須要注意片斷變量的變量名namespaces必定要與咱們ZhuSirNamespaceHttpControllerSelector中定義的NamespaceRouteVariableName字符串常量的值一致。下面貼出WebApiConfig的源碼:
public static class WebApiConfig { public static void Register(HttpConfiguration config) { //配置檢查Api控制後綴爲ApiController //var suffix = typeof(MicrosoftNamespaceHttpControllerSelector) // .GetField("ControllerSuffix", BindingFlags.Static | BindingFlags.Public); //if (suffix != null) //{ // suffix.SetValue(null, "ApiController"); //} // Web API 配置和服務 // 將 Web API 配置爲僅使用不記名令牌身份驗證。 config.SuppressDefaultHostAuthentication(); config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType)); // 對 JSON 數據使用混合大小寫。 config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); // Web API 路由 config.MapHttpAttributeRoutes(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}/{namespaces}", defaults: new { id = RouteParameter.Optional ,namespaces = new[] { "ZhuSir.HMS.WebApi.ApiControllers" } } ); config.Services.Replace(typeof(IHttpControllerSelector), new ZhuSirNamespaceHttpControllerSelector(config)); } }
如上源碼。咱們替換了原MVC框架的DefaultHttpControllerSelector爲ZhuSirNamespaceHttpControllerSelector,而且指定了默認的namespaces爲ZhuSir.HMS.WebApi.ApiControllers。大致意思就是當URL爲 http://XXX/api/testApi時,WebApi路由將在ZhuSir.HMS.WebApi.ApiControllers命名空間下尋找名稱爲testApi的WebAPI控制器的Get方法。 spa
固然,WebAPI控制器除了集成字ApiController意外還要注意命名規範,都須要以Controller結尾,爲了避免讓API控制器與MVC控制器重名,我都以ApiController結尾。下面貼出testAPI的源碼:code
namespace ZhuSir.HMS.WebApi.ApiControllers { public class TestApiController : ApiController { [HttpGet] public string Gettest() { return "測試數據"; } } }
程序Debug,錄入URL: http://localhost:4541/api/TestApi,獲得以下結果: orm
能夠看出,WebAPI路由成功訪問到了其餘類庫中的WebAPI控制器。 htm
但願本文對你能有所幫助,若是轉載請註明出處:http://www.cnblogs.com/smallprogram/p/5673066.html