[英]Select ASP.NET Web API Controller depending on route value (namespace)
我正在嘗試將各種Web API插件添加到我的MVC網站。 復制和粘貼API文件的DLL允許我使用默認路由調用適當的服務方法 - hostname / api / {controller} / {id},但是當我有相同名稱但位於不同位置的控制器時,它會拋出錯誤( DLL,命名空間)。 錯誤消息是這樣的(這是正常的):
找到了多個匹配名為“Names”的控制器的類型。 如果為此請求提供服務的路由('api / {controller} / {id}')發現多個控制器定義了相同名稱但名稱不同的命名空間(不受支持),則會發生這種情況。 對'Names'的請求找到了以下匹配的控制器:ApiExt.NamesController ApiExt1.NamesController
我在不同的DLL(名稱空間)ApiExt,ApiExt1中有相同的“名稱”控制器。
我已經找到了一個類似的主題,根據API版本選擇控制器 - http://shazwazza.com/post/multiple-webapi-controllers-with-the-same-name-but-different-namespaces/ ,但這並不完全我需要的。 我需要根據路由值選擇控制器(命名空間),如下所示:
主機名/ API / {命名空間} / {控制器} / {ID}
我相信這絕對是可能的,但我不熟悉重寫MVC控制器選擇器(實現IHttpControllerSelector)。
有什么建議么?
謝謝。
你絕對可以做到這一點。 您需要通過實現IHttpControllerSelector
來編寫自己的Controller Selector。 有關詳細的逐步說明,請參閱此鏈接。
描述解決方案的博客文章https://blogs.msdn.microsoft.com/webdev/2013/03/07/asp-net-web-api-using-namespaces-to-version-web-apis/ ,不包含完整的代碼,與過去提供的代碼有很糟糕的聯系。
這是一個提供Umbraco原創課程的blob
完整列表,以防它被刪除:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
namespace WebApiContrib.Selectors
{
//originally created for Umbraco https://github.com/umbraco/Umbraco-CMS/blob/7.2.0/src/Umbraco.Web/WebApi/NamespaceHttpControllerSelector.cs
//adapted from there, does not recreate HttpControllerDescriptors, instead caches them
public class NamespaceHttpControllerSelector : DefaultHttpControllerSelector
{
private const string ControllerKey = "controller";
private readonly HttpConfiguration _configuration;
private readonly Lazy<HashSet<NamespacedHttpControllerMetadata>> _duplicateControllerTypes;
public NamespaceHttpControllerSelector(HttpConfiguration configuration)
: base(configuration)
{
_configuration = configuration;
_duplicateControllerTypes = new Lazy<HashSet<NamespacedHttpControllerMetadata>>(InitializeNamespacedHttpControllerMetadata);
}
public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
{
var routeData = request.GetRouteData();
if (routeData == null || routeData.Route == null || routeData.Route.DataTokens["Namespaces"] == null)
return base.SelectController(request);
// Look up controller in route data
object controllerName;
routeData.Values.TryGetValue(ControllerKey, out controllerName);
var controllerNameAsString = controllerName as string;
if (controllerNameAsString == null)
return base.SelectController(request);
//get the currently cached default controllers - this will not contain duplicate controllers found so if
// this controller is found in the underlying cache we don't need to do anything
var map = base.GetControllerMapping();
if (map.ContainsKey(controllerNameAsString))
return base.SelectController(request);
//the cache does not contain this controller because it's most likely a duplicate,
// so we need to sort this out ourselves and we can only do that if the namespace token
// is formatted correctly.
var namespaces = routeData.Route.DataTokens["Namespaces"] as IEnumerable<string>;
if (namespaces == null)
return base.SelectController(request);
//see if this is in our cache
var found = _duplicateControllerTypes.Value.FirstOrDefault(x => string.Equals(x.ControllerName, controllerNameAsString, StringComparison.OrdinalIgnoreCase) && namespaces.Contains(x.ControllerNamespace));
if (found == null)
return base.SelectController(request);
return found.Descriptor;
}
private HashSet<NamespacedHttpControllerMetadata> InitializeNamespacedHttpControllerMetadata()
{
var assembliesResolver = _configuration.Services.GetAssembliesResolver();
var controllersResolver = _configuration.Services.GetHttpControllerTypeResolver();
var controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver);
var groupedByName = controllerTypes.GroupBy(
t => t.Name.Substring(0, t.Name.Length - ControllerSuffix.Length),
StringComparer.OrdinalIgnoreCase).Where(x => x.Count() > 1);
var duplicateControllers = groupedByName.ToDictionary(
g => g.Key,
g => g.ToLookup(t => t.Namespace ?? String.Empty, StringComparer.OrdinalIgnoreCase),
StringComparer.OrdinalIgnoreCase);
var result = new HashSet<NamespacedHttpControllerMetadata>();
foreach (var controllerTypeGroup in duplicateControllers)
{
foreach (var controllerType in controllerTypeGroup.Value.SelectMany(controllerTypesGrouping => controllerTypesGrouping))
{
result.Add(new NamespacedHttpControllerMetadata(controllerTypeGroup.Key, controllerType.Namespace,
new HttpControllerDescriptor(_configuration, controllerTypeGroup.Key, controllerType)));
}
}
return result;
}
private class NamespacedHttpControllerMetadata
{
private readonly string _controllerName;
private readonly string _controllerNamespace;
private readonly HttpControllerDescriptor _descriptor;
public NamespacedHttpControllerMetadata(string controllerName, string controllerNamespace, HttpControllerDescriptor descriptor)
{
_controllerName = controllerName;
_controllerNamespace = controllerNamespace;
_descriptor = descriptor;
}
public string ControllerName
{
get { return _controllerName; }
}
public string ControllerNamespace
{
get { return _controllerNamespace; }
}
public HttpControllerDescriptor Descriptor
{
get { return _descriptor; }
}
}
}
}
然后只需將Namespaces標記添加到路由中
route.DataTokens["Namespaces"] = new string[] {"Foo.Controllers"};
它還可以通過緩存實現更多生產。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.