Swagger 在 Node.js/Express 项目中的常见落地方式是:swagger-jsdoc 把路由文件里的 JSDoc 注释转成 OpenAPI 规范,swagger-ui-express 把规范渲染成可交互的 API 文档页面。原文以用户管理 API 为例,使用 OpenAPI 3.0.0,扫描路径为 ./src/routes/*.js,文档挂载到 /api-docs,并通过 bearerAuth 声明 JWT 认证。
一、安装依赖- npm install swagger-jsdoc swagger-ui-express
复制代码 其中 swagger-jsdoc 负责把 JSDoc 注释转换为 Swagger/OpenAPI 规范,swagger-ui-express 负责提供 Swagger UI 界面。
二、配置 src/config/swagger.js
核心配置包括 openapi 版本、info 信息、servers、securitySchemes、schemas、tags、security 和 apis 扫描路径。原文在 components.schemas 中还定义了 LoginResponse、UserListResponse、User、CreateUserRequest 等结构,下面保留核心配置结构:- // src/config/swagger.js
- const swaggerJsdoc = require('swagger-jsdoc')
- const options = {
- definition: {
- openapi: '3.0.0',
- info: {
- title: '用户管理 API 文档',
- version: '1.0.0',
- description: '基于 Express + JWT 的用户管理接口文档',
- contact: { name: 'API Support', email: 'support@example.com' },
- license: { name: 'MIT', url: 'https://opensource.org/licenses/MIT' }
- },
- servers: [
- { url: 'http://localhost:3000/api', description: '开发服务器' },
- { url: 'https://api.example.com/api', description: '生产服务器' }
- ],
- components: {
- securitySchemes: {
- bearerAuth: {
- type: 'http',
- scheme: 'bearer',
- bearerFormat: 'JWT',
- description: '输入 Bearer Token'
- }
- },
- schemas: {
- ApiResponse: {
- type: 'object',
- properties: {
- code: { type: 'integer', description: '状态码:0-成功,其他-失败', example: 0 },
- msg: { type: 'string', description: '响应消息', example: '操作成功' },
- data: { type: 'object', description: '响应数据' }
- }
- },
- LoginRequest: {
- type: 'object',
- required: ['username', 'password'],
- properties: {
- username: { type: 'string', description: '用户名', example: 'admin' },
- password: { type: 'string', description: '密码', example: '123456' }
- }
- }
- }
- },
- security: [{ bearerAuth: [] }],
- tags: [
- { name: '认证', description: '用户认证相关接口' },
- { name: '用户管理', description: '用户管理相关接口' }
- ]
- },
- apis: ['./src/routes/*.js']
- }
- module.exports = swaggerJsdoc(options)
复制代码
三、在 app.js 中集成 Swagger UI- const express = require('express')
- const cors = require('cors')
- require('dotenv').config()
- const swaggerUi = require('swagger-ui-express')
- const swaggerSpec = require('./config/swagger')
- const authRoutes = require('./routes/auth')
- const userRoutes = require('./routes/user')
- const app = express()
- const PORT = process.env.PORT || 3000
- app.use(cors())
- app.use(express.json())
- app.use(express.urlencoded({ extended: true }))
- app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec, {
- explorer: true,
- customCss: '.swagger-ui .topbar { display: none }',
- customSiteTitle: '用户管理 API 文档',
- swaggerOptions: { persistAuthorization: true }
- }))
- app.get('/api-docs.json', (req, res) => {
- res.setHeader('Content-Type', 'application/json')
- res.send(swaggerSpec)
- })
- app.use('/api/auth', authRoutes)
- app.use('/api/user', userRoutes)
- app.listen(PORT, () => {
- console.log(`服务器地址: http://localhost:${PORT}`)
- console.log(`Swagger 文档: http://localhost:${PORT}/api-docs`)
- })
复制代码 这里 explorer 用于显示搜索框,customCss 隐藏顶部栏,customSiteTitle 设置页面标题,persistAuthorization 持久化认证信息;/api-docs.json 可导出 Swagger 规范给其他工具使用。
四、在路由中写 JSDoc 注释
认证路由的登录接口可按如下方式声明请求体和响应:- /**
- * @swagger
- * /auth/login:
- * post:
- * summary: 用户登录
- * description: 使用用户名和密码登录,返回 JWT Token
- * tags: [认证]
- * requestBody:
- * required: true
- * content:
- * application/json:
- * schema:
- * $ref: '#/components/schemas/LoginRequest'
- * responses:
- * 200:
- * description: 登录成功
- * content:
- * application/json:
- * schema:
- * $ref: '#/components/schemas/LoginResponse'
- * 400:
- * description: 请求参数错误
- * 401:
- * description: 用户名或密码错误
- * 403:
- * description: 用户已被禁用
- */
- router.post('/login', async (req, res) => {
- // ... 原有代码
- })
复制代码 用户列表接口需要声明 security 和 query 参数,参数含义包括 page 页码、size 每页大小、keyword 关键词搜索、status 状态 0/1、role 角色枚举:- /**
- * @swagger
- * /user/list:
- * get:
- * summary: 获取用户列表
- * tags: [用户管理]
- * security:
- * - bearerAuth: []
- * parameters:
- * - in: query
- * name: page
- * schema: { type: integer, default: 1 }
- * description: 页码
- * - in: query
- * name: size
- * schema: { type: integer, default: 10 }
- * description: 每页大小
- * - in: query
- * name: keyword
- * schema: { type: string }
- * description: 关键词搜索(用户名或邮箱)
- * - in: query
- * name: status
- * schema: { type: integer, enum: [0, 1] }
- * description: 用户状态:0-禁用,1-启用
- * - in: query
- * name: role
- * schema: { type: string, enum: [admin, manager, user, developer] }
- * description: 用户角色
- * responses:
- * 200:
- * description: 获取用户列表成功
- * content:
- * application/json:
- * schema:
- * $ref: '#/components/schemas/UserListResponse'
- * 400:
- * description: 请求参数错误
- * 401:
- * description: 未登录或 Token 无效
- */
- router.get('/list', authenticate, (req, res) => {
- // ... 原有代码
- })
复制代码
五、访问与调试
启动服务后访问 http://localhost:3000/api-docs。Swagger UI 顶部提供搜索框,左侧按 Tags 分组显示接口,右侧显示选中接口的详细信息。需要 Token 的接口先点击右上角 Authorize,输入 Bearer <你的Token> 并确认。测试接口时展开目标接口,点击 Try it out,填写参数后点击 Execute 查看响应结果。原文还列出的导出方案包括使用 swagger-to-markdown 以及通过 Swagger Editor 导出 Markdown/PDF;常见问题部分列出了 Swagger 页面空白、JSDoc 注释没有被解析、Swagger 中显示不了中文三类现象。
整体上,这种写法的关键是让路由注释、swagger.js 中的 schemas 定义和实际接口实现保持同步;接口路径、参数、JWT 认证声明一旦变化,也要同步修改对应 JSDoc 注释,否则最终渲染出的 API 文档会与实现不一致。 |