问题描述
我有一个应用程序,根据open-api规范,我将API响应模式定义为纯javascript对象。目前,我将其传递给@ nestjs / swagger中的ApiResponse
装饰器,如下所示:
class CatsController {
@Get()
@ApiResponse({
status: 200,schema: catSchema // plain js object imported from another file
})
getAll() {}
}
这很好。但是,输出的open-api规范包含每个使用catSchema
的端点的详细架构。 相反,我希望输出的swagger文件在components
部分下具有catSchema,在paths部分中具有相应的$ref
。
components:
schemas:
Cat:
properties:
name:
type: string
paths:
/cats/{id}:
get:
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/Cat'
到目前为止,似乎唯一的方法是将架构定义为DTO类,并对每个类属性使用ApiProperty
装饰器。就我而言,这意味着我必须将open-api规范中的所有纯对象模式重构为DTO类。
是否可以将原始模式馈送到库并获得预期的结果?
// instead of this:
class CatDto {
@Apiproperty()
name: string;
}
// I want to do:
const catSchema = {
type: 'object',properties: {
name: { type: 'string' }
}
}
解决方法
经过数天的反复试验,我能够使用Javascript中的一个有趣技巧来实现这一目标。
首先,我将open-api规范创建为一个普通对象(如问题中所述)。然后将其传递给新的装饰器,神奇的地方发生。
在装饰器中,我创建一个具有预定义名称的DTO类,并将属性从普通对象映射到DTO类。棘手的部分是动态为其命名。这可以通过以下技术来实现。
const dynamicName = 'foo'; // passed as a parameter to the decorator
class IntermediateDTO {
@ApiProperty(schema) // schema as a plain object
data: any;
}
const proxyObject = {
[dynamicName] = class extends IntermediateDTO {}
}
通过使用代理对象,并将class extends IntermediateDTO {}
分配给其中的属性,该条目将动态获得名称。现在,可以将具有动态名称的新DTO传递给ApiResponse
的{{1}}装饰器,以达到预期的结果。