9

使用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 适当地发现为

  • 发布: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 项目页面上问题报告的链接

4

2 回答 2

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直接在扩展方法中使用。

于 2013-03-17T19:28:05.813 回答
5

我相信您所看到的是 ApiExplorer 的一个已知错误。发生的事情是 ApiExplorer 遍历路由集合中的每个路由并检查控制器及其操作是否可以解决。

在这种情况下,例如,操作“GetById”可以通过上述两种路由进行探索,ApiExplorer 错误地认为由于不明确的匹配而导致冲突,并尝试过滤掉重复的操作,在这种情况下导致所有要过滤/删除的操作。由于这个错误在 ApiExplorer(它是主要 WebAPI 核心的一部分)中,恐怕我们不能很快修复它。

于 2013-01-25T01:51:24.133 回答