【问题标题】:How can versioning be done in ASP.NET Core Web Api如何在 ASP.NET Core Web Api 中进行版本控制
【发布时间】:2016-11-13 06:48:29
【问题描述】:

在之前的asp.net web api 中,我实现了DefaultHttpControllerSelector 来指定我希望请求如何定位我的控制器。我经常有不同名称的不同控制器,但用于相同的进程。唯一的区别是一个版本比另一个版本高。

例如,我可以有一个名为 BookingV1Controller 的控制器,用于处理服务的版本一。我也会有BookingV2Controller,它旨在处理服务的第二版。然后,客户端应用程序将使用此 URL http://myservice.com/api/v2/booking/someaction?id=12 向服务发出请求。为了处理请求,我将提供DefaultHttpControllerSelector 的自定义实现,以根据请求的版本选择所需的控制器的适当版本。

但是,我似乎没有办法在 ASP.NET Core 中执行此操作。我到处搜索都无济于事。也没有可以提供帮助的文档。

如果有人可以在这里帮助我,我将不胜感激。谢谢。

更新 如果在自定义标头中指定了版本,我还想知道该怎么做。例如X-Version:v1

更新 2

要求是服务的版本不应在 URL 中公开。如果不存在版本,则服务将返回有关如何添加版本的说明。如果请求的控制器不存在于请求的版本中,系统将搜索较低的版本。如果它在任何较低版本中找到它,它就会使用它。这样做的原因是为了防止在所有版本上重复控制器。但是对于 ASP.NET Core,这可能是不可能的。

【问题讨论】:

  • 使用标头进行版本控制可能不是最好的方法。没有发送标头时会发生什么 - 它会默认为 v1 还是最新版本(或错误)?测试有多容易?
  • @Evan Mulawski,要求版本不应在 URL 中公开。如果不存在版本,则服务将返回有关如何添加版本的说明。如果请求的控制器不存在于请求的版本中,系统将搜索较低的版本。如果它在任何较低版本中找到它,它就会使用它。这样做的原因是为了防止在所有版本上重复控制器。但是对于 ASP.NET Core,这可能是不可能的。

标签: c# asp.net asp.net-web-api asp.net-core


【解决方案1】:

使用路由属性控制版本。

[Route("api/v1/[controller]")]
public class BookingV1Controller : Controller
{
  ....
}

[Route("api/v2/[controller]")]
public class BookingV2Controller : Controller
{
  ....
}

有关从标准 Web Api 和 .NET Core ASP.NET 迁移的更多信息,请查看:MSDN: Migrating from ASP.NET Web Api

【讨论】:

  • 如果在自定义标头中指定版本可以使用什么
  • 抱歉,没有以这种方式进行版本控制,因为最佳做法通常是让 URL 确定资源位置。
  • 如果您这样做,您最终不会在 URL 中获得两次版本吗? api/v1/BookingV1api/v2/BookingV2。您需要以这种方式手动将路由写入[Route("api/v1/Booking")][Route("api/v2/Booking")]。还是我在这里错了?这就是我必须这样做的方式。
  • 通常,如果您这样做,您也会在方法上使用适当的属性。类级别的属性由类方法继承。如果您确实希望我们将“BookingController”作为唯一的 Controller 类名,则还需要使用 C# 类命名空间来帮助进行版本控制。
【解决方案2】:

我为此问题专门研究了几天后,为此目的创建了一个包。它不需要属性。

https://github.com/GoAheadTours/NamespaceVersioning

总而言之,您可以在启动文件中注册一个 IApplicationModelConvention,它可以遍历控制器并根据命名空间注册路由。我创建了一个 v1 文件夹,并将我的控制器放入其中

实现 IApplicationModelConvention 的类实现了一个带有 ApplicationModel 参数的 Apply 方法,该参数将有权访问您应用中的控制器及其现有路由。如果我看到控制器没有在我的类中设置路由,我会从命名空间获取版本并使用预定义的 URL 前缀为该版本生成路由。

public void Apply(ApplicationModel application) {
    foreach (var controller in application.Controllers) {
        var hasRouteAttribute = controller.Selectors.Any(x => x.AttributeRouteModel != null);
        if (hasRouteAttribute) {
            continue;
        }
        var nameSpace = controller.ControllerType.Namespace.Split('.');
        var version = nameSpace.FirstOrDefault(x => Regex.IsMatch(x, @"[v][\d*]"));
        if (string.IsNullOrEmpty(version)) {
            continue;
        }
        controller.Selectors[0].AttributeRouteModel = new AttributeRouteModel() {
            Template = string.Format(urlTemplate, apiPrefix, version, controller.ControllerName)
        };
    }
}

我在 github 上提供了所有代码,并在 nuget 上提供了包的链接

