NestJS 接口版本控制
版本控制可以允许在同一应用中运行不同版本的控制器或独立的路由,在进行大版本迭代或 API 交付的应用场景下版本控制是一个必备的需求。
标记版本
分配版本支持控制器范围和路由处理函数范围:
- 通过
@Controller(options)
装饰器选项中的version分配当前控制器的版本,版本信息支持传递string
、string[]
类型且唯一的值用于区分; - 通过
@Version(options)
装饰器选项分配当前路由处理函数的版本,传递的版本信息及要求同@Controller(options)
装饰器;
如下代码演示了控制器版本分配,表示这是处理用户相关资源的第一个版本:
代码语言:javascript复制@Controller({
path: 'users',
version: '1',
})
export class UsersController {}
如下代码演示了路由处理函数版本分配,表示这个处理函数可能由于需要客户端提供更多的参数而进行被迫升级,从而支持最新的业务:
代码语言:javascript复制@Version('2')
@Post()
create(@Body() createOrderDto: CreateOrderDto) {
return this.ordersService.create(createOrderDto);
}
如下代码演示了路由处理函数被分配多个版本,表示它 v1
和 v2
版本的获取单个订单将全部由同一个处理函数处理:
@Version(['1', '2'])
@Get(':id')
findOne(@Param('id') id: string) {
return this.ordersService.findOne( id);
}
如下代码通过设置特殊的常量(VERSION_NEUTRAL
),表示获取所有订单的路由处理函数不受版本的要求:
@Version(VERSION_NEUTRAL)
@Get()
findAll() {
return this.ordersService.findAll();
}
版本控制
在 NestJS 中支持 4 种版本控制的方式:
- 通过请求 URL 进行版本控制;
- 通过自定义 Header 进行版本控制;
- 通过 Accept 头进行版本控制;
- 完全自定义进行版本控制;
URL 进行版本控制
激活 URL 版本控制:
代码语言:javascript复制const app = await NestFactory.create(AppModule);
// 激活 URL 版本控制,默认值
app.enableVersioning();
// or
const app = await NestFactory.create(AppModule);
app.enableVersioning({
type: VersioningType.URI,
});
获取所有用户(v1):
代码语言:javascript复制curl --request GET
--url http://localhost:3000/v1/users
创建订单(v2):
代码语言:javascript复制curl --request POST
--url http://localhost:3000/v2/orders
获取指定 ID 的订单(v1,v2):
代码语言:javascript复制curl --request GET
--url http://localhost:3000/v1/orders/1
// and
curl --request GET
--url http://localhost:3000/v2/orders/1
获取所有订单(无版本控制):
代码语言:javascript复制curl --request GET
--url http://localhost:3000/orders
自定义 Header 进行版本控制
激活 自定义 Header 版本控制:
代码语言:javascript复制const app = await NestFactory.create(AppModule);
app.enableVersioning({
type: VersioningType.HEADER,
header: 'x-api-version',
});
获取所有用户(v1):
代码语言:javascript复制curl --request GET
--url http://localhost:3000/users
--header 'x-api-version: 1'
创建订单(v2):
代码语言:javascript复制curl --request POST
--url http://localhost:3000/orders
--header 'x-api-version: 2'
获取指定 ID 的订单(v1,v2):
代码语言:javascript复制curl --request GET
--url http://localhost:3000/orders
--header 'x-api-version: 1'
// and
curl --request GET
--url http://localhost:3000/orders
--header 'x-api-version: 2'
获取所有订单(无版本控制):
代码语言:javascript复制curl --request GET
--url http://localhost:3000/orders
Accept 头进行版本控制
激活 Accept 头版本控制:
代码语言:javascript复制const app = await NestFactory.create(AppModule);
app.enableVersioning({
type: VersioningType.MEDIA_TYPE,
key: 'v=',
});
获取所有用户(v1):
代码语言:javascript复制curl --request GET
--url http://localhost:3000/users
--header 'Accept: application/json;v=1'
创建订单(v2):
代码语言:javascript复制curl --request POST
--url http://localhost:3000/orders
--header 'Accept: application/json;v=2'
获取指定 ID 的订单(v1,v2):
代码语言:javascript复制curl --request GET
--url http://localhost:3000/orders
--header 'Accept: application/json;v=1'
// and
curl --request GET
--url http://localhost:3000/orders
--header 'Accept: application/json;v=2'
获取所有订单(无版本控制):
代码语言:javascript复制curl --request GET
--url http://localhost:3000/orders
完全自定义进行版本控制
激活完全自定义版本控制,并实现类 Header
版本控制:
app.enableVersioning({
type: VersioningType.CUSTOM,
extractor,
});
// 自定义版本提取函数
const extractor = (request: Request): string | string[] => {
return [request.headers['x-api-version'] ?? '']
.flatMap((v: string) => v.split(','))
.filter((v) => !!v)
.sort()
.reverse();
};
获取指定 ID 的订单(v1,v2):
代码语言:javascript复制curl --request GET
--url http://localhost:3000/orders
--header 'x-api-version: 1'
// and
curl --request GET
--url http://localhost:3000/orders
--header 'x-api-version: 2'
全局默认版本
目前为止在 UsersController
和 OrdersController
还有一些路由处理函数是没有分配版本,如果你希望为它们设置统一的默认版本的话,可以在启动版本控制类型的时候提供默认版本。
app.enableVersioning({
defaultVersion: '1',
});
// or
app.enableVersioning({
defaultVersion: ['1', '2'],
});
// or
app.enableVersioning({
defaultVersion: VERSION_NEUTRAL,
});
中间件的版本控制
分配中间件的时候为 forRoutes
选项添加 version
属性分配其激活控制器的版本:
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes({ path: 'users', method: RequestMethod.GET, version: '1' });
}
}