23.08-Swagger接口调试

要点

  • Swagger UI 提供交互式 API 文档,可以直接在浏览器中测试接口
  • 支持认证、参数填充、响应查看等功能
  • 可以配置多个环境(开发、测试、生产)
  • 是前后端联调的重要工具

内容

1. 基础配置

// src/index.ts
import { OpenAPIHono } from '@hono/zod-openapi'
import { SwaggerUI } from '@hono/swagger-ui'
 
const app = new OpenAPIHono()
 
// ... 定义路由
 
// OpenAPI spec
app.doc('/doc', {
  openapi: '3.0.0',
  info: {
    title: 'AI Gateway API',
    version: '1.0.0',
  },
})
 
// Swagger UI
app.get('/swagger', SwaggerUI({ url: '/doc' }))
 
export default app

访问 http://localhost:8787/swagger 可以看到 Swagger UI 界面。

2. Swagger UI 功能

2.1 测试接口

  1. 展开一个接口(如 POST /v1/chat/completions)
  2. 点击 "Try it out" 按钮
  3. 填写参数
  4. 点击 "Execute" 按钮
  5. 查看响应结果

2.2 认证

如果配置了认证,Swagger UI 会显示 "Authorize" 按钮:

  1. 点击 "Authorize" 按钮
  2. 输入 token 或 API key
  3. 后续请求会自动带上认证信息

3. 多环境配置

// src/index.ts
app.doc('/doc', {
  openapi: '3.0.0',
  info: {
    title: 'AI Gateway API',
    version: '1.0.0',
  },
  servers: [
    {
      url: 'http://localhost:8787',
      description: '本地开发',
    },
    {
      url: 'https://api-staging.example.com',
      description: '测试环境',
    },
    {
      url: 'https://api.example.com',
      description: '生产环境',
    },
  ],
})

在 Swagger UI 顶部可以选择不同的服务器环境。

4. 自定义 Swagger UI

import { SwaggerUI } from '@hono/swagger-ui'
 
app.get('/swagger', (c) => {
  const html = `
<!DOCTYPE html>
<html>
<head>
  <title>AI Gateway API</title>
  <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css" />
</head>
<body>
  <div id="swagger-ui"></div>
  <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
  <script>
    SwaggerUIBundle({
      url: '/doc',
      dom_id: '#swagger-ui',
      presets: [
        SwaggerUIBundle.presets.apis,
        SwaggerUIBundle.SwaggerUIStandalonePreset
      ],
      layout: "StandaloneLayout",
      deepLinking: true,
      showExtensions: true,
      showCommonExtensions: true,
      validatorUrl: null,
      // 自定义配置
      docExpansion: 'list',  // 默认展开级别
      defaultModelsExpandDepth: 1,
      defaultModelExpandDepth: 1,
    })
  </script>
</body>
</html>
  `
 
  return c.html(html)
})

5. 保护 Swagger UI

生产环境应该保护 Swagger UI,避免公开访问:

// 方式 1:基本认证
import { basicAuth } from 'hono/basic-auth'
 
app.get('/swagger', basicAuth({
  username: 'admin',
  password: process.env.SWAGGER_PASSWORD!,
}), (c) => {
  return c.html(SwaggerUI({ url: '/doc' }))
})
 
// 方式 2:只对特定 IP 开放
app.get('/swagger', async (c, next) => {
  const ip = c.req.header('CF-Connecting-IP')
  const allowedIPs = ['10.0.0.0/8', '172.16.0.0/12']
 
  if (!isIPInRanges(ip, allowedIPs)) {
    return c.json({ error: 'Forbidden' }, 403)
  }
 
  await next()
}, SwaggerUI({ url: '/doc' }))
 
// 方式 3:只在开发环境启用
if (process.env.NODE_ENV === 'development') {
  app.get('/swagger', SwaggerUI({ url: '/doc' }))
}

6. 常用配置选项

