Swagger UI 与文档发布教程

Swagger UI 是一种基于 OpenAPI 规范(原 Swagger 规范)的 API 文档可视化工具,它能将 API 规范文档转换为交互式 API 文档界面。

通过 Swagger UI,开发者和用户可以:

  • 查看 API 的详细信息
  • 直接在界面上测试 API 请求
  • 查看请求和响应示例
  • 了解不同 API 端点的功能和参数

Swagger UI 的优势

  1. 可视化:提供清晰、直观的 API 文档界面
  2. 交互性:支持在线测试 API,无需额外工具
  3. 实时更新:代码变更后,文档自动更新
  4. 标准化:基于 OpenAPI 规范,保持一致性
  5. 跨语言支持:适用于各种编程语言和框架

集成 Swagger UI

1. 在 Spring Boot 项目中集成

添加依赖

pom.xml 添加以下依赖:

实例

<!-- Springfox Swagger2 -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>3.0.0</version>
</dependency>

<!-- Springfox Swagger UI -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>3.0.0</version>
</dependency>

或者使用 SpringDoc OpenAPI:

实例

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-ui</artifactId>
    <version>1.6.14</version>
</dependency>

配置 Swagger

实例

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.example.controller"))
                .paths(PathSelectors.any())
                .build()
                .apiInfo(apiInfo());
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("API 接口文档")
                .description("API 接口详细描述")
                .version("1.0.0")
                .contact(new Contact("开发团队", "https://example.com", "team@example.com"))
                .build();
    }
}

对于 SpringDoc OpenAPI:

实例

import org.springdoc.core.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.Contact;

@Configuration
public class SwaggerConfig {
    @Bean
    public OpenAPI customOpenAPI() {
        return new OpenAPI()
                .info(new Info()
                        .title("API 接口文档")
                        .version("1.0.0")
                        .description("API 接口详细描述")
                        .contact(new Contact()
                                .name("开发团队")
                                .email("team@example.com")
                                .url("https://example.com")));
    }

    @Bean
    public GroupedOpenApi publicApi() {
        return GroupedOpenApi.builder()
                .group("public-apis")
                .pathsToMatch("/api/**")
                .build();
    }
}

2. 在 Node.js Express 项目中集成

安装依赖

npm install swagger-jsdoc swagger-ui-express --save

配置 Swagger

实例

const express = require('express');
const swaggerJsDoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');

const app = express();

// Swagger 配置
const swaggerOptions = {
  definition: {
    openapi: '3.0.0',
    info: {
      title: 'API 接口文档',
      version: '1.0.0',
      description: 'API 接口详细描述',
      contact: {
        name: '开发团队',
        email: 'team@example.com',
        url: 'https://example.com'
      }
    },
    servers: [
      {
        url: 'http://localhost:3000',
        description: '开发服务器'
      }
    ]
  },
  apis: ['./routes/*.js'] // API 路由文件的路径
};

const swaggerDocs = swaggerJsDoc(swaggerOptions);
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocs));

// ... 其他路由和中间件

app.listen(3000, () => {
  console.log('服务器运行在 http://localhost:3000');
  console.log('API 文档可在 http://localhost:3000/api-docs 访问');
});

3. 在 Python FastAPI 项目中使用

FastAPI 默认集成了 Swagger UI:

实例

from fastapi import FastAPI

app = FastAPI(
    title="API 接口文档",
    description="API 接口详细描述",
    version="1.0.0",
    contact={
        "name": "开发团队",
        "email": "team@example.com",
        "url": "https://example.com",
    },
)

@app.get("/")
async def root():
    return {"message": "Hello World"}

Swagger UI 默认可在 /docs 路径访问,可通过访问 http://localhost:8000/docs 查看文档。

详细内容参见:FastAPI 交互式 API 文档


编写 API 文档

1. Spring Boot 项目中的 API 文档注解

实例

import io.swagger.annotations.*;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/users")
@Api(tags = "用户管理")
public class UserController {

    @ApiOperation(value = "获取用户列表", notes = "分页获取所有用户信息")
    @ApiResponses({
        @ApiResponse(code = 200, message = "成功"),
        @ApiResponse(code = 400, message = "请求参数错误"),
        @ApiResponse(code = 500, message = "服务器内部错误")
    })
    @GetMapping("/")
    public Page<User> getUsers(
            @ApiParam(value = "页码", required = true) @RequestParam int page,
            @ApiParam(value = "每页记录数", required = true) @RequestParam int size) {
        // 业务逻辑
        return userService.getUsers(page, size);
    }

    @ApiOperation(value = "获取单个用户", notes = "根据ID获取用户信息")
    @GetMapping("/{id}")
    public User getUser(
            @ApiParam(value = "用户ID", required = true) @PathVariable Long id) {
        // 业务逻辑
        return userService.getUser(id);
    }

