PHP实现OpenAPI/Swagger文档:利用Annotation或YAML文件自动生成API文档

好的,下面我们开始今天的讲座,主题是 PHP实现OpenAPI/Swagger文档:利用Annotation或YAML文件自动生成API文档

在现代Web API开发中,API文档的重要性不言而喻。它不仅方便开发者了解和使用API,还能提升团队协作效率,并作为API规范的有效载体。 OpenAPI(原Swagger)是业界广泛采用的API描述规范,它定义了一种标准化的方式来描述RESTful API。 本次讲座将深入探讨如何利用PHP来实现OpenAPI/Swagger文档的自动生成,主要包括基于Annotation和YAML文件两种方式,并提供实际代码示例。

一、OpenAPI/Swagger 概述

OpenAPI 规范 (OAS) 是一种用于描述 RESTful API 的标准化格式。它允许开发者以机器可读的方式定义API的接口、参数、响应、认证等信息。 Swagger 是一套围绕 OpenAPI 规范构建的开源工具集,包括Swagger Editor、Swagger UI和Swagger Codegen等,用于API的设计、构建、文档化和消费。

核心概念:

  • OpenAPI Specification (OAS): 描述API结构的规范。
  • Swagger Editor: 用于编写和编辑OpenAPI规范的Web编辑器。
  • Swagger UI: 将OpenAPI规范渲染成交互式API文档的UI工具。
  • Swagger Codegen: 根据OpenAPI规范生成服务器端和客户端代码的工具。

二、基于Annotation自动生成OpenAPI文档

Annotation(注解)是一种元数据,可以添加到PHP代码中,用于提供关于代码的额外信息。 我们可以使用特定的Annotation来描述API接口,然后通过工具解析这些Annotation,自动生成OpenAPI文档。

1. 安装必要的依赖

我们需要安装一个能够解析Annotation并生成OpenAPI文档的库。常用的库包括 zircote/swagger-php

composer require zircote/swagger-php

2. 使用Annotation描述API接口

在Controller或API接口的代码中使用Annotation来描述API的各种属性,例如路径、方法、参数、响应等。

<?php

namespace AppController;

use SymfonyComponentRoutingAnnotationRoute;
use SymfonyComponentHttpFoundationJsonResponse;

/**
 * @OAInfo(title="My API", version="1.0")
 */
class ApiController
{
    /**
     * @Route("/api/users/{id}", methods={"GET"})
     * @OAGet(
     *     path="/api/users/{id}",
     *     summary="Get a user by ID",
     *     @OAParameter(
     *         name="id",
     *         in="path",
     *         description="User ID",
     *         required=true,
     *         @OASchema(type="integer")
     *     ),
     *     @OAResponse(
     *         response=200,
     *         description="Successful operation",
     *         @OAJsonContent(
     *             type="object",
     *             @OAProperty(property="id", type="integer", example=1),
     *             @OAProperty(property="name", type="string", example="John Doe"),
     *             @OAProperty(property="email", type="string", example="[email protected]")
     *         )
     *     ),
     *     @OAResponse(
     *         response=404,
     *         description="User not found"
     *     )
     * )
     */
    public function getUser(int $id): JsonResponse
    {
        // 模拟从数据库获取用户数据
        $user = [
            'id' => $id,
            'name' => 'John Doe',
            'email' => '[email protected]',
        ];

        if (!$user) {
            return new JsonResponse(['message' => 'User not found'], 404);
        }

        return new JsonResponse($user);
    }

    /**
     * @Route("/api/users", methods={"POST"})
     * @OAPost(
     *     path="/api/users",
     *     summary="Create a new user",
     *     @OARequestBody(
     *         required=true,
     *         @OAJsonContent(
     *             type="object",
     *             @OAProperty(property="name", type="string", example="Jane Doe"),
     *             @OAProperty(property="email", type="string", example="[email protected]")
     *         )
     *     ),
     *     @OAResponse(
     *         response=201,
     *         description="User created successfully",
     *         @OAJsonContent(
     *             type="object",
     *             @OAProperty(property="id", type="integer", example=2),
     *             @OAProperty(property="name", type="string", example="Jane Doe"),
     *             @OAProperty(property="email", type="string", example="[email protected]")
     *         )
     *     ),
     *     @OAResponse(
     *         response=400,
     *         description="Invalid input"
     *     )
     * )
     */
    public function createUser(): JsonResponse
    {
        // 在实际应用中,你需要从请求体中获取数据并创建用户
        $user = [
            'id' => 2,
            'name' => 'Jane Doe',
            'email' => '[email protected]',
        ];

        return new JsonResponse($user, 201);
    }
}

