Допустимые маршруты не обнаружены 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
в методе расширения напрямую.