Files
d8d-vite-starter/.roo/rules/07-openapi.md
D8D Developer b9a3c991d0 update
2025-06-27 01:56:30 +00:00

6.6 KiB
Raw Blame History

Hono OpenAPI规范

常见不规范问题

  1. 路径参数问题:

    • 使用冒号定义路径参数: /:id
    • 必须使用花括号: /{id}
  2. 参数Schema缺失:

    • 未定义params Schema
    • 必须定义并添加OpenAPI元数据
  3. 参数获取方式:

    • 使用c.req.param()
    • 必须使用c.req.valid('param')
  4. URL参数类型转换:

    • 直接使用z.number()验证URL查询参数
    • 必须使用z.coerce.number()自动转换字符串参数
  5. OpenAPI元数据:

    • 路径参数缺少OpenAPI描述
    • 必须包含example和description
  6. api响应:

    • 200响应码缺少
    • 200也必须写c.json(result, 200)
  7. 认证中间件:

    • security: [{ Bearer: [] }],
    • middleware: [authMiddleware],
  8. 子路由路径:

    • path: '/users',
    • path: '/',
    • path: '/users/{id}',
    • path: '/{id}',

核心规范

1. 路由定义

2. 查询参数处理

  • URL参数类型:

    • URL查询参数总是以字符串形式传递
    • 必须正确处理字符串到其他类型的转换
  • 数字参数处理:

    // 错误方式 - 直接使用z.number()
    z.number().int().positive() // 无法处理字符串参数
    
    // 正确方式 - 使用z.coerce.number()
    z.coerce.number().int().positive() // 自动转换字符串参数
    
  • 布尔参数处理:

    // 错误方式 - 直接使用z.boolean()
    z.boolean() // 无法处理字符串参数
    
    // 正确方式 - 使用z.coerce.boolean()
    z.coerce.boolean() // 自动转换字符串参数
    
  • 路径参数:

    • 必须使用花括号 {} 定义 (例: /{id})
    • 必须定义 params Schema 并添加 OpenAPI 元数据:
      const GetParams = z.object({
        id: z.string().openapi({
          param: { name: 'id', in: 'path' },
          example: '1',
          description: '资源ID'
        })
      });
      
    • 路由定义中必须包含 params 定义:
      request: { params: GetParams }
      
    • 必须使用 c.req.valid('param') 获取路径参数
  • 请求定义:

    request: {
      body: {
        content: {
          'application/json': { schema: YourZodSchema }
        }
      }
    }
    
  • 响应定义:

    responses: {
      200: {
        description: '成功响应描述',
        content: { 'application/json': { schema: SuccessSchema } }
      },
      400: {
        description: '客户端错误',
        content: { 'application/json': { schema: ErrorSchema } }
      },
      500: {
        description: '服务器错误',
        content: { 'application/json': { schema: ErrorSchema } }
      }
    }
    

    列表响应定义示例

    // 列表响应Schema, 响应时data应统一用实体中定义的schema
    import { RackInfoSchema } from '@/server/modules/racks/rack-info.entity';
    const RackListResponse = z.object({
      data: z.array(RackInfoSchema),
      pagination: z.object({
        total: z.number().openapi({
          example: 100,
          description: '总记录数'
        }),
        current: z.number().openapi({
          example: 1,
          description: '当前页码'
        }),
        pageSize: z.number().openapi({
          example: 10,
          description: '每页数量'
        })
      })
    });
    
  • 路由示例:

    const routeDef = createRoute({
      method: 'post',
      path: '/',
      middleware: [authMiddleware],
      request: {
        body: {
          content: { 'application/json': { schema: CreateSchema } }
        }
      },
      responses: {
        200: { ... },
        400: { ... },
        500: { ... }
      }
    });
    

2. 错误处理

  • 错误响应必须使用统一格式: { code: number, message: string }
  • 必须与OpenAPI定义完全一致
  • 处理示例:
    try {
      // 业务逻辑
    } catch (error) {
      return c.json({ code: 500, message: '操作失败' }, 500);
    }
    

3. dataSource引入

  • 示例:
    import { AppDataSource } from '@/server/data-source';
    

4. service初始化

  • 示例:
    import { WorkspaceService } from '@/server/modules/workspaces/workspace.service';
    const workspaceService = new WorkspaceService(AppDataSource);
    
    

5. 用户context获取

  • 示例:
    const user = c.get('user');
    
    • 注意: 确保 c.get('user') 已经在 authMiddleware 中设置

6. AuthContext引用

  • 示例:
    import { AuthContext } from '@/server/types/context';
    

7. createRoute, OpenAPIHono 引入

  • 示例:
    import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
    

8. ErrorSchema 引入

  • 示例:
    import { ErrorSchema } from '@/server/utils/errorHandler';
    

进阶规范

1. 路由聚合

当多个相关路由需要组合时:

  1. 文件结构:
    • 拆分为独立文件 (create.ts, list.ts 等)
    • 创建 index.ts 聚合所有子路由
src/server/api/
├── [resource]/          # 资源路由目录
│   ├── [id]/           # 带ID的子路由
│   │   ├── get.ts      # 获取单条
│   │   ├── put.ts      # 更新单条  
│   │   └── delete.ts   # 删除单条
│   ├── get.ts          # 列表查询
│   ├── post.ts         # 创建资源
│   └── index.ts        # 聚合导出
  1. 实现:

     import listRoute from './get';
     import createRackRoute from './post';
     import getByIdRoute from './[id]/get';
     import updateRoute from './[id]/put';
     import deleteRoute from './[id]/delete';
     import { OpenAPIHono } from '@hono/zod-openapi';
    
     const app = new OpenAPIHono()
       .route('/', listRoute)
       .route('/', createRackRoute)
       .route('/', getByIdRoute)
       .route('/', updateRoute)
       .route('/', deleteRoute)
    
     export default app;
    
  2. 优势:

    • 保持模块化
    • 简化维护
    • 统一API入口

路由文件代码结构规范

+imports: 依赖导入 +serviceInit: 服务初始化 +paramsSchema: 路径参数定义 +responseSchema: 响应定义 +errorSchema: 错误定义 +routeDef: 路由定义 +app: 路由实例

src/server/api.ts 统一引入

import authRoute from '@/server/api/auth/index'
const routes = api.route('/api/v1/auth', authRoute)

完整示例

// 路由实例
const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
  try {
    // 业务逻辑
    return c.json(result, 200);
  } catch (error) {
    return c.json({ 
      code: 500, 
      message: error instanceOf Error ? error.message : '操作失败' 
    }, 500);
  }
});

export default app;