使用 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将路由适当地发现为
然后添加不太通用且更有意义的规则
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只返回
是什么导致我的某些路线无法找到?
答案 0 :(得分:5)
我相信你所看到的是ApiExplorer的一个已知错误。发生的事情是ApiExplorer遍历路由集合中的每个路由,并检查控制器及其操作是否可以解决。
在这种情况下,例如,上述两条路线都可以探索“GetById”动作,ApiExplorer错误地假定由于模糊匹配而导致冲突,并尝试过滤掉重复动作,在这种情况下导致所有操作被过滤/删除。由于这个错误发生在ApiExplorer(它是主要的WebAPI核心的一部分),我担心我们不能很快修复它。
答案 1 :(得分:5)
虽然ASP.NET Web API团队没有修复此错误,但我正在使用自己的哑修复程序。
IApiExplorer
的扩展方法与ApiDescriptions
类中的原始ApiExplorer
实现相同,但不是删除不同路由的重复操作,而是返回具有不同ID的操作(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
。