    @ApiOperation(value = "创建用户", notes = "创建新用户")
    @PostMapping("/")
    public User createUser(
            @ApiParam(value = "用户信息", required = true) @RequestBody UserDTO userDTO) {
        // 业务逻辑
        return userService.createUser(userDTO);
    }
}

对于 SpringDoc OpenAPI:

实例

import io.swagger.v3.oas.annotations.*;
import io.swagger.v3.oas.annotations.media.*;
import io.swagger.v3.oas.annotations.responses.*;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/users")
@Tag(name = "用户管理", description = "用户相关的API")
public class UserController {

    @Operation(
        summary = "获取用户列表",
        description = "分页获取所有用户信息"
    )
    @ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "成功"),
        @ApiResponse(responseCode = "400", description = "请求参数错误"),
        @ApiResponse(responseCode = "500", description = "服务器内部错误")
    })
    @GetMapping("/")
    public Page<User> getUsers(
            @Parameter(description = "页码", required = true) @RequestParam int page,
            @Parameter(description = "每页记录数", required = true) @RequestParam int size) {
        // 业务逻辑
        return userService.getUsers(page, size);
    }
}

2. Node.js Express 项目中的 API 文档注释

实例

/**
 * @swagger
 * /api/users:
 *   get:
 *     summary: 获取用户列表
 *     description: 分页获取所有用户信息
 *     tags: [用户管理]
 *     parameters:
 *       - in: query
 *         name: page
 *         schema:
 *           type: integer
 *         required: true
 *         description: 页码
 *       - in: query
 *         name: size
 *         schema:
 *           type: integer
 *         required: true
 *         description: 每页记录数
 *     responses:
 *       200:
 *         description: 成功
 *         content:
 *           application/json:
 *             schema:
 *               type: object
 *               properties:
 *                 data:
 *                   type: array
 *                   items:
 *                     $ref: '#/components/schemas/User'
 *                 total:
 *                   type: integer
 *       400:
 *         description: 请求参数错误
 *       500:
 *         description: 服务器内部错误
 */

router.get('/users', (req, res) => {
  // 业务逻辑
});

/**
 * @swagger
 * components:
 *   schemas:
 *     User:
 *       type: object
 *       required:
 *         - name
 *         - email
 *       properties:
 *         id:
 *           type: integer
 *           description: 用户ID
 *         name:
 *           type: string
 *           description: 用户名
 *         email:
 *           type: string
 *           format: email
 *           description: 用户邮箱
 */

3. Python FastAPI 项目中的 API 文档

实例

from fastapi import FastAPI, Query, Path
from pydantic import BaseModel
from typing import List, Optional

app = FastAPI(title="用户管理API")

class User(BaseModel):
    id: int
    name: str
    email: str
   
    class Config:
        schema_extra = {
            "example": {
                "id": 1,
                "name": "张三",
                "email": "zhangsan@example.com"
            }
        }

@app.get(
    "/api/users/",
    summary="获取用户列表",
    description="分页获取所有用户信息",
    response_model=List[User],
    tags=["用户管理"]
)
async def get_users(
    page: int = Query(..., description="页码", ge=1),
    size: int = Query(..., description="每页记录数", ge=1, le=100)
):
    # 业务逻辑
    return [
        {"id": 1, "name": "张三", "email": "zhangsan@example.com"},
        {"id": 2, "name": "李四", "email": "lisi@example.com"}
    ]

@app.get(
    "/api/users/{user_id}",
    summary="获取单个用户",
    description="根据ID获取用户信息",
    response_model=User,
    tags=["用户管理"]
)
async def get_user(
    user_id: int = Path(..., description="用户ID", ge=1)
):
    # 业务逻辑
    return {"id": user_id, "name": "张三", "email": "zhangsan@example.com"}

自定义 Swagger UI

1. Spring Boot 自定义配置

实例

@Bean
public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2)
            .select()
            .apis(RequestHandlerSelectors.basePackage("com.example.controller"))
            .paths(PathSelectors.regex("/api/.*"))
            .build()
            .apiInfo(apiInfo())
            .useDefaultResponseMessages(false)  // 禁用默认响应消息
            .globalResponseMessage(RequestMethod.GET, globalResponses())  // 自定义全局响应消息
            .securitySchemes(Arrays.asList(apiKey()))  // 配置安全认证
            .securityContexts(Arrays.asList(securityContext()));  // 配置安全上下文
}

