有没有办法实现以下目标:
- Controller 使用 major.minor 模式通过 ApiVersionAttribute 进行版本控制,因此 1.1、2.3 等等里>
- 相应的路由仅包含路径中的主要部分,例如/v1/WeatherForecast
我尝试将如下内容添加到为 ASP.NET Core Web API (.NET 6) 创建的默认项目中。
WeatherForecastController.cs:
[ApiController]
[ApiVersion("1.1")]
[Route("v1/[controller]")]
public class WeatherForecastController : ControllerBase
{
}
Program.cs
builder.Services.AddApiVersioning(
options =>
{
options.ReportApiVersions = true;
options.AssumeDefaultVersionWhenUnspecified = true;
});
现在我无法调用端点/v1/WeatherForecast,我收到错误:
{
"error": {
"code": "UnsupportedApiVersion",
"message": "The HTTP resource that matches the request URI 'https://localhost:7170/v1/WeatherForecast' is not supported.",
"innerError": null
}
}
谢谢
最佳答案
听起来就像您可能将API版本与版本的某些内部服务器概念混为一谈。 API 版本是您契约(Contract)的一部分。
根据设计,客户端不能请求 1.0
,而是获取 1.1
。这对客户来说是一种误导和困惑。作为服务器,您不能从客户端下面拉出地毯并期望它不会破坏客户端(除非您拥有两侧)。 向后兼容是一个谬论。服务器无法保证添加或删除数据属性不会破坏客户端。 可能不会,但你不能保证。
上面没有任何内容匹配的原因是 API 版本控制不会对您的路由模板做出任何假设或推理。如果您想要 URL 路径中的版本(不是 RESTful),那是您的选择,但您必须按照 @kanils_ 建议在模板中使用路由约束。它应该看起来像:
[ApiController]
[ApiVersion("1.1")]
[Route("v{version:apiVersion}/[controller]")]
public class WeatherForecastController : ControllerBase
{
}
您可以随意调用该参数,但通常使用 version
。现在这将启用 https://localhost:7170/v1.1/WeatherForecast
。 1.0
没有定义,因此 v1
将无法解析。
options.AssumeDefaultVersionWhenUnspecified = true
不会执行您认为它会执行的操作(很可能)。这是一个被严重滥用的功能。它的目的是在现有 API 正式提供正式版本之前支持其祖父。此行为只存在于一个版本中。如果不支持此功能,则所有不知道在请求中包含版本的现有客户端都会中断。路由模板不能有默认路由参数值,除非它位于模板的末尾。这意味着除非在 URL 中指定 API 版本,否则模板将永远匹配。通过 URL 段进行版本控制是唯一存在此问题的方法。
我不认可、同意或建议使用除显式 API 版本之外的任何内容,但这可以发挥作用。如果客户端请求 application/json
并希望它被理解,您就不会返回 application/xml
。 API 版本也不异常(exception)。由于您是通过 URL 段进行版本控制,因此这将需要双重路由注册。您需要一条中性路线作为捕获所有和每条特定路线。这将为那些想要稳定的东西的客户提供一个明确的 URL,并为其他所有东西提供一个 float 路由。如果一条路线是客户的移动目标,那么老实说,在我看来,它并不比没有版本控制好。
开头:
namespace V1
{
[ApiController]
[ApiVersion("1.0")]
[Route("v{version:apiVersion}/[controller]")]
public class WeatherForecastController : ControllerBase
{
}
}
namespace V1_1
{
[ApiController]
[ApiVersion("1.1")]
[Route("[controller]")] // ← 2nd 'floating' route template
[Route("v{version:apiVersion}/[controller]")]
public class WeatherForecastController : ControllerBase
{
}
}
在配置中添加:
builder.Services.AddApiVersioning(
options =>
{
options.ReportApiVersions = true;
options.AssumeDefaultVersionWhenUnspecified = true;
// OPTION 1: explicitly set the default value;
// this is the 'assumed' version by default.
// the default value is new ApiVersion(1.0)
//
// options.DefaultApiVersion = new ApiVersion(1.1);
// OPTION 2: most people using this setup want to 'float'
// an API to the most current version available
//
options.ApiVersionReader =
new CurrentImplementationApiVersionSelector(options);
});
当客户端请求 WeatherForecast
时,请求将定向到与 v1.1/WeatherForecast
相同的位置。这是因为:
- 未从模板中解析任何 API 版本
AssumeDefaultVersionWhenUnspecified = true
CurrentImplementationApiVersionSelector
将1.1
解析为该 API 的当前(例如最高)可用版本
当您最终添加新版本(例如 1.2
或 2.0
)时,您将必须移动 float [Route("[controller]") ]
将模板从旧的当前 Controller 路由到新的 Controller 。可能有更方便的方法来管理它,但这种方法肯定有效。
由于文字 /v1
未知或无法被 API 版本控制识别,因此您可能可以将其与此方法结合使用,并且它会起作用,但恕我直言,这是一个令人头疼的问题.
在所有这些配置中,您不能(也不应该)消除显式路由,但您可以实现隐式路由目标。缺少文档可能会隐藏它们。
关于c# - ASP.NET Core API 版本控制具有 Major.minor 但仅在路径中包含 Major,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74235911/