参考:
Nest.js:https://docs.nestjs.cn/
API 多版本:https://docs.nestjs.cn/9/techniques?id=api-%e5%a4%9a%e7%89%88%e6%9c%ac
在日常开发中,一个非常常见的需求就是 api 开发完成后,又需要进行调整,同时要不影响旧 api 的使用,以免影响到用户体验,此时就需要对 API 进行多版本管理,实现不同版本的 api 共存,以达到兼容旧版本 api 的目的。
下面来看一下在 Nest.js 中如何实现这一点。
开始
Nest.js 支持一下 4 种形式的版本管理:
类型 | 说明 |
---|---|
URI 版本类型 |
版本在请求的 URI 中传递 (默认) |
Header 版本管理 |
在自定义的 Header 中传递版本 |
Media Type 版本管理 |
在 Accept 头部标签中声明版本 |
自定义版本管理 |
请求的任何部分都可用于指定版本,需要提供一个自定义函数来提取所述版本 |
下面来逐一介绍一下,并说明笔者自己的选择。
版本管理
URI 版本类型
这种方法就是在 uri 中添加版本号,例如https://example.com/v1/route
,这里的 v1
就是版本号
以下是具体写法。
const app = await NestFactory.create(AppModule);
// or "app.enableVersioning()"
app.enableVersioning({
type: VersioningType.URI,
});
await app.listen(3000);
在 uri 中添加版本号是非常有效且常见的做法,可以非常方便的通过路由来划分不同版本的 api。
但缺点也很明显,那就是在 uri 中写死了版本号,不利于后续的升级。
Header 版本类型
在 Header 版本管理中,使用用户指定的自定义头部标签来标明使用的请求版本,即:
const app = await NestFactory.create(AppModule);
app.enableVersioning({
type: VersioningType.HEADER,
header: 'Api-Version', // 在 header 中标记的 Api-Version 标签就是指定的版本号
});
await app.listen(3000);
笔者认为这种写法是相当不错的,可以复用旧版本的 url,只需要添加一个 Api-Version
就可以了。
不过缺点就是依赖于后端对 Api-Version
的解析,前端如果到处都要改的话工作量也不小。
Media Type 版本类型
在
Accept
头部标签内, 版本将与媒体类型用分号;
分隔。它应该包含一个键值对,表示要用于请求的版本,例如Accept: application/json;v=2
。在配置包含键和分隔符的版本时,可以设置key
的值作为键的前缀值。
跟 Header 版本类似的是,Media 版本类型使用 Accept
头部标签来声明请求的版本,即:
const app = await NestFactory.create(AppModule);
app.enableVersioning({
type: VersioningType.MEDIA_TYPE,
key: 'v=',
});
await app.listen(3000);
在笔者看来,提供 Media 版本管理和通过 Header 版本管理并无太大区别,故优缺点也类似,不再赘述。
自定义版本类型
最后则是自定义版本控制,想要怎么实现版本控制就自己写。
自定义版本控制使用请求的任何部分来指定一个或多个版本,并使用返回字符串或字符串数组的
extractor
函数分析传入请求。
// Example extractor that pulls out a list of versions from a custom header and turns it into a sorted array.
// This example uses Fastify, but Express requests can be processed in a similar way.
const extractor = (request: FastifyRequest): string | string[] =>
[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);
忽略版本控制
在实际开发中,很多时候接口升级都是逐步的,并不会一次性升级全部接口,此时,对于旧版本的接口,以前并没有做版本控制的接口,也需要做一个兼容,让它们继续工作。
有的控制器或路由不关心接口版本并且无论版本如何,都将具有相同的功能,为了适应这种情况,可以将版本设置为
VERSION_NEUTRAL
。传入的请求将映射到
VERSION_NEUTRAL
控制器或路由,而不管请求中发送的版本如何,或者请求中根本不包含版本信息。
app.enableVersioning({
defaultVersion: VERSION_NEUTRAL // 将全局的默认版本指定为 VERSION_NEUTRAL 即可
});
此时需要注意一个问题,要将设置了版本号的接口放在没设置版本的接口前面,以优先匹配到有版本的接口,参考如下:
import { Controller, Get, Version } from '@nestjs/common'
import { AppService } from './app.service'
@Controller()
export class AppController {
constructor(private readonly appService: AppService) { }
@Version('1')
@Get('/')
getHelloV1() {
return this.appService.getHello('1')
}
@Version('2')
@Get('/')
getHelloV2() {
return this.appService.getHello('2')
}
@Get('/')
getHello() {
return this.appService.getHello()
}
}
此时如果用户传递的版本号为1
,那么就执行 getHelloV1()
,为 2
就执行 getHelloV2()
;如果什么都不传就执行 getHello()
。
这样一来就可以逐步对旧版本 api 进行升级而不影响到原有接口了。
结语
在 api 的开发中,难免需要对 api 进行升级,但又要避免影响到原有用户,所以需要对 api 进行版本管理。通常,在一个项目刚开始的时候是没有对 api 进行版本管理的,所以后续升级的时候往往都是逐步升级的,因此要避免对默认版本的影响,只对设置了版本号的接口进行改动,尽可能减少影响范围。
- 本文链接: https://wp.cmyr.ltd/archives/implement-multiple-versions-of-api-in-nest-js
- 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
欢迎关注我的其它发布渠道
发表回复
要发表评论,您必须先登录。