private List<ResponseMessage> globalResponses() {
    return Arrays.asList(
            new ResponseMessageBuilder().code(200).message("成功").build(),
            new ResponseMessageBuilder().code(400).message("请求参数错误").build(),
            new ResponseMessageBuilder().code(401).message("未授权").build(),
            new ResponseMessageBuilder().code(403).message("禁止访问").build(),
            new ResponseMessageBuilder().code(500).message("服务器内部错误").build()
    );
}

private ApiKey apiKey() {
    return new ApiKey("JWT", "Authorization", "header");
}

private SecurityContext securityContext() {
    return SecurityContext.builder()
            .securityReferences(defaultAuth())
            .forPaths(PathSelectors.regex("/api/.*"))
            .build();
}

private List<SecurityReference> defaultAuth() {
    AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
    AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
    authorizationScopes[0] = authorizationScope;
    return Arrays.asList(new SecurityReference("JWT", authorizationScopes));
}

2. Express 自定义配置

实例

const options = {
  customCss: '.swagger-ui .topbar { display: none }',  // 自定义CSS
  customSiteTitle: "API 文档中心",  // 页面标题
  customfavIcon: "/favicon.png",  // 自定义图标
  swaggerOptions: {
    persistAuthorization: true,  // 保留授权信息
    docExpansion: 'none',  // 默认折叠所有接口
    tagsSorter: 'alpha',   // 标签按字母排序
    operationsSorter: 'alpha', // 操作按字母排序
    defaultModelsExpandDepth: -1, // 隐藏模型
    filter: true,  // 启用过滤
  }
};

app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocs, options));

3. FastAPI 自定义配置

实例

from fastapi import FastAPI
from fastapi.openapi.docs import get_swagger_ui_html
from fastapi.staticfiles import StaticFiles

app = FastAPI(
    title="API 接口文档",
    docs_url=None,  # 禁用默认的 Swagger UI
)

app.mount("/static", StaticFiles(directory="static"), name="static")

@app.get("/docs", include_in_schema=False)
async def custom_swagger_ui_html():
    return get_swagger_ui_html(
        openapi_url=app.openapi_url,
        title=app.title + " - API 文档",
        oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
        swagger_js_url="/static/swagger-ui-bundle.js",
        swagger_css_url="/static/swagger-ui.css",
        swagger_favicon_url="/static/favicon.png",
        swagger_ui_parameters={
            "docExpansion": "none",
            "defaultModelsExpandDepth": -1,
            "filter": True,
        }
    )

文档发布

1. 内部发布

对于团队内部使用,可以直接通过应用程序内部的 Swagger UI 访问:

  • Spring Boot: http://your-app-host:port/swagger-ui/index.html
  • Express: http://your-app-host:port/api-docs
  • FastAPI: http://your-app-host:port/docs

2. 静态文档生成

使用 Swagger Codegen 生成静态文档

# 安装 Swagger Codegen CLI
wget https://repo1.maven.org/maven2/io/swagger/codegen/v3/swagger-codegen-cli/3.0.35/swagger-codegen-cli-3.0.35.jar -O swagger-codegen-cli.jar

# 生成静态 HTML 文档
java -jar swagger-codegen-cli.jar generate -i http://your-app-host:port/v3/api-docs -l html2 -o ./api-docs

使用 Redoc 生成静态文档

# 安装 redoc-cli
npm install -g redoc-cli

# 生成静态 HTML 文档
redoc-cli bundle http://your-app-host:port/v3/api-docs -o ./api-docs/index.html

3. 整合到 CI/CD 流程

在 CI/CD 管道中添加文档生成和发布步骤:

Jenkins 流水线示例

实例

pipeline {
    agent any
   
    stages {
        // ... 其他构建和测试阶段
       
        stage('Generate API Documentation') {
            steps {
                sh 'java -jar swagger-codegen-cli.jar generate -i http://your-app-host:port/v3/api-docs -l html2 -o ./api-docs'
            }
        }
       
        stage('Publish Documentation') {
            steps {
                // 发布到 Nginx 静态文件服务器
                sh 'rsync -avz --delete ./api-docs/ user@doc-server:/var/www/api-docs/'
               
                // 或发布到对象存储(如 AWS S3)
                sh 'aws s3 sync ./api-docs/ s3://your-bucket/api-docs/ --delete'
            }
        }
    }
}

GitHub Actions 工作流示例

实例

name: Generate and Deploy API Docs

on:
  push:
    branches: [ main ]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
   
    steps:
    - uses: actions/checkout@v2
   
    - name: Set up JDK
      uses: actions/setup-java@v2
      with:
        distribution: 'adopt'
        java-version: '11'
   
    - name: Build application
      run: ./mvnw clean package
   
    - name: Start application
      run: |
        java -jar target/your-app.jar &
        sleep 30  # 等待应用启动
   
    - name: Generate API Documentation
      run: |
        wget -q https://repo1.maven.org/maven2/io/swagger/codegen/v3/swagger-codegen-cli/3.0.35/swagger-codegen-cli-3.0.35.jar -O swagger-codegen-cli.jar
        java -jar swagger-codegen-cli.jar generate -i http://localhost:8080/v3/api-docs -l html2 -o ./api-docs
   
    - name: Deploy to GitHub Pages
      uses: JamesIves/github-pages-deploy-action@4.1.5
      with:
        branch: gh-pages
        folder: api-docs

