Допустимые маршруты не обнаружены MVC.ApiExplorer

При использовании страницы справки ASP.NET Web API и соответствующего MVC.ApiExplorer у меня есть действительные маршруты, которые доступны через http, но не обнаружены ApiExplorer. Эти маршруты обнаруживаются только при использовании общего правила маршрутизации. Использование более конкретного правила (в сочетании с общим), по-видимому, скрывает маршруты от ApiExplorer.

В примере из трех правил два маршрута относятся к GET и POST-действию для метода контроллера, который не принимает параметры запроса, переходят в MIA.

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

При использовании правила маршрутизации

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

Api Explorer соответственно обнаруживает маршруты

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

все же при добавлении менее общего и более значимого правила

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 только возвращает

  • GET: api/somecontroller/{id}

Что является причиной того, что некоторые из моих маршрутов не могут быть найдены?

РЕДАКТИРОВАТЬ Ссылка на отчет о проблеме на странице проекта ApiExplorer

2 ответа

Я считаю, что вы видите, это известная ошибка с ApiExplorer. Происходит то, что ApiExplorer проходит через каждый маршрут в коллекции маршрутов и проверяет, может ли контроллер и его действия быть разрешены.

В этом случае, например, действие "GetById" может быть исследовано обоими вышеупомянутыми маршрутами, которые ApiExplorer ошибочно полагает, что вызывает конфликт из-за неоднозначного сопоставления, и пытается отфильтровать повторяющиеся действия, которые в этом случае вызывают все действия, которые будут отфильтрованы / удалены. Так как эта ошибка в ApiExplorer(который является частью основного ядра WebAPI), я боюсь, что мы не сможем исправить это в ближайшее время.

Хотя эта ошибка не исправлена ​​командой веб-API ASP.NET, я использую свое собственное глупое исправление.

Мой метод расширения для IApiExplorer делает то же самое, что и оригинал ApiDescriptions реализация в ApiExplorer класс, но вместо удаления повторяющихся действий для разных маршрутов, он просто возвращает действия с отличным идентификатором (HTTP-метод + маршрут). Таким образом, он возвращает все объявленные действия, независимо от количества маршрутов.

И да, он бесстыдно использует рефлексию для вызова приватного метода.

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

Это легко использовать:

var apiDescriptions = apiExplorer.GetAllApiDescriptions(httpConfig);

HttpConfiguration параметр добавлен для проверки. Если вас это не волнует, удалите параметр и просто используйте GlobalConfiguration.HttpConfiguration в методе расширения напрямую.

Другие вопросы по тегам