9

Quando si utilizza ASP.NET Web API Aiuto Pagina e il relativo MVC.ApiExplorer ho percorsi validi che sono accessibili via HTTP ancora non sono rilevati da ApiExplorer. Queste rotte si trovano solo quando viene utilizzata una regola di routing generale. L'utilizzo di una regola più specifica (insieme a quella generale) sembra nascondere i percorsi da ApiExplorer.percorsi validi non scoperti da MVC.ApiExplorer

In un caso di esempio di tre regole, due route si riferiscono a un'azione GET e POST su un metodo controller che non accetta parametri di query passa a MIA.

public class SomeControllerController : ApiController 
{ 
    [HttpPost] public HttpResponseMessage Post(PostObject value) { ... } 
    [HttpGet] public IEnumerable<DisplayObject> GetAll() { ... } 
    [HttpGet] public DisplayObject GetById(string id) { ... } 
} 

Quando si utilizza una regola di routing di

routes.MapHttpRoute(
    name: "ApiDefault", 
    routeTemplate: "api/{controller}/{id}", 
    defaults: new 
       { 
        id = RouteParameter.Optional 
       } 
    ); 

I percorsi vengono scoperti in modo appropriato da Api Explorer come

  • POST: api/SomeController
  • GET: api/SomeController
  • GET: api/SomeController/{id}

ancora quando si aggiunge la regola meno generico e più significativo

routes.MapHttpRoute(
    name: "ApiSomeControllerDefault", 
    routeTemplate: "api/somecontroller/{id}", 
    defaults: new 
       { 
       controller = "SomeController", 
       id = RouteParameter.Optional 
       } 
    ); 

routes.MapHttpRoute(
    name: "ApiDefault", 
    routeTemplate: "api/{controller}/{id}", 
    defaults: new 
       { 
        id = RouteParameter.Optional 
       } 
    ); 

Api Explorer restituisce solo

  • GET: api/somecontroller/{id}

Qual è la causa alcuni dei miei percorsi non si trovano?

EDIT Link to Issue Report on ApiExplorer project page

risposta

5

Credo che quello che state vedendo è un bug noto con ApiExplorer. Quello che sta succedendo è che ApiExplorer passa attraverso ogni route nella raccolta del percorso e controlla se il controller e le sue azioni possono essere risolti.

In questo caso, per esempio, l'azione "GetById" può essere esplorato da entrambi i percorsi sopra, che ApiExplorer assume erroneamente essere la causa di un conflitto tramite la corrispondenza ambigua e cerca di filtrare azioni duplicati, che in questo caso sta causando il filtraggio/rimozione di tutte le azioni. Poiché questo bug si trova in ApiExplorer (che fa parte del core WebAPI principale), temo di non poterlo risolvere in qualsiasi momento.

+0

Ho appena verificato la rimozione della regola "ApiDefault" e la regola "ApiSomeControllerDefault" rende effettivamente disponibili i percorsi una volta nascosti a ApiExplorer. – rheone

+0

Come hai rimosso la regola ApiDefault? – user3654055

5

Mentre questo bug non è stato risolto dal team di API Web di ASP.NET, sto utilizzando la mia correzione stupida.

Il mio metodo di estensione per IApiExplorer sta facendo le stesse cose di originale ApiDescriptions implementazione in ApiExplorer classe, ma invece di rimuovere le azioni duplicate per percorsi diversi, si restituisce solo azioni con ID distinti (metodo HTTP + percorso). Quindi restituisce tutte le azioni dichiarate, indipendentemente dal numero di rotte.

E, sì, utilizza spensieratamente il riflesso per chiamare il metodo privato.

public static class WebApiExtensions 
{ 
    public static Collection<ApiDescription> GetAllApiDescriptions(this IApiExplorer apiExplorer, HttpConfiguration httpConfig) 
    { 
     if (!(apiExplorer is ApiExplorer)) 
     { 
      return apiExplorer.ApiDescriptions; 
     } 

     IList<ApiDescription> apiDescriptions = new Collection<ApiDescription>(); 
     var controllerSelector = httpConfig.Services.GetHttpControllerSelector(); 
     var controllerMappings = controllerSelector.GetControllerMapping(); 

     if (controllerMappings != null) 
     { 
      foreach (var route in httpConfig.Routes) 
      { 
       typeof(ApiExplorer).GetMethod("ExploreRouteControllers", 
        bindingAttr: BindingFlags.Instance | BindingFlags.NonPublic, 
        binder: null, 
        types: new[] {typeof(IDictionary<string, HttpControllerDescriptor>), typeof(IHttpRoute), typeof(Collection<ApiDescription>)}, 
        modifiers: null 
       ).Invoke(apiExplorer, new object[] {controllerMappings, route, apiDescriptions}); 
      } 

      apiDescriptions = apiDescriptions 
       .GroupBy(api => api.ID.ToLower()) 
       .Select(g => g.First()) 
       .ToList(); 
     } 

     return new Collection<ApiDescription>(apiDescriptions); 
    } 
} 

E 'facile da usare:

var apiDescriptions = apiExplorer.GetAllApiDescriptions(httpConfig); 

HttpConfiguration parametro aggiunto per testabilità. Se non ti interessa, rimuovi il parametro e usa semplicemente GlobalConfiguration.HttpConfiguration nel metodo di estensione direttamente.

+0

Grazie mille. Questa risposta ha risolto il mio problema dopo tanto combattimento con il framework !!! –

Problemi correlati