Как вы наследуете префикс маршрута на уровне класса контроллера в WebApi?
Примечание. Я прочитал о новых функциях маршрутизации как части WebApi 2.2, чтобы разрешить наследование маршрутов. Однако это, похоже, не решает мою конкретную проблему. Кажется, он решает проблему наследования атрибутов маршрута уровня действия, но не префиксов маршрутов, определенных на уровне класса.
http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-22#ARI
Я хотел бы сделать что-то вроде этого:
[RoutePrefix("account")]
public abstract class AccountControllerBase : ControllerBase { }
[RoutePrefix("facebook")]
public class FacebookController : AccountControllerBase
{
[Route("foo")]
public async Task<string> GetAsync() { ... }
}
[RoutePrefix("google")]
public class GoogleController : AccountControllerBase
{
[Route("bar")]
public async Task<string> GetAsync() { ... }
}
Я бы хотел, чтобы префикс маршрута account
наследовался, поэтому при определении контроллеров Facebook и Google я получаю маршруты:
~/account/facebook/foo
~/account/google/bar
В настоящее время маршруты определяются без части account
из базового класса.
Ответы
Ответ 1
У меня было аналогичное требование. Я сделал это:
public class CustomDirectRouteProvider : DefaultDirectRouteProvider
{
protected override string GetRoutePrefix(HttpControllerDescriptor controllerDescriptor)
{
var routePrefix = base.GetRoutePrefix(controllerDescriptor);
var controllerBaseType = controllerDescriptor.ControllerType.BaseType;
if (controllerBaseType == typeof(BaseController))
{
//TODO: Check for extra slashes
routePrefix = "api/{tenantid}/" + routePrefix;
}
return routePrefix;
}
}
Где BaseController
- это тот, который определяет префикс. Теперь нормальные префиксы работают, и вы можете добавить свои собственные. При настройке маршрутов вызовите
config.MapHttpAttributeRoutes(new CustomDirectRouteProvider());
Ответ 2
Как @HazardouS идентифицирует, ответ @Grbinho жестко запрограммирован. Заимствование из этого ответа на наследование прямой маршрутизации и из @HazardouS, я написал этот объект
public class InheritableDirectRouteProvider : DefaultDirectRouteProvider {}
Затем переопределите следующие методы, надеясь, что RoutePrefixAttribute наследуется:
protected override IReadOnlyList<IDirectRouteFactory> GetControllerRouteFactories(HttpControllerDescriptor controllerDescriptor)
{
// Inherit route attributes decorated on base class controller
// GOTCHA: RoutePrefixAttribute doesn't show up here, even though we were expecting it to.
// Am keeping this here anyways, but am implementing an ugly fix by overriding GetRoutePrefix
return controllerDescriptor.GetCustomAttributes<IDirectRouteFactory>(true);
}
protected override IReadOnlyList<IDirectRouteFactory> GetActionRouteFactories(HttpActionDescriptor actionDescriptor)
{
// Inherit route attributes decorated on base class controller actions
return actionDescriptor.GetCustomAttributes<IDirectRouteFactory>(true);
}
К сожалению, в соответствии с полученным комментарием RoutePrefixAttribute не отображается в списке factory. Я не вникал в то, почему, если кто-то хочет исследовать немного глубже в этом.
Поэтому я сохранил эти методы для будущей совместимости и переопределил метод GetRoutePrefix следующим образом:
protected override string GetRoutePrefix(HttpControllerDescriptor controllerDescriptor)
{
// Get the calling controller route prefix
var routePrefix = base.GetRoutePrefix(controllerDescriptor);
// Iterate through each of the calling controller base classes that inherit from HttpController
var baseControllerType = controllerDescriptor.ControllerType.BaseType;
while(typeof(IHttpController).IsAssignableFrom(baseControllerType))
{
// Get the base controller route prefix, if it exists
// GOTCHA: There are two RoutePrefixAttributes... System.Web.Http.RoutePrefixAttribute and System.Web.Mvc.RoutePrefixAttribute!
// Depending on your controller implementation, either one or the other might be used... checking against typeof(RoutePrefixAttribute)
// without identifying which one will sometimes succeed, sometimes fail.
// Since this implementation is generic, I'm handling both cases. Preference would be to extend System.Web.Mvc and System.Web.Http
var baseRoutePrefix = Attribute.GetCustomAttribute(baseControllerType, typeof(System.Web.Http.RoutePrefixAttribute))
?? Attribute.GetCustomAttribute(baseControllerType, typeof(System.Web.Mvc.RoutePrefixAttribute));
if (baseRoutePrefix != null)
{
// A trailing slash is added by the system. Only add it if we're prefixing an existing string
var trailingSlash = string.IsNullOrEmpty(routePrefix) ? "" : "/";
// Prepend the base controller prefix
routePrefix = ((RoutePrefixAttribute)baseRoutePrefix).Prefix + trailingSlash + routePrefix;
}
// Traverse up the base hierarchy to check for all inherited prefixes
baseControllerType = baseControllerType.BaseType;
}
return routePrefix;
}
Примечания:
- Метод Attribute.GetCustomAttributes(сборка, тип, bool)
включает в себя "наследовать" логическое... но он игнорируется для этого метода
подпись. ARG! Потому что, если бы это сработало, мы могли бы отказаться от
цикл отражения... который переносит нас в следующую точку:
- Это пересекает иерархию наследования с отражением. Не идеально
из-за вызовов O (n) через отражение, но для моего
необходимо. Вы можете избавиться от цикла, если у вас есть только 1 или 2 уровня
наследования.
- В GOTCHA в коде, RoutePrefixAttribute
объявляется в System.Web.Http и в System.Web.Mvc. Они оба
наследуют непосредственно от Атрибута, и они оба реализуют свои собственные
Интерфейс IRoutePrefix (т.
System.Web.Http.RoutePrefixAttribute < - System.Web.Http.IRoutePrefix
а также
System.Web.Mvc.RoutePrefixAttribute < - System.Web.Mvc.IRoutePrefix).
Конечным результатом является то, что библиотека, используемая для объявления вашего контроллера
(web.mvc или web.http) - это библиотека, атрибут RoutePrefixAttribute
назначены. Это, конечно, имеет смысл, но я потерял 2 часа
код рефакторинга, который был фактически законным, потому что мой тестовый пример
неявно проверяется на System.Web.Http.RoutePrefixAttribute, но
контроллер был объявлен с помощью System.Web.Mvc... Следовательно, явное пространство имен в коде.
Ответ 3
Пробовал это в ASP.NET Web Api 2.2 (должен/мог также работать в MVC):
public class InheritedRoutePrefixDirectRouteProvider : DefaultDirectRouteProvider
{
protected override string GetRoutePrefix(HttpControllerDescriptor controllerDescriptor)
{
var sb = new StringBuilder(base.GetRoutePrefix(controllerDescriptor));
var baseType = controllerDescriptor.ControllerType.BaseType;
for (var t = baseType; typeof(ApiController).IsAssignableFrom(t); t = t.BaseType)
{
var a = (t as MemberInfo).GetCustomAttribute<RoutePrefixAttribute>(false);
if (a != null)
{
sb.Insert(0, $"{a.Prefix}{(sb.Length > 0 ? "/": "")}");
}
}
return sb.ToString();
}
}
Он связывает префиксы маршрута вместе в цепочке наследования контроллера.
Ответ 4
Можно подтвердить, что ответ Marlon работает в MVC, переключив типы веб-API на эквивалентные типы MVC:
public class InheritedRoutePrefixDirectRouteProvider : DefaultDirectRouteProvider
{
protected override string GetRoutePrefix(ControllerDescriptor controllerDescriptor)
{
var sb = new StringBuilder(base.GetRoutePrefix(controllerDescriptor));
var baseType = controllerDescriptor.ControllerType.BaseType;
for (var t = baseType; typeof(Controller).IsAssignableFrom(t); t = t.BaseType)
{
var a = (t as MemberInfo).GetCustomAttribute<RoutePrefixAttribute>(false);
if (a != null)
{
sb.Insert(0, $"{a.Prefix}{(sb.Length > 0 ? "/" : "")}");
}
}
return sb.ToString();
}
}
Ответ 5
Может быть, уже поздно, но я думаю, что этот атрибут базового контроллера заставит его работать:
[Route("account/[Controller]")]