使用 RouteAttribute 或隐式路由时的 WebAPI 路径差异



我一直在使用 WebApi,我试图了解使用 Route 属性与让 WebApi 根据函数名称自动创建路由的区别是如何工作的。我有很多这种形式的控制器:

public class MyController : RestControllerBase
{
    public async Task<HttpResponseMessage> GetData()
    {
        return _data;
    }
    [Route(ApiVersion.Version + "/MyController/{param}/specific/route")]
    public async Task<HttpResponseMessage> GetMoreData()
    {
        return _moreData;
    }
}

此控制器和其他控制器都指定了一个路由注册表,该注册表执行以下操作:

routingManager.RegisterVersionedHttpRoute(
                routeName: "MyController",
                routeTemplate: "MyController/{param}",
                defaults: new { controller = "MyController", param = RouteParameter.Optional },
                version: ApiVersion.Version);

当我构建和测试 API 时,两个端点都可以正确访问。

我还用一个基于命名空间的 HttpControllerSelector 覆盖了 HttpControllerSelector(用于版本控制)。

我的问题是,除了由特定 http 路由调用的传统功能外,这两个路由的行为不同。例如,在我被覆盖的控制器选择器中,我必须编写此逻辑,以便控制器和路由正确映射:

private string GetControllerName(IHttpRouteData routeData)
{
    if (routeData.GetSubRoutes() != null)
    {
        // With route attribute
        var subroute = routeData.GetSubRoutes().FirstOrDefault();
        var dataTokenValue = subroute.Route.DataTokens["actions"];
        if (dataTokenValue == null) return null;
        var controllername = ((HttpActionDescriptor[])dataTokenValue).First().ControllerDescriptor.ControllerName.Replace("Controller", string.Empty);
        return controllername;
    }
    else
    {
        // Try Strategy for without route attribute.
        return (String)routeData.Values["controller"];
    }
}
private string GetVersion(IHttpRouteData routeData)
{
    var routeTemplate = "";
    if (routeData.GetSubRoutes() != null)
    {
        var subRouteData = routeData.GetSubRoutes().FirstOrDefault();
        if (subRouteData == null) return null;
        routeTemplate = subRouteData.Route.RouteTemplate;
    }
    else
    {
        routeTemplate = routeData.Route.RouteTemplate;
    }
    var index = routeTemplate.IndexOf("/");
    if (index == -1)
    {
        return Unversioned;
    }
    var version = routeTemplate.Substring(0, index).ToUpperInvariant();
    return version;
}

似乎具有显式属性的路由将具有路由。GetSubroutes() 可用,而隐式路由则不可用。这导致我编写两种不同的方法来从传入请求中提取版本和控制器名称,然后才能正确路由它们。我可以接受这一点,但 WebApi 内部的某些东西正在路由之间创建这种区别,这确实让我感到困扰。

不幸的是,我随后开始与 https://github.com/domaindrivendev/Swashbuckle 合作来记录我的 api,并遇到了类似的问题。对于具有 Route 属性的路由,招摇页面会正确生成,但完全忽略隐式路由。我怀疑导致此问题的问题与导致我的选择逻辑中的分支的问题相同。

我一直在挖掘尽可能多的 ApiExplorer、WebAPI 和其他文档,但我无法找到使隐式/显式路由方法同质化的解决方案。这甚至可能是其实现中的错误。

我应该从这里去哪里?

所以这里的答案是与Web Api 2存在错误或不一致若要验证,可以运行此代码片段。在我提到的案例中,ApiExplorer 不会返回未显式标记路由属性的路径的结果。

        IApiExplorer apiExplorer = configuration.Services.GetApiExplorer();
        foreach (ApiDescription api in apiExplorer.ApiDescriptions)
        {
            Console.WriteLine("Uri path: {0}", api.RelativePath);
            Console.WriteLine("HTTP method: {0}", api.HttpMethod);
            foreach (ApiParameterDescription parameter in api.ParameterDescriptions)
            {
                Console.WriteLine("Parameter: {0} - {1}", parameter.Name, parameter.Source);
            }
            Console.WriteLine();
        }

最新更新