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!
È possibile utilizzare l'attributo di percorso 'RoutePrefix' o' controller-level' per minimizzare la specifica di ciascuna azione? –
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. –
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' –