Asp.net mvc MVC.ApiExplorer未发现有效路由
当使用ASP.NET Web API帮助页和相关的MVC.ApiExplorer时,我有通过http访问但ApiExplorer未发现的有效路由。只有在使用常规路由规则时才能找到这些路由。使用更具体的规则(与常规规则结合使用)似乎会对ApiExplorer隐藏路由 在三条规则的示例中,两条路由与控制器方法上的GET和POST操作相关,该方法不使用查询参数Asp.net mvc MVC.ApiExplorer未发现有效路由,asp.net-mvc,asp.net-web-api,asp.net-mvc-routing,asp.net-mvc-apiexplorer,Asp.net Mvc,Asp.net Web Api,Asp.net Mvc Routing,Asp.net Mvc Apiexplorer,当使用ASP.NET Web API帮助页和相关的MVC.ApiExplorer时,我有通过http访问但ApiExplorer未发现的有效路由。只有在使用常规路由规则时才能找到这些路由。使用更具体的规则(与常规规则结合使用)似乎会对ApiExplorer隐藏路由 在三条规则的示例中,两条路由与控制器方法上的GET和POST操作相关,该方法不使用查询参数 public class SomeControllerController : ApiController { [HttpPost]
public class SomeControllerController : ApiController
{
[HttpPost] public HttpResponseMessage Post(PostObject value) { ... }
[HttpGet] public IEnumerable<DisplayObject> GetAll() { ... }
[HttpGet] public DisplayObject GetById(string id) { ... }
}
这些路由由Api资源管理器适当地发现,如下所示:
- 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资源管理器仅返回
- GET:api/somecontroller/{id}
我相信您看到的是ApiExplorer的一个已知错误。发生的情况是,ApiExplorer遍历route集合中的每个路由,并检查控制器及其操作是否可以解决
例如,在本例中,操作“GetById”可以通过上述两个路由进行探索,ApiExplorer错误地认为这会由于不明确的匹配而导致冲突,并尝试过滤掉重复的操作,在本例中,这会导致过滤/删除所有操作。由于这个bug在ApiExplorer中(它是WebAPI主核心的一部分),我担心我们不能很快修复它 虽然ASP.NET Web API团队没有修复此错误,但我正在使用自己的哑修复程序 我对
IApiExplorer
的扩展方法与ApiExplorer
类中的原始apisdescriptions
实现做了相同的事情,但它没有删除不同路由的重复操作,而是返回具有不同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);
}
}
HttpConfiguration
为可测试性添加了参数。如果您不关心它,请删除该参数,直接在扩展方法中使用GlobalConfiguration.HttpConfiguration
。我刚刚验证了删除“apisdefault”规则并保留“ApiSomeControllerDefault”规则确实使一度隐藏的路由可供ApiExplorer使用。您是如何删除ApiDefault规则的?非常感谢。这个答案解决了我的问题后,这么多的斗争与框架!!!
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);