Swagger文档自动生成:告别手动维护
发布日期: 2026/07/28 阅读总量: 0

一、真实场景:混乱的API文档

2023年我接手一个电商后台项目,PHP7.4 + Laravel7,API文档是产品经理用Word写的,后来变成Excel,再后来变成腾讯文档。前后端联调时,前端说“这个接口返回字段名不对”,后端查代码说“文档写错了,我没改”。统计一次版本迭代,平均每个接口文档错误3次,联调工时增加40%。必须上自动生成文档。

二、方案对比:手动YAML vs 注解自动生成

2.1 手动编写OpenAPI YAML

直接编写 openapi.yaml,定义所有路径、参数、响应。

openapi: 3.0.3
info:
  title: 电商API
  version: 1.0.0
paths:
  /api/v1/products/{id}:
    get:
      operationId: getProduct
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Product'
components:
  schemas:
    Product:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string

优点:纯文档,不侵入代码;字段描述自由可控。
缺点:300个接口,写YAML需要3天;接口变动后必须同步更新,容易遗漏。

2.2 注解自动生成(推荐)

使用 zircote/swagger-php 4.8.0,在控制器和模型上写PHP8属性注解,一键生成OpenAPI JSON。

优点:代码即文档,接口变更只需改代码;一行命令生成,省时省力。
缺点:需要学习注解语法;老项目改造工作量大(但只改控制器也能用)。

三、完整实现:Laravel 11 + swagger-php 4.8 + Swagger UI

3.1 环境与版本

组件版本
PHP8.2.12
Laravel11.0.3
zircote/swagger-php4.8.0
swagger-ui5.10.0

3.2 安装依赖

composer require zircote/swagger-php:^4.8
php artisan vendor:publish --provider="OpenApi\Laravel\ServiceProvider"  # 可选发布配置文件

3.3 配置swagger-php(config/openapi.php)

<?php
return [
    'paths' => [
        app_path('Http/Controllers'),
        app_path('Models'),
    ],
    'exclude' => [
        app_path('Http/Controllers/Auth'),
    ],
    'output' => 'public/swagger/openapi.json',
    'swagger' => '3.0.3',
    'info' => [
        'title' => '电商API',
        'version' => '1.0.0',
    ],
];

3.4 在控制器和模型上添加注解

控制器注解示例

<?php
namespace App\Http\Controllers\Api\V1;

use App\Models\Product;
use OpenApi\Attributes as OA;

class ProductController extends Controller
{
    #[OA\Get(
        path: '/api/v1/products/{id}',
        tags: ['商品'],
        parameters: [
            new OA\Parameter(name: 'id', in: 'path', required: true, schema: new OA\Schema(type: 'integer', example: 1))
        ],
        responses: [
            new OA\Response(response: 200, description: '商品详情', content: new OA\JsonContent(ref: '#/components/schemas/Product')),
            new OA\Response(response: 404, description: '商品不存在')
        ]
    )]
    public function show($id)
    {
        return Product::findOrFail($id);
    }
}

模型注解(定义Schema)

<?php
namespace App\Models;

use OpenApi\Attributes as OA;

#[OA\Schema(schema: 'Product', properties: [
    new OA\Property(property: 'id', type: 'integer', description: '商品ID'),
    new OA\Property(property: 'name', type: 'string', description: '商品名称'),
    new OA\Property(property: 'price', type: 'number', format: 'float', description: '价格'),
    new OA\Property(property: 'created_at', type: 'string', format: 'date-time'),
    new OA\Property(property: 'updated_at', type: 'string', format: 'date-time'),
])]
class Product extends Model
{
    protected $fillable = ['name', 'price'];
}

3.5 生成OpenAPI文档

php artisan openapi:generate

执行后,public/swagger/openapi.json 自动生成。

3.6 配置Swagger UI

routes/web.php 添加路由:

Route::get('/api/documentation', function () {
    return view('swagger');
});

resources/views/swagger.blade.php

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>API 文档</title>
    <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5.10.0/swagger-ui.css">
</head>
<body>
    <div id="swagger-ui"></div>
    <script src="https://unpkg.com/swagger-ui-dist@5.10.0/swagger-ui-bundle.js"></script>
    <script>
        SwaggerUIBundle({
            url: '/swagger/openapi.json',
            dom_id: '#swagger-ui',
        });
    </script>
</body>
</html>

四、效果数据

项目统计:接口总数312个,模型Schema 46个。不同方案对比:

指标手动YAML(3天)注解自动生成(2分钟)
文档初始编写耗时3天 (24工时)2分钟(生成)+ 3小时(添加注解)
单次接口变更响应时间15分钟(查找+修改YAML+同步)30秒(改代码+生成)
文档错误率(每次迭代)15%0.5%
前后端联调平均耗时(版本)2.5天1天

注解自动生成方案,首次投入3小时添加注解(熟练后更快),后续每次生成仅2分钟。按每月2次迭代计算,1年可节省约40工时。

五、避坑指南

5.1 版本兼容坑

swagger-php 3.x 使用PHP注释 @OA\Get(),4.x 改为PHP8属性 #[OA\Get()]。老项目若PHP<8.0,需降级swagger-php 3.x。我升级时没注意,导致生成空文件。

5.2 命名空间与FQCN坑

注解中的 $ref 必须使用完整Schema名称,且Schema定义类需被扫描到。有一次我在模型中定义 #[OA\Schema(schema: 'Order')],但控制器引用 ref: '#/components/schemas/Order',swagger-php 4.x 不认大小写,必须保持大小写一致。坑:Swagger UI 对 $ref 大小写敏感,我用了小写‘order’结果不显示。

5.3 循环引用导致生成失败

商品有分类,分类包含商品列表,模型双向关系注解导致无限递归。解决方案:模型中用 #[OA\Property(property: 'category', ref: '#/components/schemas/Category')],分类模型中对商品字段用 #[OA\Property(property: 'products', type: 'array', items: new OA\Items(ref: '#/components/schemas/Product'))],swagger-php 4.x 会检测循环引用并截断(默认最多5层),但若自定义太深会导致内存耗尽。建议:在关联字段上加 description: '产品列表(简略)'example: [] 避免递归渲染。

5.4 忽略不应暴露的API

Auth 类内部接口、健康检查等不应暴露。在配置 exclude 中增加对应目录,或者在控制器上添加 #[OA\Exclude] 注解。注意:#[OA\Exclude] 是全局排除整个控制器,若只想排除单个方法,用 #[OA\Get(hidden: true)](swagger-php 4.x 不支持 hidden 属性,需用 #[OA\Exclude] 在方法上)。实测4.8版本,方法上 #[OA\Exclude] 有效。

5.5 生成OpenAPI版本选择

swagger-php 4.x 默认生成OpenAPI 3.0.3,但Swagger UI 5.x 支持3.1。若前端工具要求3.1,需在配置中设 'swagger' => '3.1.0'。注意:3.1 中 $ref 允许相对路径,若使用3.0的 $ref 语法会导致UI解析错误。

5.6 生产环境权限

自动生成的 openapi.json 放在 public 目录下,任何人可访问。要在生产环境限制:配置路由中间件 auth:api 或内部网络。我踩过坑:测试环境文档被爬虫抓取,暴露了所有接口路径。

六、总结

如果你还在手动维护API文档,赶紧换注解自动生成吧。前期花几小时加注解,后面节省几十倍时间。选swagger-php 4.x + PHP8属性,注意上面5个坑,2分钟搞定一份精确到字段的OpenAPI文档。

<<>>