Ultimate ASP.NET Core Web API 24 VERSIONING APIS

24 VERSIONING APIS
24 版本控制 API

As our project grows, so does our knowledge; therefore, we have a better understanding of how to improve our system. Moreover, requirements change over time — thus, our API has to change as well.‌
随着我们项目的发展,我们的知识也在增长;因此,我们对如何改进我们的系统有了更好的理解。此外,需求会随着时间的推移而变化——因此,我们的 API 也必须发生变化。

When we implement some breaking changes, we want to ensure that we don’t do anything that will cause our API consumers to change their code. Those breaking changes could be:
当我们实施一些重大更改时,我们希望确保我们不会执行任何会导致 API 使用者更改其代码的作。这些重大更改可能是:

• Renaming fields, properties, or resource URIs.
重命名字段、属性或资源 URI。

• Changes in the payload structure.
有效负载结构的更改。

• Modifying response codes or HTTP Verbs.
修改响应代码或 HTTP 动词。

• Redesigning our API endpoints.
重新设计我们的 API 端点。

If we have to implement some of these changes in the already working API, the best way is to apply versioning to prevent breaking our API for the existing API consumers.
如果我们必须在已经运行的 API 中实现其中一些更改,最好的方法是应用版本控制以防止破坏现有 API 使用者的 API。

There are different ways to achieve API versioning and there is no guidance that favors one way over another. So, we are going to show you different ways to version an API, and you can choose which one suits you best.
有多种方法可以实现 API 版本控制,并且没有支持一种方法的指导。因此,我们将向您展示对 API 进行版本控制的不同方法,您可以选择最适合您的方法。

24.1 Required Package Installation and Configuration

24.1 所需的软件包安装和配置

In order to start, we have to install the Microsoft.AspNetCore.Mvc.Versioning library in the Presentation project:‌
首先,我们必须在 Presentation 项目中安装 Microsoft.AspNetCore.Mvc.Versioning 库:

alt text

This library is going to help us a lot in versioning our API.
这个库将对我们的 API 版本控制有很大帮助。

After the installation, we have to add the versioning service in the service collection and configure it. So, let’s create a new extension method in the ServiceExtensions class:
安装完成后,我们必须在服务集合中添加版本控制服务并对其进行配置。因此,让我们在 ServiceExtensions 类中创建新的扩展方法:

public static void ConfigureVersioning(this IServiceCollection services) { services.AddApiVersioning(opt => { opt.ReportApiVersions = true; opt.AssumeDefaultVersionWhenUnspecified = true; opt.DefaultApiVersion = new ApiVersion(1, 0); }); }

With the AddApiVersioning method, we are adding service API versioning to the service collection. We are also using a couple of properties to initially configure versioning:
使用 AddApiVersioning 方法,我们将服务 API 版本控制添加到服务集合中。我们还使用几个属性来初始配置版本控制:

• ReportApiVersions adds the API version to the response header.
ReportApiVersions 将 API 版本添加到响应标头中。

• AssumeDefaultVersionWhenUnspecified does exactly that. It specifies the default API version if the client doesn’t send one.
AssumeDefaultVersionWhenUnspecified 正是这样做的。如果客户端未发送默认 API 版本,则它指定默认 API 版本。

• DefaultApiVersion sets the default version count.
DefaultApiVersion 设置默认版本计数。

After that, we are going to use this extension in the Program class:
之后,我们将在 Program 类中使用此扩展:

builder.Services.ConfigureVersioning();

API versioning is installed and configured, and we can move on.
API 版本控制已安装并配置完毕,我们可以继续前进。

24.2 Versioning Examples

24.2 版本控制示例

Before we continue, let’s create another controller: CompaniesV2Controller (for example’s sake), which will represent a new version of our existing one. It is going to have just one Get action:‌
在我们继续之前,让我们创建另一个控制器:CompaniesV2Controller(例如,为了起见),它将代表我们现有控制器的新版本。它将只有一个 Get作:

[ApiVersion("2.0")] [Route("api/companies")] [ApiController] public class CompaniesV2Controller : ControllerBase { private readonly IServiceManager _service; public CompaniesV2Controller(IServiceManager service) => _service = service; [HttpGet]public async Task<IActionResult> GetCompanies() { var companies = await _service.CompanyService .GetAllCompaniesAsync(trackChanges: false); return Ok(companies); } }

By using the [ApiVersion(“2.0”)] attribute, we are stating that this controller is version 2.0.
通过使用 [ApiVersion(“2.0”)] 属性,我们声明此控制器是 2.0 版。

After that, let’s version our original controller as well:
之后,让我们也对原始控制器进行版本控制:

[ApiVersion("1.0")] [Route("api/companies")] [ApiController] public class CompaniesController : ControllerBase