【讨论】:

  • @PeterLazziarino 非常感谢会看一看。如果有效,我会将其标记为正确答案。为深入挖掘所花费的时间 +1 点赞。
  • @shittujosepholugbenga 谢谢!我很感激。我有一个工作示例,如果您需要任何进一步的帮助,请告诉我。
  • @PeterLazzarino 这个包救了我的命。在我与团队一起构建的微服务中使用它。
  • @PeterLazzarino 链接离线
【解决方案3】:

这是我偶然发现的一个非常古老的问题,但现在有更好的解决方案。有这个包

Microsoft.AspNetCore.Mvc.Versioning

它具有更丰富的实现版本控制的方式。其中包括能够使用 URL 查询字符串、url 路径、标题或自定义版本阅读器。能够从 HTTPContext 等读取版本。

简而言之,您将以下内容添加到 startup.cs 中的 ConfigureServices 方法中

services.AddApiVersioning(o => {
    o.ReportApiVersions = true;
    o.AssumeDefaultVersionWhenUnspecified = true;
            o.DefaultApiVersion = new ApiVersion(1, 0);
});

然后你必须用 ApiVersion 来装饰你的控制器。

[ApiVersion("1.0")]
[Route("api/home")]
public class HomeV1Controller : Controller
{
    [HttpGet]
    public string Get() => "Version 1";
}

[ApiVersion("2.0")]
[Route("api/home")]
public class HomeV2Controller : Controller
{
    [HttpGet]
    public string Get() => "Version 2";
}

你也可以通过把它放在路由中来实现它。

[ApiVersion("1.0")]
[Route("api/{version:apiVersion}/home")]
public class HomeV1Controller : Controller
{
    [HttpGet]
    public string Get() => "Version 1";
}

[ApiVersion("2.0")]
[Route("api/{version:apiVersion}/home")]
public class HomeV2Controller : Controller
{
    [HttpGet]
    public string Get() => "Version 2";
}

当您采用这种通过 Microsoft 包实际实现的方法时,这也意味着您能够弃用版本、进行版本发现、轻松地从 HttpContext 访问版本号等。这些都不是您真正可以做到的如果它只是在您的路线中硬编码,请执行此操作。

更多信息(包括在标题中使用):

【讨论】:

  • 这是正确答案。但是,此包不支持 ASP.NET Core 1.1。在this GitHub issue 中了解更多信息。
  • Web APIMVC 结合使用时,ApiVersioning 似乎无法正常工作。 ApiVerionNeutral 无济于事。我的 MVC 项目也得到了版本控制和混乱。
【解决方案4】:

为此将服务 API 版本控制添加到您的 ASP.NET Core 应用程序

  public void ConfigureServices( IServiceCollection services )
    {
        services.AddMvc();
        services.AddApiVersioning();

        // remaining other stuff omitted for brevity
    }

查询字符串参数版本

[ApiVersion( "2.0" )]
[Route( "api/helloworld" )]
public class HelloWorld2Controller : Controller {
    [HttpGet]
    public string Get() => "Hello world!";
}

所以这意味着在另一个具有相同路由的控制器中获得 2.0 超过 1.0,你应该去这里:

/api/helloworld?api-version=2.0

我们可以在不同的命名空间中使用相同的控制器名称

网址路径段版本

 [ApiVersion( "1.0" )]
 [Route( "api/v{version:apiVersion}/[controller]" )]
 public class HelloWorldController : Controller {
    public string Get() => "Hello world!";
 }
[ApiVersion( "2.0" )]
[ApiVersion( "3.0" )]
[Route( "api/v{version:apiVersion}/helloworld" )]
public class HelloWorld2Controller : Controller {
    [HttpGet]
    public string Get() => "Hello world v2!";

    [HttpGet, MapToApiVersion( "3.0" )]
    public string GetV3() => "Hello world v3!";
}

标头版本控制

  public void ConfigureServices( IServiceCollection services )
    {
        services.AddMvc();
        services.AddApiVersioning(o => o.ApiVersionReader = new HeaderApiVersionReader("api-version"));
    }

当您执行 HeaderApiVersioning 时,您将无法仅在浏览器中执行 GET,因此我将使用 Postman 添加标头(或者我可以使用 Curl、WGet、PowerShell 或单元测试) :

Image

请参考https://www.hanselman.com/blog/ASPNETCoreRESTfulWebAPIVersioningMadeEasy.aspx

【讨论】:

    猜你喜欢
    • 2020-10-19
    • 2018-02-21
    • 1970-01-01
    • 2017-03-18
    • 1970-01-01
    • 2016-12-17
    • 1970-01-01
    • 2021-06-19
    • 1970-01-01
    相关资源
    最近更新 更多