OpenAPI 文档导出实现说明
✅ 已实现功能
1. OpenAPI JSON 接口
接口: GET /api/swagger/json
功能: 返回完整的 OpenAPI 3.0 规范 JSON 文档,用于 Apifox、Postman 等工具导入
特性:
- ✅ 直接返回标准 OpenAPI JSON 格式
- ✅ 不包装在统一响应格式中(使用
@SkipTransform()装饰器) - ✅ 公开访问,无需认证(
@Public()装饰器) - ✅ 不在 Swagger UI 中显示(
@ApiExcludeEndpoint()避免递归)
2. API 统计接口
接口: GET /api/swagger/stats
功能: 返回 API 统计信息(总接口数、分组、服务器等)
特性:
- ✅ 包装在统一响应格式中(正常使用 TransformInterceptor)
- ✅ 公开访问
- ✅ 在 Swagger UI 中显示
🔧 技术实现
核心组件
1. SkipTransform 装饰器
文件: src/common/decorators/skip-transform.decorator.ts
typescript
import { SetMetadata } from '@nestjs/common';
export const SKIP_TRANSFORM_KEY = 'skipTransform';
export const SkipTransform = () => SetMetadata(SKIP_TRANSFORM_KEY, true);作用: 标记接口跳过全局的 TransformInterceptor
2. TransformInterceptor 更新
文件: src/common/interceptors/transform.interceptor.ts
新增逻辑:
typescript
import { Reflector } from '@nestjs/core';
import { SKIP_TRANSFORM_KEY } from '../decorators/skip-transform.decorator';
@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> {
constructor(private reflector: Reflector) {}
intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {
// 检查是否跳过转换
const skipTransform = this.reflector.getAllAndOverride<boolean>(SKIP_TRANSFORM_KEY, [
context.getHandler(),
context.getClass(),
]);
if (skipTransform) {
// 直接返回原始数据,不做转换
return next.handle();
}
// 正常转换(包装响应)
return next.handle().pipe(
map((data) => ({
success: true,
data: data,
message: 'success',
timestamp: new Date().toISOString(),
})),
);
}
}功能:
- 使用 Reflector 检查
@SkipTransform()元数据 - 如果标记跳过,直接返回原始数据
- 否则包装成统一格式
3. SwaggerController
文件: src/modules/swagger/swagger.controller.ts
typescript
@Controller('swagger')
export class SwaggerController {
@Get('json')
@Public() // 公开访问
@SkipTransform() // 跳过响应包装
@ApiExcludeEndpoint() // 不在 Swagger UI 显示
getOpenApiJson() {
return this.swaggerService.getDocument();
}
@Get('stats')
@Public() // 公开访问
// 不使用 @SkipTransform(),会正常包装
getStats() {
return this.swaggerService.getStats();
}
}4. SwaggerService
文件: src/modules/swagger/swagger.service.ts
typescript
@Injectable()
export class SwaggerService {
private document: OpenAPIObject;
setDocument(document: OpenAPIObject) {
this.document = document;
}
getDocument(): OpenAPIObject {
return this.document;
}
getStats() {
// 返回统计信息
return {
title: this.document.info?.title,
version: this.document.info?.version,
totalEndpoints: ...,
totalPaths: ...,
tags: ...,
};
}
}5. main.ts 集成
文件: src/main.ts
typescript
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 配置 Swagger 并获取 document
const document = setupSwagger(app);
// 将 document 注入到 SwaggerService
const swaggerService = app.get(SwaggerService);
swaggerService.setDocument(document);
await app.listen(port);
}📋 响应格式对比
/api/swagger/json - 原始 OpenAPI JSON
json
{
"openapi": "3.0.0",
"info": { ... },
"paths": { ... },
"components": { ... }
}✅ 正确: 直接返回标准 OpenAPI 格式,Apifox 可以正确解析
/api/swagger/stats - 统一包装格式
json
{
"success": true,
"data": {
"title": "NestBase API Documentation",
"version": "1.0",
"totalEndpoints": 18,
...
},
"message": "success",
"timestamp": "2025-10-16T06:30:00.000Z"
}✅ 正确: 包装在统一响应格式中,与其他 API 保持一致
🎯 使用场景
场景 1: Apifox 自动导入
URL: http://localhost:3001/api/swagger/jsonApifox 会:
- 发送 GET 请求到该 URL
- 接收到标准 OpenAPI JSON
- 解析并导入所有接口定义
- 自动创建接口分组和数据模型
场景 2: 查看 API 统计
bash
curl http://localhost:3001/api/swagger/stats
# 响应(包装格式)
{
"success": true,
"data": {
"totalEndpoints": 18,
"totalPaths": 14,
"tags": [...]
}
}🛠️ 扩展用法
其他需要跳过包装的场景
如果有其他接口需要返回原始数据,只需添加 @SkipTransform() 装饰器:
typescript
@Controller('files')
export class FilesController {
@Get('download')
@SkipTransform() // 直接返回文件流
downloadFile() {
return streamFile();
}
}适用场景
- ✅ 文件下载(返回 Buffer/Stream)
- ✅ 第三方格式规范(OpenAPI、RSS、Sitemap 等)
- ✅ Webhook 回调(需要特定响应格式)
- ✅ 健康检查端点(返回简单状态)
📊 技术优势
- 标准兼容: 符合 OpenAPI 3.0 规范,所有工具都能识别
- 灵活控制: 通过装饰器精确控制哪些接口需要包装
- 统一管理: 所有响应处理逻辑集中在 TransformInterceptor
- 易于扩展: 新增跳过包装的场景只需添加装饰器
- 类型安全: 使用 TypeScript 保证类型正确性
✅ 验证测试
测试 OpenAPI JSON
bash
# 应该直接返回 OpenAPI JSON(不包装)
curl http://localhost:3001/api/swagger/json | jq '.openapi'
# 输出: "3.0.0"
# 不应该有 success 字段
curl http://localhost:3001/api/swagger/json | jq '.success'
# 输出: null测试 API 统计
bash
# 应该返回包装格式
curl http://localhost:3001/api/swagger/stats | jq '.success'
# 输出: true
# data 字段包含统计信息
curl http://localhost:3001/api/swagger/stats | jq '.data.totalEndpoints'
# 输出: 18📚 相关文件
src/common/decorators/skip-transform.decorator.ts- 跳过转换装饰器src/common/interceptors/transform.interceptor.ts- 响应转换拦截器(已更新)src/modules/swagger/swagger.controller.ts- Swagger 控制器src/modules/swagger/swagger.service.ts- Swagger 服务src/modules/swagger/swagger.module.ts- Swagger 模块src/config/swagger.config.ts- Swagger 配置src/main.ts- 应用入口(注入 document)
最后更新: 2025-10-16 版本: 1.0.0 状态: ✅ 已实现并测试通过