返回文章列表文章索引阅读全文
STARBLOG / 阅读手记
Asp-Net-Core开发笔记

Asp-Net-Core Development Notes: API Version Management

For Web API applications, as time passes and requirements increase or change, the API will inevitably encounter the need for upgrades. In fact, Web API applications should consider API versioning from the moment they are created. Changes in business logic, addition of features, removal or renaming of endpoints, modifications to endpoint parameters, and additions, deletions, or changes to entity properties will all alter the API's functionality, thereby leading to version changes.

曦远 (DealiAxy)约 29 分钟
目录9 个章节

Preface

For Web API applications, as time progresses and requirements increase or change, APIs will inevitably encounter the need for upgrades. In fact, Web API applications should consider API versioning from the very beginning of their creation. Business adjustments, feature additions, endpoint removals and renaming, changes in endpoint parameters, as well as adding, removing, or modifying entity properties can all alter API functionality, thus resulting in version changes.

Most existing resources use the Microsoft.AspNetCore.Mvc.Versioning package, but upon actual use, I found that this package has long been discontinued. Microsoft's official documentation also seems to lack information on this. However, the package's NuGet page indicates it has been replaced by the new Asp.Versioning.Mvc package. It turns out Microsoft's renaming department has been at it again—my apologies for the oversight! 😂

Fortunately, this new package has detailed documentation on GitHub, but the speed of renaming is quite astonishing. In order to implement this functionality, I took quite a few detours. 😂

OK, this article is based on .Net6.0 and uses AspNetCore WebApi as an example to introduce the process of incorporating API version management.

Basics

There are two ways to specify a version: you can either use the [ApiVersion] attribute or the version convention approach. Once different versions of an API interface are defined, clients can access a specific version of the API through several methods as follows:

  • Using the URL path, e.g., api/v1.0/values
  • Using a query string, e.g., api/values?api-version=1.0
  • Using a custom HTTP header
  • Using a Media Type parameter, e.g., Accept: application/json;v=2.0

The default method in ASP.NET Core MVC is using a query string, with the parameter name api-version. The specific method to use is determined by the server (configured using the ApiVersionReader property introduced below), and it can be one of the methods or multiple methods used simultaneously.

The API version format consists of a major version number and a minor version number, and it can also include two optional parts: a version group and a status.

  • [Version Group.]<Major>.<Minor>[-Status]
  • <Version Group>[<Major>[.Minor]][-Status]

The version group format is YYYY-MM-DD and serves to logically group API interfaces, while the status indicates the current state of the version, such as Alpha, Beta, and RC. Here are some common version formats:

  • /api/foo?api-version=1.0
  • /api/foo?api-version=2.0-Alpha
  • /api/foo?api-version=2015-05-01.3.0
  • /api/v1/foo
  • /api/v2.0-Alpha/foo
  • /api/v2015-05-01.3.0/foo

This article uses the format /api/v1/foo.

Installing Dependencies

You need to install these two NuGet packages:

  • Asp.Versioning.Mvc
  • Asp.Versioning.Mvc.ApiExplorer

Register Services

Edit the Program.cs file

builder.Services.AddApiVersioning(options => {
  options.DefaultApiVersion = new ApiVersion(1, 0);
  options.AssumeDefaultVersionWhenUnspecified = true;
  options.ReportApiVersions = true;
  options.ApiVersionReader = ApiVersionReader.Combine(
    new UrlSegmentApiVersionReader(),
    new HeaderApiVersionReader("x-api-version"),
    new MediaTypeApiVersionReader("ver")
  );
})
  .AddMvc()
  .AddApiExplorer(options => {
    options.GroupNameFormat = "'v'VVV";
    options.SubstituteApiVersionInUrl = true;
  });

The code above does the following:

  • DefaultApiVersion sets the default version to 1.0.
  • AssumeDefaultVersionWhenUnspecified uses the default version when no version is specified.
  • ReportApiVersions adds available API versions to the response headers.
  • ApiVersionReader defines that API version information can be retrieved from three places: the URL and two request headers.
  • GroupNameFormat specifies the format for version names, see the table below for details.
  • SubstituteApiVersionInUrl is set to true because URL-based versioning is being used.

API Version Format Strings

In this article, I use the 'v'VVV format

Format SpecifierDescriptionExamples
FFull API version as [group version][.major[.minor]][-status]2017-05-01.1-RC -> 2017-05-01.1-RC
FFFull API version with optional minor version as [group version][.major[.minor,0]][-status]2017-05-01.1-RC -> 2017-05-01.1.0-RC
GGroup version as yyyy-MM-dd2017-05-01.1-RC -> 2017-05-01
GGGroup version as yyyy-MM-dd with status2017-05-01.1-RC -> 2017-05-01-RC
yGroup version year from 0 to 992001-05-01.1-RC -> 1
yyGroup version year from 00 to 992001-05-01.1-RC -> 01
yyyGroup version year with a minimum of three digits2017-05-01.1-RC -> 017
yyyyGroup version year as a four-digit number2017-05-01.1-RC -> 2017
MGroup version month from 1 through 122001-05-01.1-RC -> 5
MMGroup version month from 01 through 122001-05-01.1-RC -> 05
MMMGroup version abbreviated name of the month2001-06-01.1-RC -> Jun
MMMMGroup version full name of the month2001-06-01.1-RC -> June
dGroup version day of the month, from 1 through 312001-05-01.1-RC -> 1
ddGroup version day of the month, from 01 through 312001-05-01.1-RC -> 01
dddGroup version abbreviated name of the day of the week2001-05-01.1-RC -> Mon
ddddGroup version full name of the day of the week2001-05-01.1-RC -> Monday
vMinor version2001-05-01.1-RC -> 1 1.1 -> 1
VMajor version1.0-RC -> 1 2.0 -> 2
VVMajor and minor version1-RC -> 1 1.1-RC -> 1.1 1.1 -> 1.1
VVVMajor, optional minor version, and status1-RC -> 1-RC 1.1 -> 1.1
VVVVMajor, minor version, and status1-RC -> 1.0-RC 1.1 -> 1.1 1 -> 1.0
SStatus1.0-Beta -> Beta
pPadded minor version with default of two digits1.1 -> 01 1 -> 00
p[n]Padded minor version with N digitsp2: 1.1 -> 01 p3: 1.1 -> 001
PPadded major version with default of two digits2.1 -> 02 2 -> 02
P[n]Padded major version with N digitsP2: 2.1 -> 02 P3: 2.1 -> 002
PPPadded major and minor version with a default of two digits2.1 -> 02.01 2 -> 02.00
PPPPadded major, optional minor version, and status with a default of two digits1-RC -> 01-RC 1.1-RC -> 01.01-RC
PPPPPadded major, minor version, and status with a default of two digits1-RC -> 01.00-RC 1.1-RC -> 01.01-RC