Annotation 解释:

  • @OAInfo: 定义API的基本信息,例如标题和版本。
  • @OAGet, @OAPost, @OAPut, @OADelete: 定义API的HTTP方法。
  • path: API的路径。
  • summary: API的简短描述。
  • @OAParameter: 定义API的参数。
    • name: 参数名称。
    • in: 参数位置(path, query, header, cookie)。
    • description: 参数描述。
    • required: 是否必须。
    • @OASchema: 参数的数据类型。
  • @OARequestBody: 定义API的请求体。
  • @OAResponse: 定义API的响应。
    • response: HTTP状态码。
    • description: 响应描述。
    • @OAJsonContent: 响应的内容类型为JSON。
    • @OAProperty: 定义JSON对象的属性。
  • @OASecurityScheme: 定义API的安全认证方式

3. 生成OpenAPI文档

使用 zircote/swagger-php 提供的命令行工具或编程方式来扫描代码中的Annotation,并生成OpenAPI文档(通常是YAML或JSON格式)。

命令行方式:

./vendor/bin/openapi -o public/openapi.yaml ./src/Controller

这条命令会扫描 src/Controller 目录下的所有PHP文件,提取其中的Annotation,并将生成的OpenAPI文档保存到 public/openapi.yaml 文件中。

编程方式:

<?php

require_once __DIR__ . '/vendor/autoload.php';

use OpenApiGenerator;

$openapi = Generator::scan([__DIR__ . '/src/Controller']);

header('Content-Type: application/x-yaml');
echo $openapi->toYaml(); // 输出 YAML 格式的 OpenAPI 文档
// 或者
// header('Content-Type: application/json');
// echo $openapi->toJson(); // 输出 JSON 格式的 OpenAPI 文档

这段代码会扫描 src/Controller 目录,生成OpenAPI文档,并将其输出到浏览器。 你可以根据需要选择输出YAML或JSON格式。

4. 使用Swagger UI展示API文档

将生成的OpenAPI文档集成到Swagger UI中,以便开发者可以方便地查看和测试API。

  • 下载Swagger UI: 从Swagger UI的官方网站下载最新版本的Swagger UI。
  • 配置Swagger UI: 将下载的Swagger UI解压到你的Web服务器目录中。 修改 dist/index.html 文件,将 url 参数指向你的OpenAPI文档(例如 public/openapi.yaml)。
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>Swagger UI</title>
    <link rel="stylesheet" type="text/css" href="dist/swagger-ui.css" >
    <link rel="icon" type="image/png" href="dist/favicon-32x32.png" sizes="32x32" />
    <link rel="icon" type="image/png" href="dist/favicon-16x16.png" sizes="16x16" />
    <style>
      html {
        box-sizing: border-box;
        overflow: hidden;
      }

      *,
      *:before,
      *:after {
        box-sizing: inherit;
      }

      body {
        margin:0;
        background: #fafafa;
      }
    </style>
  </head>

  <body>
    <div id="swagger-ui"></div>
    <script src="dist/swagger-ui-bundle.js"> </script>
    <script src="dist/swagger-ui-standalone-preset.js"> </script>
    <script>
    window.onload = function() {
      // Begin Swagger UI call region
      const ui = SwaggerUIBundle({
        url: "openapi.yaml", // 将这里的 url 修改为你的 OpenAPI 文档的路径
        dom_id: '#swagger-ui',
        deepLinking: true,
        presets: [
          SwaggerUIBundle.presets.apis,
          SwaggerUIStandalonePreset
        ],
        plugins: [
          SwaggerUIBundle.plugins.DownloadUrl
        ],
        layout: "StandaloneLayout"
      })
      // End Swagger UI call region

      window.ui = ui
    }
  </script>
  </body>
