2013-10-22 42 views
6

我將Web API 2的nuget包從RC1升級到了5.0.0,並且發現原來可以訪問的HttpRouteBuilder是內部的。除此之外,HttpConfiguration.MapHttpAttributeRoutes不再承擔以HttpRouteBuilder爲參數的重載。爲什麼?HttpRouteBuilder - 它去了哪裏,爲什麼?

我正在使用它,它解決了我的項目中的一個主要問題。我用什麼來代替?

背景: 我正在編寫一個服務器,它使用Web API 2的屬性路由。我實現了一個繼承自HttpRouteBuilder的類,以便我可以爲每個URI注入幾個額外的路徑段。例如,如果默認路由生成器最終爲// myserver/user/update創建路由,則我的路由生成器會將該路由修改爲// myserver/{instance}/user/update。我希望這樣做是自動完成的,這樣我就不必在每一個數百個HttpGet,HttpPost等屬性中粘貼它。那麼現在我該如何處理這個重大改變?

+0

你可以使用'RoutePrefix'或'controller-level'路由屬性來最小化每一個動作的指定嗎? –

+0

是的,我可以,但我最大的問題是我的服務器允許第三方控制器,我不想留給他們添加所需的段到他們的路線。無論他們是否指定它們,都需要添加這些段。我的路線建設者非常聰明,只有在尚未指定的情況下才能添加它們。 –

+0

Just FYI ...在Web API(5.2)的下一個版本中,我們提供了一個使用它可以完成您的場景的鉤子...實際上,您也可以嘗試使用新的Nightly構建...新的接口被稱爲'IDirectRouteProvider' –

回答

1

這個國際化打破了我一直在努力的事情。

A change set made on August 21st 2013使這個API修改爲修復this issue。根據這個問題,刪除功能的唯一原因是讓Web API更接近MVC的API。在我看來,這不是一個特別好的理由。

要解決我的問題,我實施了從ApiControllerActionSelector派生的自定義IHttpActionSelector。我希望這不會是我最終的解決方案,因爲它對於一件簡單的事情來說確實是太多的代碼。這種方法也適用於你的問題。

在我的項目中,每條路線都需要根據發現哪個組件進行修改。在以下簡化代碼中,每條路線的前綴爲/Api(如果存在控制器的RoutePrefixAttribute之前)。

實際IHttpActionSelector

public class PrefixWithApiControllerActionSelector : WrappingApiControllerActionSelector { 
    protected override HttpActionDescriptor WrapHttpActionDescriptor(HttpActionDescriptor actionDescriptor) { 
     if (actionDescriptor is ReflectedHttpActionDescriptor) 
      return new PrefixWithApiReflectedHttpActionDescriptor((ReflectedHttpActionDescriptor)actionDescriptor); 
     return actionDescriptor; 
    } 
} 

public abstract class WrappingApiControllerActionSelector : ApiControllerActionSelector { 
    protected abstract HttpActionDescriptor WrapHttpActionDescriptor(HttpActionDescriptor actionDescriptor); 

    public override ILookup<string, HttpActionDescriptor> GetActionMapping(HttpControllerDescriptor controllerDescriptor) { 
     return base.GetActionMapping(controllerDescriptor).SelectMany(grouping => { 
      return grouping.Select(actionDescriptor => new KeyValuePair<string, HttpActionDescriptor>(grouping.Key, WrapHttpActionDescriptor(actionDescriptor))); 
     }).ToLookup(_ => _.Key, _ => _.Value); 
    } 
} 

改變路線的部分:

public class PrefixWithApiHttpRouteInfoProvider : WrappedHttpRouteInfoProvider { 
    public PrefixWithApiHttpRouteInfoProvider(IHttpRouteInfoProvider infoProvider, HttpControllerDescriptor controllerDescriptor) : base(infoProvider, controllerDescriptor) { } 

    public override string Template { 
     get { 
      var parts = new List<string>(); 
      parts.Add("Api"); 

      var prefix = ControllerDescriptor.GetCustomAttributes<RoutePrefixAttribute>().FirstOrDefault(); 
      if (prefix != null && !string.IsNullOrEmpty(prefix.Prefix)) { 
       parts.Add(prefix.Prefix); 
      } 

      if (!string.IsNullOrEmpty(InfoProvider.Template)) { 
       parts.Add(InfoProvider.Template); 
      } 

      var route = "~/" + string.Join("/", parts); 
      if (route.Length > 2 && route.EndsWith("/", StringComparison.Ordinal)) { 
       route = route.Substring(0, route.Length - 1); 
      } 
      return route; 
     } 
    } 
} 