app.get('/swagger', (c) => {
  return c.html(`
<!DOCTYPE html>
<html>
<head>
  <title>API Documentation</title>
  <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css" />
  <style>
    /* 自定义样式 */
    .swagger-ui .topbar { display: none; }
  </style>
</head>
<body>
  <div id="swagger-ui"></div>
  <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
  <script>
    const ui = SwaggerUIBundle({
      url: '/doc',
      dom_id: '#swagger-ui',
      presets: [
        SwaggerUIBundle.presets.apis,
        SwaggerUIBundle.SwaggerUIStandalonePreset
      ],
      layout: "StandaloneLayout",
      
      // 请求配置
      requestInterceptor: (request) => {
        // 添加自定义 header
        request.headers['X-Custom-Header'] = 'value'
        return request
      },
      
      // 响应配置
      responseInterceptor: (response) => {
        console.log('Response:', response)
        return response
      },
      
      //  onComplete: () => {
        console.log('Swagger UI loaded')
      },
      
      // 其他配置
      deepLinking: true,
      displayOperationId: true,
      filter: true,
      showExtensions: true,
      showCommonExtensions: true,
      tryItOutEnabled: true,  // 默认启用 Try it out
      requestSnippetsEnabled: true,  // 显示请求代码片段
    })
  </script>
</body>
</html>
  `)
})

7. 导出和分享

7.1 导出 OpenAPI spec

# JSON 格式
curl http://localhost:8787/doc > openapi.json
 
# YAML 格式
npm install js-yaml
node -e "const yaml = require('js-yaml'); const fs = require('fs'); const spec = JSON.parse(fs.readFileSync('openapi.json')); fs.writeFileSync('openapi.yaml', yaml.dump(spec));"

7.2 分享到在线平台

可以将 OpenAPI spec 上传到以下平台:

8. 实战:完整的 Swagger 配置

// src/index.ts
import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi'
import { SwaggerUI } from '@hono/swagger-ui'
 
const app = new OpenAPIHono()
 
// 定义路由
app.openapi(
  createRoute({
    method: 'post',
    path: '/v1/chat/completions',
    tags: ['AI 聊天'],
    summary: '调用 LLM',
    description: '发送消息给 LLM,获取回复',
    request: {
      body: {
        content: {
          'application/json': {
            schema: z.object({
              messages: z.array(z.object({
                role: z.enum(['system', 'user', 'assistant']),
                content: z.string(),
              })),
              model: z.enum(['gpt-4', 'gpt-3.5-turbo']),
              temperature: z.number().optional(),
            }),
          },
        },
      },
    },
    responses: {
      200: {
        content: {
          'application/json': {
            schema: z.object({
              id: z.string(),
              choices: z.array(z.object({
                message: z.object({
                  role: z.enum(['assistant']),
                  content: z.string(),
                }),
              })),
              usage: z.object({
                total_tokens: z.number(),
              }),
            }),
          },
        },
        description: '成功响应',
      },
    },
  }),
  async (c) => {
    const body = c.req.valid('json')
    const result = await callLLM(body)
    return c.json(result, 200)
  }
)
 
// OpenAPI 文档
app.doc('/doc', {
  openapi: '3.0.0',
  info: {
    title: 'AI Gateway API',
    version: '1.0.0',
    description: 'AI 网关 API 文档',
    contact: {
      name: 'API Support',
      email: '[email protected]',
    },
  },
  servers: [
    { url: 'http://localhost:8787', description: '开发环境' },
    { url: 'https://api.example.com', description: '生产环境' },
  ],
  components: {
    securitySchemes: {
      BearerAuth: {
        type: 'http',
        scheme: 'bearer',
        bearerFormat: 'JWT',
      },
    },
  },
  security: [{ BearerAuth: [] }],
})
 
// Swagger UI(带自定义配置)
app.get('/swagger', (c) => {
  return c.html(`
<!DOCTYPE html>
<html>
<head>
  <title>AI Gateway API Documentation</title>
  <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css" />
</head>
<body>
  <div id="swagger-ui"></div>
  <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
  <script>
    SwaggerUIBundle({
      url: '/doc',
      dom_id: '#swagger-ui',
      presets: [
        SwaggerUIBundle.presets.apis,
        SwaggerUIBundle.SwaggerUIStandalonePreset
      ],
      layout: "StandaloneLayout",
      deepLinking: true,
      tryItOutEnabled: true,
      requestSnippetsEnabled: true,
    })
  </script>
</body>
</html>
  `)
})
 
export default app

9. 小结

Swagger 接口调试的关键点:

  1. 交互式文档:Swagger UI 提供可视化的 API 文档和测试界面
  2. 多环境支持:配置多个服务器环境,方便切换测试
  3. 认证支持:支持 JWT、API Key 等多种认证方式
  4. 自定义配置:可以自定义 UI 样式、默认展开级别等
  5. 安全保护:生产环境应该保护 Swagger UI,避免公开访问
  6. 导出分享:可以导出 OpenAPI spec 分享到在线平台

一句话带走:

Swagger UI 是前后端联调的利器,后端提供交互式文档,前端可以直接在浏览器中测试接口,大幅提升联调效率。