如何在WebApi中inheritance控制器类级别的路由前缀?

注意,我已经阅读了新的路由function作为WebApi 2.2的一部分,以允许inheritance路由。 但是,这似乎并没有解决我的特定问题。 它似乎解决了inheritance动作级别路由属性的问题,但没有解决在类级别定义的路由前缀。 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 GetAsync() { ... } } [RoutePrefix("google")] public class GoogleController : AccountControllerBase { [Route("bar")] public async Task GetAsync() { ... } } 

我希望inheritanceaccount路由前缀,因此在定义Facebook和Google控制器时,我会获得路由:

 ~/account/facebook/foo ~/account/google/bar 

目前,在没有基类的account部分的情况下定义路由。

我有类似的要求。 我做的是:

 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()); 

正如@HazardouS所说,@ Grbinho的答案是硬编码的。 借用这个直接路由inheritance的答案和@HazardouS,我写了这个对象

 public class InheritableDirectRouteProvider : DefaultDirectRouteProvider {} 

然后覆盖以下方法,希望RoutePrefixAttributeinheritance:

 protected override IReadOnlyList 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(true); } protected override IReadOnlyList GetActionRouteFactories(HttpActionDescriptor actionDescriptor) { // Inherit route attributes decorated on base class controller's actions return actionDescriptor.GetCustomAttributes(true); } 

遗憾的是,根据问题评论,RoutePrefixAttribute不会出现在工厂列表中。 我没有深入研究为什么,如果有人想深入研究这个问题 。 所以我保留了这些方法以备将来兼容,并覆盖了GetRoutePrefix方法,如下所示:

 protected override string GetRoutePrefix(HttpControllerDescriptor controllerDescriptor) { // Get the calling controller's route prefix var routePrefix = base.GetRoutePrefix(controllerDescriptor); // Iterate through each of the calling controller's base classes that inherit from HttpController var baseControllerType = controllerDescriptor.ControllerType.BaseType; while(typeof(IHttpController).IsAssignableFrom(baseControllerType)) { // Get the base controller's 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's prefix routePrefix = ((RoutePrefixAttribute)baseRoutePrefix).Prefix + trailingSlash + routePrefix; } // Traverse up the base hierarchy to check for all inherited prefixes baseControllerType = baseControllerType.BaseType; } return routePrefix; } 

笔记:

  1. Attribute.GetCustomAttributes(Assembly,Type,bool)方法包含一个“inherit”布尔值……但是这个方法签名被忽略了。 ARG! 因为如果它起作用,我们可以放弃reflection循环……这将我们带到下一点:
  2. 这会使用reflection遍历inheritance层次结构。 由于O(n)通过reflection调用,但不是理想的,但需要我的需要。 如果你只有1或2级inheritance,你可以摆脱循环。
  3. 根据代码中的GOTCHA,RoutePrefixAttribute在System.Web.Http和System.Web.Mvc中声明。 它们都直接从Attributeinheritance,它们都实现了自己的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声明的...因此显式名称空间在代码中。

在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(false); if (a != null) { sb.Insert(0, $"{a.Prefix}{(sb.Length > 0 ? "/": "")}"); } } return sb.ToString(); } } 

它在控制器inheritance链中将路由前缀链接在一起。