</html>
  • 访问Swagger UI: 通过浏览器访问Swagger UI的地址(例如 http://localhost/swagger-ui/dist/index.html),即可查看你的API文档。

三、基于YAML文件生成OpenAPI文档

除了Annotation,还可以使用YAML文件来描述API接口。 这种方式更加灵活,可以将API文档与代码分离。

1. 创建YAML文件

创建一个YAML文件(例如 openapi.yaml),按照OpenAPI规范编写API的各种属性。

openapi: 3.0.0
info:
  title: My API
  version: 1.0.0
paths:
  /api/users/{id}:
    get:
      summary: Get a user by ID
      parameters:
        - name: id
          in: path
          description: User ID
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Successful operation
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                    example: 1
                  name:
                    type: string
                    example: John Doe
                  email:
                    type: string
                    example: [email protected]
        '404':
          description: User not found
  /api/users:
    post:
      summary: Create a new user
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                name:
                  type: string
                  example: Jane Doe
                email:
                  type: string
                  example: [email protected]
      responses:
        '201':
          description: User created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                    example: 2
                  name:
                    type: string
                    example: Jane Doe
                  email:
                    type: string
                    example: [email protected]
        '400':
          description: Invalid input

2. 使用Swagger UI展示API文档

与基于Annotation的方式类似,将YAML文件集成到Swagger UI中。 修改 dist/index.html 文件,将 url 参数指向你的YAML文件(例如 openapi.yaml)。

3. 动态生成YAML文件
如果需要动态生成YAML文件,可以使用PHP的YAML库,例如 symfony/yaml

composer require symfony/yaml
<?php

use SymfonyComponentYamlYaml;

$data = [
    'openapi' => '3.0.0',
    'info' => [
        'title' => 'My API',
        'version' => '1.0.0',
    ],
    'paths' => [
        '/api/users/{id}' => [
            'get' => [
                'summary' => 'Get a user by ID',
                'parameters' => [
                    [
                        'name' => 'id',
                        'in' => 'path',
                        'description' => 'User ID',
                        'required' => true,
                        'schema' => [
                            'type' => 'integer',
                        ],
                    ],
                ],
                'responses' => [
                    '200' => [
                        'description' => 'Successful operation',
                        'content' => [
                            'application/json' => [
                                'schema' => [
                                    'type' => 'object',
                                    'properties' => [
                                        'id' => [
                                            'type' => 'integer',
                                            'example' => 1,
                                        ],
                                        'name' => [
                                            'type' => 'string',
                                            'example' => 'John Doe',
                                        ],
                                        'email' => [
                                            'type' => 'string',
                                            'example' => '[email protected]',
                                        ],
                                    ],
                                ],
                            ],
                        ],
                    ],
                    '404' => [
                        'description' => 'User not found',
                    ],
                ],
            ],
        ],
    ],
];

$yaml = Yaml::dump($data, 4, 2);

header('Content-Type: application/x-yaml');
echo $yaml;

这段代码会生成一个YAML格式的OpenAPI文档,并将其输出到浏览器。你可以根据需要修改 $data 数组来动态生成API文档。

四、Annotation vs YAML:优缺点分析

特性 Annotation YAML
代码耦合度
可读性 较低,代码中混杂了文档信息 较高,文档与代码分离
维护性 相对困难,修改API定义需要修改代码 相对容易,修改API定义只需修改YAML文件
适用场景 适用于小型项目,或者API定义相对稳定的项目 适用于大型项目,或者API定义频繁变动的项目
学习成本 较低,只需要学习Annotation的语法 较高,需要学习OpenAPI规范和YAML语法

五、最佳实践

  • 保持API定义的清晰和简洁: 避免在Annotation或YAML文件中编写过于复杂的API定义。
  • 使用版本控制: 将API文档纳入版本控制,以便跟踪API的变化。
  • 自动化构建流程: 将OpenAPI文档的生成过程集成到自动化构建流程中,确保API文档与代码同步更新。
  • 使用代码生成工具: 利用Swagger Codegen等工具,根据OpenAPI文档自动生成服务器端和客户端代码,提高开发效率。
  • 进行API文档审查: 定期对API文档进行审查,确保其准确性和完整性。
  • 使用描述性名称: 为API操作和参数使用清晰和描述性的名称。这有助于提高API文档的可读性和可用性。
  • 提供示例值: 在API文档中提供示例值,可以帮助开发者更快地理解如何使用API。
  • 使用枚举类型: 对于具有固定值的参数,可以使用枚举类型来限制取值范围,并提高API文档的清晰度。
  • 定义错误代码: 定义清晰的错误代码和错误消息,可以帮助开发者更好地处理API调用中的错误。

六、高级技巧

  • 使用@OASchemaref属性引用可重用的组件: 避免在多个地方重复定义相同的Schema,提高API文档的复用性和可维护性。
  • 使用@OASecurityScheme定义安全认证方式: 支持多种安全认证方式,例如API Key、OAuth2、HTTP Basic Auth等。
  • 使用@OATag对API操作进行分组: 将相关的API操作分组到一起,提高API文档的可读性。
  • 自定义Swagger UI: 通过修改Swagger UI的配置文件,可以自定义Swagger UI的外观和行为,例如修改主题、添加插件等。

七、安全注意事项

在生成和展示API文档时,需要注意以下安全事项:

  • 避免泄露敏感信息: 不要在API文档中包含敏感信息,例如API密钥、数据库密码等。
  • 限制访问权限: 对API文档的访问进行权限控制,只允许授权用户访问。
  • 防止跨站脚本攻击(XSS): 对API文档中的用户输入进行验证和过滤,防止XSS攻击。
  • 定期更新Swagger UI: 及时更新Swagger UI到最新版本,修复安全漏洞。

总结的话

本次讲座我们深入探讨了如何使用Annotation和YAML文件自动生成OpenAPI文档。Annotation方式代码耦合度高,YAML方式代码耦合度低,选择哪种方式取决于项目规模和API变动频率。无论选择哪种方式,都需要遵循最佳实践,并注意安全事项,以确保API文档的质量和安全性。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注