If you remember, we configured versioning to use 1.0 as a default API version (opt.AssumeDefaultVersionWhenUnspecified = true;). Therefore, if a client doesn’t state the required version, our API will use this one:
如果您还记得,我们将版本控制配置为使用 1.0 作为默认 API 版本(opt.AssumeDefaultVersionWhenUnspecified = true;因此,如果客户端没有说明所需的版本,我们的 API 将使用以下版本:
https://localhost:5001/api/companies

alt text

If we inspect the Headers tab of the response, we are going to find that the controller V1 was assigned for this request:
如果我们检查响应的 Headers 选项卡,我们将发现为此请求分配了控制器 V1:

alt text

Of course, you can place a breakpoint in GetCompanies actions in both controllers and confirm which endpoint was hit.
当然,您可以在两个控制器的 GetCompanies作中放置一个断点,并确认命中了哪个终端节点。

Now, let’s see how we can provide a version inside the request.
现在,让我们看看如何在请求中提供版本。

24.2.1 Using Query String‌

24.2.1 使用查询字符串

We can provide a version within the request by using a query string in the URI. Let’s test this with an example:
我们可以在 URI 中使用查询字符串在请求中提供版本。让我们用一个例子来测试一下:

https://localhost:5001/api/companies?api-version=2.0

alt text

So, we get the same response body.
因此,我们得到相同的响应正文。

But, we can inspect the response headers to make sure that version 2.0 is used:
但是,我们可以检查响应标头以确保使用版本 2.0:

alt text

24.2.2 Using URL Versioning‌

24.2.2 使用 URL 版本控制

For URL versioning to work, we have to modify the route in our controller:
要使 URL 版本控制正常工作,我们必须在控制器中修改路由:

[ApiVersion("2.0")] [Route("api/{v:apiversion}/companies")] [ApiController] public class CompaniesV2Controller : ControllerBase

Also, let’s just slightly modify the GetCompanies action in this controller, so we could see the difference in Postman by just inspecting the response body:
此外,我们只需稍微修改此控制器中的 GetCompanies作,以便我们只需检查响应正文即可看到 Postman 中的差异:

[HttpGet] public async Task<IActionResult> GetCompanies() { var companies = await _service.CompanyService .GetAllCompaniesAsync(trackChanges: false); var companiesV2 = companies.Select(x => $"{x.Name} V2"); return Ok(companiesV2); }

We are creating a projection from our companies collection by iterating through each element, modifying the Name property to contain the V2 suffix, and extracting it to a new collection companiesV2.
我们将通过循环访问每个元素,修改 Name 属性以包含 V2 后缀,并将其提取到新的集合 companiesV2 中,从 companies 集合创建投影。

Now, we can test it:
现在,我们可以测试它:

https://localhost:5001/api/2.0/companies

alt text

One thing to mention, we can’t use the query string pattern to call the companies v2 controller anymore. We can use it for version 1.0, though.
值得一提的是,我们不能再使用查询字符串模式来调用公司 v2 控制器。不过,我们可以在 1.0 版本中使用它。

24.2.3 HTTP Header Versioning‌

24.2.3 HTTP 标头版本控制

If we don’t want to change the URI of the API, we can send the version in the HTTP Header. To enable this, we have to modify our configuration:
如果我们不想更改 API 的 URI,我们可以在 HTTP Header 中发送版本。要启用此功能,我们必须修改我们的配置:

public static void ConfigureVersioning(this IServiceCollection services) { services.AddApiVersioning(opt => { opt.ReportApiVersions = true; opt.AssumeDefaultVersionWhenUnspecified = true; opt.DefaultApiVersion = new ApiVersion(1, 0); opt.ApiVersionReader = new HeaderApiVersionReader("api-version"); }); }

And to revert the Route change in our controller:
要在我们的控制器中恢复 Route 更改:

[Route("api/companies")]
[ApiVersion("2.0")]

Let’s test these changes:
让我们测试一下这些变化:
https://localhost:5001/api/companies

alt text

If we want to support query string versioning, we should use a new QueryStringApiVersionReader class instead:
如果我们想支持查询字符串版本控制,我们应该改用新的 QueryStringApiVersionReader 类:

opt.ApiVersionReader = new QueryStringApiVersionReader("api-version");

24.2.4 Deprecating Versions‌

24.2.4 弃用版本

If we want to deprecate version of an API, but don’t want to remove it completely, we can use the Deprecated property for that purpose:
如果我们想弃用 API 的版本,但又不想完全删除它,我们可以为此目的使用 Deprecated 属性:

[ApiVersion("2.0", Deprecated = true)]

We will be able to work with that API, but we will be notified that this version is deprecated:
我们将能够使用该 API,但会收到此版本已弃用的通知:

alt text

24.2.5 Using Conventions

24.2.5 使用约定

If we have a lot of versions of a single controller, we can assign these versions in the configuration instead:
如果我们有很多单个控制器的版本,我们可以在配置中分配这些版本:

services.AddApiVersioning(opt => { opt.ReportApiVersions = true; opt.AssumeDefaultVersionWhenUnspecified = true; opt.DefaultApiVersion = new ApiVersion(1, 0); opt.ApiVersionReader = new HeaderApiVersionReader("api-version"); opt.Conventions.Controller<CompaniesController>() .HasApiVersion(new ApiVersion(1, 0)); opt.Conventions.Controller<CompaniesV2Controller>() .HasDeprecatedApiVersion(new ApiVersion(2, 0)); });

Now, we can remove the [ApiVersion] attribute from the controllers.
现在,我们可以从控制器中删除 [ApiVersion] 属性。

Of course, there are a lot more features that the installed library provides for us — but with the mentioned ones, we have covered quite enough to version our APIs.
当然,已安装的库为我们提供了更多功能 — 但对于上述功能,我们已经涵盖了足够多的功能来对我们的 API 进行版本控制。

Leave a Reply

Your email address will not be published. Required fields are marked *