Skip to content

📄 OpenAPI 文档导出功能 - 更新总结

✅ 已完成的工作

1. 实现 OpenAPI JSON 导出接口

创建了 /api/swagger/json 接口,直接返回标准 OpenAPI 3.0 JSON 格式,可用于 Apifox、Postman 等工具自动导入。

关键特性:

  • ✅ 直接返回 OpenAPI JSON(不包装在 { success, data } 中)
  • ✅ 公开访问,无需认证
  • ✅ 符合 OpenAPI 3.0 规范
  • ✅ 包含所有接口定义、Schema、认证配置

2. 创建 @SkipTransform() 装饰器

实现了跳过全局响应转换的机制,用于需要返回原始格式的接口。

文件: src/common/decorators/skip-transform.decorator.ts

typescript
export const SkipTransform = () => SetMetadata(SKIP_TRANSFORM_KEY, true);

3. 更新 TransformInterceptor

增强了全局拦截器,支持检查 @SkipTransform() 装饰器。

文件: src/common/interceptors/transform.interceptor.ts

新增功能:

  • 检查是否标记为 @SkipTransform()
  • 如果标记,直接返回原始数据
  • 否则包装成统一响应格式

4. 创建 Swagger 模块

新增文件:

  • src/modules/swagger/swagger.controller.ts - 控制器
  • src/modules/swagger/swagger.service.ts - 服务
  • src/modules/swagger/swagger.module.ts - 模块

提供接口:

  • GET /api/swagger/json - OpenAPI JSON 文档(不包装)
  • GET /api/swagger/stats - API 统计信息(包装)

5. 更新配置和文档

更新的文件:

  • src/main.ts - 注入 document 到 SwaggerService
  • src/config/swagger.config.ts - 添加服务器配置
  • src/app.module.ts - 注册 SwaggerModule
  • README.md - 添加 OpenAPI 导出说明
  • CLAUDE.md - 更新 API 端点列表

新增文档:

  • APIFOX_IMPORT_GUIDE.md - Apifox 导入详细指南
  • OPENAPI_IMPLEMENTATION.md - 技术实现说明
  • test-swagger-api.sh - API 测试脚本

🎯 使用方式

在 Apifox 中导入

  1. URL 自动导入(推荐):

    http://localhost:3000/api/swagger/json
  2. 手动下载导入:

    bash
    curl http://localhost:3000/api/swagger/json > openapi.json
    # 然后在 Apifox 中导入该文件

查看 API 统计

bash
curl http://localhost:3000/api/swagger/stats

📊 响应格式对比

OpenAPI JSON 接口(不包装)

bash
curl http://localhost:3000/api/swagger/json

响应:

json
{
  "openapi": "3.0.0",
  "info": { ... },
  "paths": { ... },
  "components": { ... }
}

API 统计接口(包装)

bash
curl http://localhost:3000/api/swagger/stats

响应:

json
{
  "success": true,
  "data": {
    "totalEndpoints": 20,
    "totalPaths": 16,
    ...
  },
  "message": "success",
  "timestamp": "..."
}

🔧 技术实现要点

问题:统一响应格式导致 Apifox 无法解析

原因: 全局的 TransformInterceptor 会将所有响应包装成:

json
{
  "success": true,
  "data": { ... }
}

但 OpenAPI 规范要求直接返回 JSON,不能有额外包装。

解决方案

  1. 创建 @SkipTransform() 装饰器标记需要跳过包装的接口
  2. TransformInterceptor 中检查该装饰器
  3. 如果标记了,直接返回原始数据
  4. 在 SwaggerController 的 getOpenApiJson() 方法上使用该装饰器

📁 创建/修改的文件

新增文件(7个)

src/common/decorators/
└── skip-transform.decorator.ts         # 跳过转换装饰器

src/modules/swagger/
├── swagger.controller.ts               # Swagger 控制器
├── swagger.service.ts                  # Swagger 服务
└── swagger.module.ts                   # Swagger 模块

APIFOX_IMPORT_GUIDE.md                  # Apifox 导入指南
OPENAPI_IMPLEMENTATION.md               # 技术实现说明
test-swagger-api.sh                     # 测试脚本

修改文件(5个)

src/common/interceptors/
└── transform.interceptor.ts            # 添加跳过转换逻辑

src/config/
└── swagger.config.ts                   # 添加服务器配置

src/
├── main.ts                             # 注入 document
└── app.module.ts                       # 注册 SwaggerModule

README.md                               # 添加 OpenAPI 导出说明
CLAUDE.md                               # 更新 API 列表

✅ 验证清单

  • [x] OpenAPI JSON 接口直接返回标准格式(不包装)
  • [x] API 统计接口正常包装响应
  • [x] 在 Swagger UI 中隐藏 OpenAPI JSON 接口(避免递归)
  • [x] 所有其他接口仍然正常包装
  • [x] TypeScript 编译通过
  • [x] 文档已更新

🎓 学习价值

这次实现展示了以下技术点:

  1. 装饰器模式 - 使用自定义装饰器控制行为
  2. 拦截器灵活性 - 在全局拦截器中实现条件逻辑
  3. Reflector 元数据 - 使用 NestJS Reflector 读取装饰器元数据
  4. OpenAPI 规范 - 符合标准的 API 文档格式
  5. 响应格式控制 - 精细控制不同接口的响应格式

💡 扩展用法

@SkipTransform() 装饰器还可用于其他场景:

typescript
// 文件下载
@Get('download')
@SkipTransform()
downloadFile() {
  return streamFile();
}

// Webhook 回调
@Post('webhook')
@SkipTransform()
handleWebhook() {
  return { status: 'ok' };  // 第三方要求的格式
}

// 健康检查
@Get('health')
@SkipTransform()
healthCheck() {
  return { status: 'healthy' };
}

📚 相关文档


更新日期: 2025-10-16 版本: 1.0.0 状态: ✅ 已完成并测试通过