2013-10-22 6 views
6

Ich habe mein nugget-Paket für die Web-API 2 von RC1 auf 5.0.0 aktualisiert und war sprachlos, als ich feststellte, dass HttpRouteBuilder, auf das früher zugegriffen wurde, intern erstellt wurde. Darüber hinaus gibt es keine Überladung mehr für HttpConfiguration.MapHttpAttributeRoutes, die HttpRouteBuilder als Argument verwendet. Warum?HttpRouteBuilder - Wohin ging es und warum?

Ich benutzte das, und es löst ein großes Problem in meinem Projekt. Was verwende ich stattdessen?

Hintergrund: Ich schreibe einen Server, der Attribute Routing für Web-API 2 verwendet. Ich implementiert eine Klasse, die von HttpRouteBuilder geerbt, so dass ich ein paar zusätzliche Pfadsegmente zu jedem URI injizieren konnte. Wenn beispielsweise der Standardroutenerbauer eine Route für // myserver/user/update erstellen würde, würde mein Routenersteller diese Route zu // myserver/{instanz}/user/update ändern. Ich wollte das automatisch machen, damit ich das nicht in jedem meiner HttpGet, HttpPost, etc. Attribute festhalten musste. Wie gehe ich jetzt mit dieser großen Veränderung um?

+0

Können Sie das Routenattribut 'RoutePrefix' oder' controller-level' verwenden, um die Angabe für jede Aktion zu minimieren? –

+0

Ja, ich könnte, aber mein großes Problem ist, dass mein Server Controller von Drittanbietern erlaubt, und ich möchte es nicht ihnen überlassen, die erforderlichen Segmente zu ihren Routen hinzuzufügen. Diese Segmente müssen hinzugefügt werden, unabhängig davon, ob sie sie angeben oder nicht. Mein Routengenerator war schlau genug, um sie nur hinzuzufügen, wenn nicht bereits angegeben. –

+0

Nur FYI ... in der nächsten Version von Web API (5.2) stellen wir Ihnen einen Hook zur Verfügung, mit dem Sie Ihr Szenario erreichen können ... Sie können sogar die neuen Nightly Builds ausprobieren ... das neue Schnittstelle heißt 'IDirectRouteProvider' –

Antwort

1

Das Internalling brach etwas, an dem ich arbeitete.

Eine change set made on August 21st 2013 machte diese api Änderung zu beheben this issue. Gemäß diesem Problem wurde die einzige Grundfunktionalität entfernt, um Web API näher zu MVCs API zu bringen. Keine besonders gute Begründung meiner Meinung nach.

Um meine Probleme zu lösen, habe ich eine benutzerdefinierte IHttpActionSelector abgeleitet von ApiControllerActionSelector implementiert. Ich hoffe, es wird nicht meine endgültige Lösung sein, da es wirklich viel zu viel Code für eine einfache Sache ist. Dieser Ansatz sollte auch für Ihr Problem funktionieren.

In meinem Projekt benötigt jede Route es gefunden wurde Baugruppe wonach modifiziert werden. In vereinfachter Code nach jeder Route mit /Api (vor einem Controller RoutePrefixAttribute falls vorhanden) vorangestellt wird.

Die tatsächliche 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); 
    } 
} 

Der Teil, der die Route ändert:

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; } 
    } 
} 

Der Kleber:

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; } 
    } 
} 

diese Funktionalität nutzen zu können, nur ersetzen Sie den IHttpActionSelector Service mit PrefixWithApiControllerActionSelector in der Konfig.

Wenn Sie eine sauberere Art der Dinge finden, posten Sie bitte Ihre Lösung!

Verwandte Themen