首页 > 解决方案 > ASP.NET Web Api 2 控制器版本控制。找不到路线

问题描述

我有已经有控制器的 ASP.NET Web Api 2 应用程序。现在,我们有新的控制器需要添加但带有前缀 (v10)

/api/products/1          // Old controller
/api/v1/proucts/1        // the new controller

我尝试使用 ApiVersion 属性对 API 进行版本控制:

[ControllerName("Products")]
[ApiVersion("1.0")]
[RoutePrefix("api/v10/[controller]")]
public class ProductsV1Controller : ApiController
{
...
}

旧控制器是:

public class ProductsController : ApiController
{
...
}

没有版本的路由仍在工作并访问旧控制器,但是当我调用此路由时:

api/v10/产品/1

它返回 404 Page not found。两个控制器中都编写了相同的 get 方法,仅用于测试目的。

在我的启动配置中:

httpConfig.MapHttpAttributeRoutes();

httpConfig.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional },
            constraints: null);

有谁知道如何配置路由以正确导航?

标签: asp.netasp.net-web-apiapi-versioning

解决方案


@Sajid基本上是正确的。当您按 URL 段进行版本控制时,您需要使用 API 版本路由约束。有几个问题在起作用。看起来您正在从未版本化的 API 迁移到版本的API,这是受支持的场景。您提供的示例值不一致。看起来你要从v1v10。这些值无关紧要,但要意识到您现有的 API 确实有一些逻辑名称,即使您之前从未为其分配过值。

第一期

你没有指定你的设置,但它应该是这样的:

var constraintResolver = new DefaultInlineConstraintResolver()
{
  ConstraintMap = { ["apiVersion"] = typeof( ApiVersionRouteConstraint ) }
};
configuration.MapHttpAttributeRoutes( constraintResolver );
configuration.AddApiVersioning(
  options =>
  {
    // required because the original API doesn't have a version in the URL
    options.AssumeDefaultVersionWhenUnspecified = true;

    // this is already the default value, but you might need to change it.
    // this is the value that will be 'assumed' by the original, unversioned
    // API. it is addressable by /api/products. if you meant to start at 1.0,
    // then you can use a lower value such as 0.9
    options.DefaultApiVersion = new ApiVersion(1, 0);
  });

第 2 期

[controller]Web API 不支持该令牌。这是一个概念,与[action]ASP.NET Core 路由一起被引入。

您没有完全详细说明您是如何解决问题的,但您的更改几乎可以肯定地突出了混合样式是如何令人困惑的。目前尚不清楚 API 是否与属性路由或基于约定的路由匹配。

第 3 期

您的路由模板不包含 API 版本路由约束(请参阅问题 1 进行注册)。这是必需的,以便 API 版本控制知道如何从 URL 中提取 API 版本。API 版本控制不使用正​​则表达式等进行魔术字符串解析。

模板应该是:[RoutePrefix("api/v{version:apiVersion}/products")]如@Sajid 所述。请注意,文字v不是 API 版本的一部分,但在 URL 段版本控制方法中很常见。version是路由参数的名称,但可以是您想要的任何值。apiVersion是注册路由约束的key,但是如果你在注册中也改了也可以在模板里改(见Issue 1)。

第 4 期

带注释的 API 版本是1.0,但路由模板似乎暗示您的意思10.0。你可以使用任何你想要的值,但它们必须是一致的。如果您确实打算使用1.0,请不要忘记更改,options.DefaultApiVersion否则您将获得运行时异常,因为原始 API 和您的新 API 将被视为重复,因为它们具有相同的 API 版本。一旦您选择加入,就没有没有 API 版本的概念。

例如:

// ~/api/v10/products
[ApiVersion("10.0")]
[RoutePrefix("api/v{version:apiVersion}/products")]
public class ProductsV1Controller : ApiController { }

第 5 期

强烈建议不要混合路由样式。它是受支持的,但它可能会使故障排除和维护人员感到困惑。我会选择Direct Routing (aka Attribute Routing ) 或Convention-Based Routing。在某些极端情况下混合它们不起作用,但它们很少见。

这意味着您应该选择以下选项之一:

属性路由
// setup (see Issue 1)
configuration.MapHttpAttributeRoutes( constraintResolver );

// old controller
[RoutePrefix("api/products")]
public class ProductsController : ApiController { }

// new controller; ControllerNameAttribute isn't need because it's not used
[ApiVersion("10.0")]
[RoutePrefix("api/v{version:apiVersion}/products")]
public class ProductsV1Controller : ApiController { }
基于约定的路由
// setup
httpConfig.Routes.MapHttpRoute(
  name: "VersionedApi",
  routeTemplate: "api/v{apiVersion}/{controller}/{id}",
  defaults: new { id = RouteParameter.Optional },
  constraints: new { apiVersion = new ApiVersionRouteConstraint() });

httpConfig.Routes.MapHttpRoute(
  name: "DefaultApi",
  routeTemplate: "api/{controller}/{id}",
  defaults: new { id = RouteParameter.Optional },
  constraints: null);

// old controller
public class ProductsController : ApiController { }

// new controller
[ApiVersion("10.0")]
[ControllerName("Products")] // required because the convention-based name is "ProductsV1"
public class ProductsV1Controller : ApiController { }

警告:您可以混合使用这些样式,但故障排除会变得更加困难。您需要广泛的测试覆盖率来确保事情是正确的。一次性手动测试可能会产生误报。


我希望这会有所帮助


推荐阅读