Действительные маршруты, не обнаруженные MVC.ApiExplorer

При использовании страницы справки веб-API ASP.NET и связанного 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 как

  • СООБЩЕНИЕ: API/SomeController
  • ПОЛУЧИТЬ: API/SomeController
  • ПОЛУЧИТЬ: 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 возвращает только

  • ПОЛУЧИТЬ: api/somecontroller/{id}

Почему некоторые из моих маршрутов не могут быть найдены?

ИЗМЕНИТЬ Ссылка выпустить отчет на странице проекта ApiExplorer


person rheone    schedule 24.01.2013    source источник


Ответы (2)


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

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

person Kiran Challa    schedule 25.01.2013
comment
Я только что проверил удаление правила ApiDefault и оставление правила ApiSomeControllerDefault действительно делает некогда скрытые маршруты доступными для ApiExplorer. - person rheone; 25.01.2013
comment
Как вы удалили правило ApiDefault? - person user3654055; 28.04.2016

Хотя эта ошибка не исправлена ​​командой веб-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 напрямую в методе расширения.

person whyleee    schedule 17.03.2013
comment
Большое спасибо. Этот ответ решил мою проблему после стольких боев с фреймворком!!! - person Adam Levitt; 26.11.2013