NestJS 如何实现接口多版本控制

前言

在持续迭代的现代应用开发中,版本控制是无法绕开的问题。任何对现有接口的修改,如果没有一个有效的版本控制机制,都可能导致应用全局范围的影响。那么,如何实现一个清晰且高效的接口版本控制呢?

多版本控制策略希望在设计之初就考虑到,以确保应用具备良好的灵活性和可扩展性。而 NestJS 的强大功能,就带给我们实现多版本控制的可能。

NestJS 是一个强大且灵活的 Node.js 框架,它提供的模块化架构可以让我们为每个版本的代码创建特定的模块。这样,一旦对某个版本的代码需要修改,修改的内容就会被严格地限制在这个模块中,最大限度地减少对其他版本产生影响。

本文将带你了解使用 NestsJS 如何实现接口多版本控制。

接口多版本控制的类型

在 NestJS 中,多版本控制可支持三种类型:

  1. URI Versioning(URI版本控制)

    这种类型的版本控制将版本信息放在请求的 URI 中。例如 https://example.com/v1/route 和 https://example.com/v2/route

  2. Header Versioning(标头版本控制)

    标头版本控制将使用自定义的、用户指定的请求标头来指定版本信息。

  3. Media Type Versioning(媒体类型版本控制)

    媒体类型版本控制使用请求的 Accept 标头来指定版本信息。

接下来,我们将详细介绍这三种类型的版本控制。

接口多版本控制的实现方式

一、URI Versioning(URI版本控制)

URI 版本控制使用在请求的 URI 中传递的版本信息,例如 https://example.com/v1/route 和 https://example.com/v2/route。注意使用 URI 版本控制,版本信息将自动添加到 URI 中全局路径前缀(如果存在)之后,任何控制器或路由路径之前。

为了要启用 URI 版本控制,你需要在 main.ts 文件中这样配置:

shell
import { NestFactory } from '@nestjs/core'; import { VersioningType } from '@nestjs/common'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); // or "app.enableVersioning()" app.enableVersioning({ type: VersioningType.URI, }); await app.listen(3000); } bootstrap();

默认情况下,URI 中的版本将自动以 v 为前缀,但是可以通过将 prefix 键设置为你想要的前缀来配置前缀值,如果你希望禁用它,则可以设置为 false

二、Header Versioning(标头版本控制)

标头版本控制使用自定义的、用户指定的请求标头来指定版本信息。例如,你可以编写一个包含了特定版本信息的 HTTP 请求:

shell
GET /cats HTTP/1.1 Host: example.com Accept: application/json Custom-Header: 2

启用标头版本控制,你需要在 main.ts 文件中这样配置:

typescript
import { NestFactory } from '@nestjs/core'; import { VersioningType } from '@nestjs/common'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.enableVersioning({ type: VersioningType.HEADER, header: 'Custom-Header', }); await app.listen(3000); } bootstrap();

header 属性就是包含请求版本的标头的名称。

三、Media Type Versioning(媒体类型版本控制)

媒体类型版本控制使用 Accept 请求的标头来指定版本信息。在 Accept 标头中,版本将与媒体类型用分号 ; 分隔。然后它应该包含一个键值对,表示用于请求的版本,例如 Accept: application/json;v=2。在确定要配置为包含键和分隔符的版本时,它们的键更多地被视为前缀。

要启用媒体类型版本控制,你需要在 main.ts 文件中这样配置:

typescript
import { NestFactory } from '@nestjs/core'; import { VersioningType } from '@nestjs/common'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.enableVersioning({ type: VersioningType.MEDIA_TYPE, key: 'v=', }); await app.listen(3000); } bootstrap();

其中 key 属性应为包含版本的键值对的键和分隔符。例如,当 Accept 头部为 application/json;v=2 时,key 属性将设置为 v=

实战

一个最基础接口版本的 Controller

typescript
import { Controller, Get } from '@nestjs/common'; @Controller() export class UserControllerV1 { @Get('user') findAll(): string[] { return ['This action returns all user']; } }

接下来在这个基础接口的基础上进行多种方式的版本控制

一、单个路由级别设置版本

可以为单个路由设置版本。单个路由的版本设置将覆盖控制器版本。以下实例演示了如何为路由添加版本:

typescript
import { Controller, Get, Version } from '@nestjs/common'; @Controller() export class UserController { @Version('1') @Get('user') findAllV1(): string[] { return ['user1']; } @Version('2') @Get('user') findAllV2(): string { return ['user2']; } }

访问 http://localhost:3000/v1/user

二、控制器级别设置版本

分开到不同的Controller文件中

typescript
import { Controller, Get, Version } from '@nestjs/common'; @Controller({ version: ['1'], }) export class UserController { @Get('user') findAll(): string[] { return ['user1']; } }
typescript
import { Controller, Get, Version } from '@nestjs/common'; @Controller({ version: ['2'], }) export class UserController { @Get('user') findAll(): string[] { return ['user2']; } }

访问 http://localhost:3000/v1/user

三、系统级别设置版本

typescript
app.enableVersioning({ // ... defaultVersion: '1' // or defaultVersion: ['1', '2'] // or defaultVersion: VERSION_NEUTRAL });

四、无视接口版本

有时候,一些控制器或路由可能无论版本如何都具有相同的功能。这时,你可以将版本设置为 VERSION_NEUTRAL 符号,任何版本的请求都将映射到该控制器或路由。以下实例演示了如何设置无视版本的控制器:

typescript
import { Controller, Get, VERSION_NEUTRAL } from '@nestjs/common'; @Controller({ version: VERSION_NEUTRAL, }) export class UserController { @Get('user') findAll(): string[] { return return ['user']; } }

访问 http://localhost:3000/user

总结

简单的版本控制可能会在管理上引入混乱和复杂性,但是 NestJS 的多版本控制能有效地处理这类问题,让我们可以使用 URI、标头或媒体类型的方式灵活地管理各个版本。不仅如此,NestJS 还允许我们对控制器或每个独立路由进行单独的版本控制,并提供了一种选择退出版本控制的方式。另外,我们还可以将默认版本全局应用于未指定版本的所有控制器和路由,进一步简化了版本管理工作。

使用 API 版本控制,我们能在新增功能和修复缺陷时保持旧版的稳定性,同时也允许用户在准备好更新他们的客户端时自由切换到新版本。通过优雅而规范的版本控制,开发者可以始终对 API 有清晰的理解,而用户则可以享受到连续的服务。

使用 NestJS 进行接口多版本的控制,是一种高效且灵活的解决方案。它不仅能帮助我们在持续迭代和更新中保持稳定,也使得我们的应用具有更好的可扩展性和健壮性。