Setting Up API Versions

The example interface has two versions:

  • /api/v1/demo/test
  • /api/v2/demo/test

Create two directories under the Controller, v1 and v2, then create a Controller in each respectively.

Here's the code for Controllers/v1/DemoController.cs:

[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion(1.0)]
[ApiController]
public class DemoController : ControllerBase {
  [HttpGet("[action]")]
  public ApiResponse Test() {
    return ApiResponse.Ok("version=1.0");
  }
}

The other version of the interface Controllers/v2/DemoController.cs:

[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion(2.0)]
[ApiController]
public class DemoController : ControllerBase {
  [HttpGet("[action]")]
  public ApiResponse Test() {
    return ApiResponse.Ok("version=2.0");
  }
}

As you can see, to distinguish between different versions of the interface, you only need to add the [ApiVersion(2.0)] attribute.

Since I want to use the URL to select different versions of the interface, the route needs to be configured as "api/v{version:apiVersion}/[controller]".

If you don't include the version number in the URL, you can also pass it using a request parameter, for example, /api/demo/test?api-version=1.0.

This can be configured in the ApiVersionReader attribute.

Configuring Swagger

Swagger has essentially become the standard for API documentation, but I've noticed many articles overlook this part. (Fortunately, the official documentation didn't forget.)

First, create a configuration class.

public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions> {
  readonly IApiVersionDescriptionProvider provider;

  public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) =>
    this.provider = provider;

  public void Configure(SwaggerGenOptions options) {
    foreach (var description in provider.ApiVersionDescriptions) {
      options.SwaggerDoc(
        description.GroupName,
        new OpenApiInfo() {
          Title = $"Example API {description.ApiVersion}",
          Version = description.ApiVersion.ToString(),
        });
    }
  }
}

Registering Services

builder.Services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigureSwaggerOptions>();

Configuring Middleware

app.UseSwagger();
app.UseSwaggerUI(options => {
    foreach (var description in app.DescribeApiVersions()) {
        var url = $"/swagger/{description.GroupName}/swagger.json";
        var name = description.GroupName.ToUpperInvariant();
        options.SwaggerEndpoint(url, name);
    }
});

Effects & Testing

Done. Access the swagger documentation, and in the upper right corner, the API grouping (接口分组) shows different versions.

Request https://localhost:7053/api/v1/Demo/Test

The API returns:

{
  "statusCode": 200,
  "successful": true,
  "message": "version=1.0",
  "data": null,
  "errorData": null
}

Request https://localhost:7053/api/v2/Demo/Test

The API returns:

{
  "statusCode": 200,
  "successful": true,
  "message": "version=2.0",
  "data": null,
  "errorData": null
}

Not bad~ 😃

References

_EOF_

Asp-Net-Core Development Notes: API Version Management

本文作者:
程序设计实验室
版权声明:
本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

相关文章

Asp-Net-Core开发笔记:快速在已有项目中引入EFCore

很多项目一开始选型的时候没有选择EFCore,不过EFCore确实好用,也许由于种种原因后面还是需要用到,这时候引入EFCore也很方便。 本文以 StarBlog 为例,StarBlog 目前使用的 ORM 是 FreeSQL ,引入 EFCore 对我来说最大的好处是支持多个数据库,如果是 FreeSQL 的话,服务注册的时候是单例模式,只能连接一个数据库,如果需要使用 FreeSQL 同时

2023-10-08 · Asp-Net-Core开发笔记 · 约 1 分钟

Asp-Net-Core开发笔记:使用RateLimit中间件实现接口限流

前言 最近一直在忙(2月份沉迷steam,3月开始工作各种忙),好久没更新博客了,不过也积累了一些,忙里偷闲记录一下。 这个需求是这样的,我之前做了个工单系统,现在要对登录、注册、发起工单这些功能做限流,不能让用户请求太频繁。 从 .Net7 开始,已经有内置的限流功能了,但目前我们的项目还在使用 .Net6 LTS 版本,下一个 LTS 没发布之前,暂时不考虑使用 .Net7 这种非 LTS 版

2023-03-20 · Asp-Net-Core开发笔记 · 约 1 分钟

讨论

共 0 条

良言一句三冬暖,恶语伤人六月寒。

加载评论中…

Asp-Net-Core Development Notes: API Version Management · StarBlog