2013-10-22 6 views
6

Ho aggiornato il mio pacchetto nuget per l'API Web 2 da RC1 a 5.0.0 ed è stato sconcertato nel constatare che HttpRouteBuilder, che prima era accessibile, è stato reso interno. Insieme a questo, non c'è più un sovraccarico per HttpConfiguration.MapHttpAttributeRoutes che accetta HttpRouteBuilder come argomento. Perché?HttpRouteBuilder - Dove è andato e perché?

Stavo usando quello, e risolve un problema importante nel mio progetto. Cosa uso invece?

Background: Sto scrivendo un server che utilizza Attributo di routing per il Web API 2. ho implementato una classe che eredita da HttpRouteBuilder modo che io possa iniettare un paio di segmenti di percorso in più per ogni URI. Ad esempio, se il generatore di route predefinito ha terminato la creazione di una route per // myserver/user/update, il mio routebuilder avrebbe modificato tale route su // myserver/{istanza}/utente/aggiornamento. Volevo che fosse fatto automaticamente in modo da non doverlo inserire in ognuna delle mie centinaia di attributi HttpGet, HttpPost, ecc. Quindi, come posso gestirlo con questo importante cambiamento?

+0

È possibile utilizzare l'attributo di percorso 'RoutePrefix' o' controller-level' per minimizzare la specifica di ciascuna azione? –

+0

Sì, potrei, ma il mio grosso problema è che il mio server consente controller di terze parti, e non voglio lasciarlo a loro per aggiungere i segmenti richiesti ai loro percorsi. Questi segmenti devono essere aggiunti indipendentemente dal fatto che vengano specificati. Il mio generatore di percorsi è stato abbastanza intelligente da aggiungerli solo se non già specificato. –

+0

Solo FYI ... nella prossima versione di Web API (5.2), stiamo fornendo un hook usando il quale puoi realizzare il tuo scenario ... infatti, puoi provare anche usando le nuove build Nightly ... il nuovo l'interfaccia si chiama 'IDirectRouteProvider' –

risposta

1

Questo internamento ha rotto qualcosa su cui stavo lavorando.

A change set made on August 21st 2013 ha modificato questa API per risolvere this issue. Secondo questo problema, l'unica ragione per cui la funzionalità è stata rimossa era quella di rendere Web Api più vicino all'api di MVC. Non è una giustificazione particolarmente buona secondo me.

Per risolvere i miei problemi, ho implementato un numero personalizzato IHttpActionSelector derivato da ApiControllerActionSelector. Spero che non sarà la mia soluzione finale in quanto è davvero troppo codice per una cosa semplice. Questo approccio dovrebbe funzionare anche per il tuo problema.

Nel mio progetto ogni route deve essere modificato in base al gruppo in cui è stato trovato. Nel seguente codice semplificato, ogni route è preceduto da /Api (prima di un controller RoutePrefixAttribute se presente).

L'attuale 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); 
    } 
} 

La parte che cambia il percorso:

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

La colla:

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

Per utilizzare questa funzionalità basta sostituire il servizio IHttpActionSelector con PrefixWithApiControllerActionSelector nella configurazione.

Se trovi un modo più pulito di fare le cose, per favore pubblica la tua soluzione!

Problemi correlati