public abstract class WrappedHttpRouteInfoProvider : IHttpRouteInfoProvider { 
    private readonly IHttpRouteInfoProvider _infoProvider; 
    private readonly HttpControllerDescriptor _controllerDescriptor; 

    protected WrappedHttpRouteInfoProvider(IHttpRouteInfoProvider infoProvider, HttpControllerDescriptor controllerDescriptor) { 
     _infoProvider = infoProvider; 
     _controllerDescriptor = controllerDescriptor; 
    } 

    public virtual string Name { 
     get { return InfoProvider.Name; } 
    } 

    public virtual string Template { 
     get { return _infoProvider.Template; } 
    } 

    public virtual int Order { 
     get { return InfoProvider.Order; } 
    } 

    protected HttpControllerDescriptor ControllerDescriptor { 
     get { return _controllerDescriptor; } 
    } 

    protected IHttpRouteInfoProvider InfoProvider { 
     get { return _infoProvider; } 
    } 
} 

膠:

public class PrefixWithApiReflectedHttpActionDescriptor : WrappedReflectedHttpActionDescriptor { 
    public PrefixWithApiReflectedHttpActionDescriptor(ReflectedHttpActionDescriptor descriptor) : base(descriptor) {} 

    public override Collection<T> GetCustomAttributes<T>(bool inherit) { 
     if (typeof(T) == typeof(IHttpRouteInfoProvider)) { 
      var attributes = Descriptor.GetCustomAttributes<T>(inherit).Cast<IHttpRouteInfoProvider>().Select(_ => new PrefixWithApiHttpRouteInfoProvider(_, Descriptor.ControllerDescriptor)); 
      return new Collection<T>(attributes.Cast<T>().ToList()); 
     } 
     return Descriptor.GetCustomAttributes<T>(inherit); 
    } 

    public override Collection<T> GetCustomAttributes<T>() { 
     if (typeof(T) == typeof(IHttpRouteInfoProvider)) { 
      var attributes = Descriptor.GetCustomAttributes<T>().Cast<IHttpRouteInfoProvider>().Select(_ => new PrefixWithApiHttpRouteInfoProvider(_, Descriptor.ControllerDescriptor)); 
      return new Collection<T>(attributes.Cast<T>().ToList()); 
     } 
     return Descriptor.GetCustomAttributes<T>(); 
    } 
} 

public abstract class WrappedReflectedHttpActionDescriptor : ReflectedHttpActionDescriptor { 
    private readonly ReflectedHttpActionDescriptor _descriptor; 

    protected WrappedReflectedHttpActionDescriptor(ReflectedHttpActionDescriptor descriptor) : base(descriptor.ControllerDescriptor, descriptor.MethodInfo) { 
     _descriptor = descriptor; 
    } 

    public override HttpActionBinding ActionBinding { 
     get { return Descriptor.ActionBinding; } 
     set { Descriptor.ActionBinding = value; } 
    } 

    public override Collection<T> GetCustomAttributes<T>(bool inherit) { 
     return Descriptor.GetCustomAttributes<T>(inherit); 
    } 

    public override Collection<T> GetCustomAttributes<T>() { 
     return Descriptor.GetCustomAttributes<T>(); 
    } 

    public override Collection<System.Web.Http.Filters.FilterInfo> GetFilterPipeline() { 
     return Descriptor.GetFilterPipeline(); 
    } 

    public override Collection<System.Web.Http.Filters.IFilter> GetFilters() { 
     return Descriptor.GetFilters(); 
    } 

    public override System.Collections.Concurrent.ConcurrentDictionary<object, object> Properties { 
     get { return Descriptor.Properties; } 
    } 

    public override IActionResultConverter ResultConverter { 
     get { return Descriptor.ResultConverter; } 
    } 

    public override Collection<HttpMethod> SupportedHttpMethods { 
     get { return Descriptor.SupportedHttpMethods; } 
    } 

    public override Collection<HttpParameterDescriptor> GetParameters() { 
     return Descriptor.GetParameters(); 
    } 

    public override Task<object> ExecuteAsync(HttpControllerContext controllerContext, IDictionary<string, object> arguments, CancellationToken cancellationToken) { 
     return Descriptor.ExecuteAsync(controllerContext, arguments, cancellationToken); 
    } 

    public override string ActionName { 
     get { return Descriptor.ActionName; } 
    } 

    public override Type ReturnType { 
     get { return Descriptor.ReturnType; } 
    } 

    protected ReflectedHttpActionDescriptor Descriptor { 
     get { return _descriptor; } 
    } 
} 

要使用此功能,只需用PrefixWithApiControllerActionSelector替代IHttpActionSelector服務在配置中。

如果您發現一種更乾淨的做事方式,請發佈您的解決方案!