4. 使用 API 文档管理平台

除了自行部署,还可以使用专业的 API 文档管理平台:

  • Swagger Hub: 提供 API 设计和文档托管服务
  • Postman: 不仅可以测试 API,还可以发布 API 文档
  • ReadMe.io: 提供全面的 API 文档管理和开发者门户
  • Stoplight: 提供 API 设计、文档和治理工具

5. Docker 部署 Swagger UI

实例

FROM swaggerapi/swagger-ui:latest

# 设置环境变量
ENV SWAGGER_JSON=/swagger/openapi.json
ENV BASE_URL=/api-docs

# 复制 OpenAPI 规范文件
COPY openapi.json /swagger/

# 暴露端口
EXPOSE 8080

# 启动 Swagger UI
CMD ["sh", "/usr/share/nginx/docker-run.sh"]

构建和运行:

docker build -t my-swagger-ui .
docker run -p 8080:8080 my-swagger-ui

访问: http://localhost:8080/api-docs


最佳实践

1. 文档规范

  • 保持简洁明了: 描述应简洁而有针对性
  • 分组管理: 使用标签对 API 进行逻辑分组
  • 提供示例: 为请求和响应提供示例
  • 标准化错误处理: 统一错误响应格式和状态码
  • 版本控制: 在文档中明确 API 版本信息

2. 安全配置

  • 敏感信息处理: 不在文档中暴露敏感信息
  • 生产环境配置: 在生产环境中可选择性禁用或限制文档访问
  • 认证机制: 配置文档访问认证

实例

// Spring Boot 配置生产环境禁用 Swagger
@Bean
public Docket api() {
    return new Docket(DocumentationType.SWAGGER_2)
            .enable(!environment.acceptsProfiles(Profiles.of("prod")))
            // ... 其他配置
}

3. 自动化测试

结合文档和测试,确保文档内容与实际 API 行为保持一致:

实例

// Spring Boot 使用 REST Assured 和 Swagger 进行测试
@Test
public void validateSwaggerDocumentation() {
    // 获取 Swagger JSON
    String swaggerJson = given()
            .when()
            .get("/v3/api-docs")
            .then()
            .statusCode(200)
            .extract().asString();
   
    // 验证 Swagger 文档
    OpenAPI openAPI = new OpenAPIParser().readContents(swaggerJson, null, null).getOpenAPI();
    assertNotNull(openAPI);
   
    // 验证特定端点是否在文档中
    assertNotNull(openAPI.getPaths().get("/api/users"));
}

常见问题解决

1. Swagger UI 不显示或加载错误

  • 检查依赖版本: 确保依赖版本兼容
  • 检查配置类: 确保配置类正确注册
  • 检查路径映射: 确保路径映射正确
  • 检查跨域设置: 如果跨域访问,确保 CORS 配置正确

2. 接口信息不完整

  • 检查注解: 确保所有必要注解都已添加
  • 检查包扫描路径: 确保扫描路径包含所有控制器
  • 检查模型类: 确保模型类有正确的描述

3. 生产环境安全问题

配置生产环境禁用或保护文档:

# application-prod.properties
springdoc.swagger-ui.enabled=false
springdoc.api-docs.enabled=false

或使用基本认证保护:

实例

@Configuration
@Profile("prod")
public class SwaggerSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.requestMatchers()
            .antMatchers("/swagger-ui/**", "/v3/api-docs/**")
            .and()
            .authorizeRequests()
            .anyRequest().hasRole("ADMIN")
            .and()
            .httpBasic();
    }
}
```

总结

Swagger UI 是一个强大的 API 文档工具,通过正确配置和使用,可以大大提高 API 的可用性和开发效率。本教程介绍了 Swagger UI 的基本概念、集成方法、文档编写、自定义配置、文档发布以及最佳实践,希望对您的 API 文档工作有所帮助。

记住以下关键点:

  1. 选择适合项目技术栈的 Swagger 集成方式
  2. 精心设计和编写 API 文档注解或注释
  3. 根据需要自定义 Swagger UI 界面
  4. 选择合适的文档发布方式
  5. 遵循最佳实践,确保文档的准确性和安全性

通过合理使用 Swagger UI,您可以为您的 API 提供专业、交互式的文档,让您的 API 更易于理解和使用。