Asp.net mvc MVC.ApiExplorer未发现有效路由

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]

当使用ASP.NET Web API帮助页和相关的MVC.ApiExplorer时,我有通过http访问但ApiExplorer未发现的有效路由。只有在使用常规路由规则时才能找到这些路由。使用更具体的规则(与常规规则结合使用)似乎会对ApiExplorer隐藏路由

在三条规则的示例中,两条路由与控制器方法上的GET和POST操作相关,该方法不使用查询参数

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