版本控制 Versioning

导读

提示

本章仅与基于 HTTP 的应用程序相关。

版本控制允许您在同一应用程序中运行控制器或各个路由的不同版本。应用程序经常更改,因此,在仍需要支持应用程序的先前版本的同时进行重大更改并不罕见。

支持 4 种版本控制类型:

URI 版本控制版本将在请求的 URI 中传递(默认)
标头版本控制自定义请求标头将指定版本
媒体类型版本控制请求的 Accept 标头将指定版本
自定义版本控制请求的任何方面都可用于指定版本。提供自定义函数来提取所述版本。

URI 版本控制类型

URI 版本控制使用在请求的 URI 中传递的版本,例如 https://example.com/v1/routehttps://example.com/v2/route

通知

使用 URI 版本控制,版本将自动添加到 URI 中,位于 全局路径前缀(如果存在)之后,以及任何控制器或路由路径之前。

要为您的应用程序启用 URI 版本控制,请执行以下操作:

main
ts
const app = await NestFactory.create(AppModule)
// or "app.enableVersioning()"
app.enableVersioning({
  type: VersioningType.URI,
})
await app.listen(3000)
通知

URI 中的版本默认会自动以 v 作为前缀,但是可以通过将 prefix 键设置为所需的前缀或 false(如果您希望禁用它)来配置前缀值。

提示

VersioningType 枚举可用于 type 属性,并从 @nestjs/common 包导入。

标头版本控制类型

标头版本控制使用自定义的、用户指定的请求标头来指定版本,其中标头的值将是用于请求的版本。

标头版本控制的示例 HTTP 请求:

要为您的应用程序启用 标头版本控制,请执行以下操作:

main
ts
const app = await NestFactory.create(AppModule)
app.enableVersioning({
  type: VersioningType.HEADER,
  header: 'Custom-Header',
})
await app.listen(3000)

header 属性应为包含请求版本的标头的名称。

提示

VersioningType 枚举可用于 type 属性,并从 @nestjs/common 包导入。

媒体类型版本控制类型

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

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

要为您的应用程序启用 媒体类型版本控制,请执行以下操作:

main
ts
const app = await NestFactory.create(AppModule)
app.enableVersioning({
  type: VersioningType.MEDIA_TYPE,
  key: 'v=',
})
await app.listen(3000)

key 属性应为包含版本的键值对的键和分隔符。对于示例 Accept: application/json;v=2key 属性应设置为 v=

提示

VersioningType 枚举可用于 type 属性,并从 @nestjs/common 包导入。

自定义版本控制类型

自定义版本控制使用请求的任何方面来指定版本(或多个版本)。使用返回字符串或字符串数​​组的 extractor 函数分析传入的请求。

如果请求者提供了多个版本,则提取器函数可以返回一个字符串数组,按从最大/最高版本到最小/最低版本的顺序排序。版本按从最高到最低的顺序与路由匹配。

如果从 extractor 返回空字符串或数组,则不匹配任何路由,并返回 404。

例如,如果传入请求指定它支持版本 123,则 extractor 必须 返回 [3, 2, 1]。这可确保首先选择最高可能的路由版本。

如果提取了版本 [3, 2, 1],但路由仅存在于版本 21,则选择与版本 2 匹配的路由(版本 3 将被自动忽略)。

注意

由于设计限制,根据从 extractor > 返回的数组选择最高匹配版本无法可靠地与 Express 适配器配合使用。单个版本(字符串或1个元素的数组)在 Express 中可以正常工作。Fastify 正确支持最高匹配版本选择和单个版本选择。

要为您的应用程序启用自定义版本控制,请创建一个extractor函数并将其传递到您的应用程序中 如下所示:

main
ts
// 示例提取器从自定义标头中提取版本列表并将其转换为排序数组。此示例使用 Fastify,但 Express 请求可以以类似的方式处理。
function extractor(request: FastifyRequest): string | string[] {
  return [request.headers['custom-versioning-field'] ?? '']
    .flatMap(v => v.split(','))
    .filter(v => !!v)
    .sort()
    .reverse()
}

const app = await NestFactory.create(AppModule)
app.enableVersioning({
  type: VersioningType.CUSTOM,
  extractor,
})
await app.listen(3000)

用法

版本控制允许您对控制器、单个路由进行版本控制,还为某些资源提供了一种退出版本控制的方法。无论您的应用程序使用哪种版本控制类型,版本控制的用法都是相同的。

通知

如果为应用程序启用了版本控制,但控制器或路由未指定版本,则对该控制器/路由的任何请求都将返回 404 响应状态。同样,如果收到的请求包含没有相应控制器或路由的版本,它也将返回 404 响应状态。

控制器版本

版本可以应用于控制器,为控制器内的所有路由设置版本。

要将版本添加到控制器,请执行以下操作:

ts
cats.controller
ts
@Controller({
  version: '1',
})
export class CatsControllerV1 {
  @Get('cats')
  findAll(): string {
    return 'This action returns all cats for version 1'
  }
}

路线版本

版本可应用于单个路线。此版本将覆盖影响路线的任何其他版本,例如控制器版本。

要将版本添加到单个路线,请执行以下操作:

ts
cats.controller
ts
import { Controller, Get, Version } from '@nestjs/common'

@Controller()
export class CatsController {
  @Version('1')
  @Get('cats')
  findAllV1(): string {
    return 'This action returns all cats for version 1'
  }

  @Version('2')
  @Get('cats')
  findAllV2(): string {
    return 'This action returns all cats for version 2'
  }
}

多个版本

多个版本可以应用于控制器或路由。要使用多个版本,您可以将版本设置为数组。

要添加多个版本,请执行以下操作:

ts
cats.controller
ts
@Controller({
  version: ['1', '2'],
})
export class CatsController {
  @Get('cats')
  findAll(): string {
    return 'This action returns all cats for version 1 or 2'
  }
}

版本中性

某些控制器或路由可能不关心版本,无论版本如何,都具有相同的功能。为了适应这种情况,可以将版本设置为VERSION_NEUTRAL符号。

无论请求中发送的版本如何,传入请求都将映射到VERSION_NEUTRAL控制器或路由,除非请求根本不包含版本。

通知

对于 URI 版本控制,VERSION_NEUTRAL资源不会在 URI 中显示版本。

要添加版本中性的控制器或路由,请执行以下操作:

ts
cats.controller
ts
import { Controller, Get, VERSION_NEUTRAL } from '@nestjs/common'

@Controller({
  version: VERSION_NEUTRAL,
})
export class CatsController {
  @Get('cats')
  findAll(): string {
    return 'This action returns all cats regardless of version'
  }
}

全局默认版本

如果您不想为每个控制器/或单个路由提供版本,或者想要将特定版本设置为每个未指定版本的控制器/路由的默认版本,则可以按如下方式设置defaultVersion

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

中间件版本控制

中间件 还可以使用版本控制元数据来为特定路由版本配置中间件。为此,请将版本号作为 MiddlewareConsumer.forRoutes() 方法的参数之一提供:

app.module
ts
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'
import { LoggerMiddleware } from './common/middleware/logger.middleware'
import { CatsModule } from './cats/cats.module'
import { CatsController } from './cats/cats.controller'

@Module({
  imports: [CatsModule],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes({ path: 'cats', method: RequestMethod.GET, version: '2' })
  }
}

使用上述代码,LoggerMiddleware 将仅适用于 /cats 端点的版本 '2'。

注意

中间件适用于本节中描述的任何版本类型:URIHeaderMedia TypeCustom