API 文档生成
Viswoole 路由系统内置了 API 文档自动生成能力。启用后,框架会解析路由注解中的元信息(标题、描述、参数、返回值),自动构建结构化的接口文档。
启用配置
在 config/router.php 中开启 API 文档功能:
php
return [
// ... 其他配置
'api_doc' => [
'enable' => true, // 启用 API 文档生成
'header' => [], // 全局 Header 参数(数组类型)
'query' => [], // 全局 Query 参数(数组类型)
'body' => [], // 全局 Body 参数(数组类型)
'returned' => [], // 全局返回值结构(数组类型)
],
];配置项说明
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
enable | bool | false | 是否启用 API 文档生成 |
header | array | [] | 全局 Header 参数定义(数组类型) |
query | array | [] | 全局 Query 参数定义(数组类型) |
body | array | [] | 全局 Body 参数定义(数组类型) |
returned | array | [] | 全局返回值结构定义(数组类型) |
参数注解
通过自动注入注解声明接口的入参信息,框架会自动解析并纳入文档。这些注解均无构造函数参数,参数名取自 PHP 参数名(不区分大小写)。
自动注入注解
框架提供了便捷的专用注解,用于从不同数据源注入参数:
php
use Viswoole\HttpServer\AutoInject\{InjectGet, InjectPost, InjectHeader, InjectFile};
#[RouteMapping(paths: ['/upload'], method: ['POST'], title: '上传文件')]
public function upload(
#[InjectHeader] string $authorization, // 从 Header 注入
#[InjectGet] string $category, // 从 Query 注入
#[InjectPost] UploadedFile $file, // 从 Body 注入
#[InjectFile] UploadedFile $document, // 从上传文件注入
): array {}注意:这些注解无构造函数参数,参数名必须与 header/query 字段名一致(不区分大小写)。例如
#[InjectHeader] string $authorization会从Authorization请求头中取值。
Returned 注解
Returned 注解用于声明接口返回值的结构:
php
use Viswoole\Router\ApiDoc\Annotation\Returned;
#[RouteMapping(paths: ['/user/list'], method: ['GET'], title: '用户列表')]
#[Returned(
title: '成功',
data: [
'code' => 200,
'message' => '成功',
'data' => [],
],
)]
public function list(): array {}Returned 属性说明
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
title | string | (必填) | 返回值标题 |
data | array | string | (必填) | 示例响应数据,自动推导结构 |
statusCode | int | 200 | HTTP 状态码 |
type | string | application/json | 响应内容类型 |
sort | int | 0 | 排序,数值越大越靠前 |
提示:
data支持传入数组或字符串。传入数组时,框架会自动推导字段类型和嵌套结构;键名支持name|描述和name?|描述语法标记可选字段。
元信息提取
除显式声明的参数注解外,框架还会从 #[RouteMapping] 中自动提取以下元信息:
php
#[RouteMapping(
paths: ['/order/create'],
method: ['POST'],
title: '创建订单', // → 文档标题
description: '提交新订单', // → 文档描述
)]
public function create(): array {}自动提取的字段:
| 来源 | 字段 | 说明 |
|---|---|---|
#[RouteMapping]->title | 标题 | 接口显示名称 |
#[RouteMapping]->description | 描述 | 接口详细说明 |
#[RouteMapping]->method | 请求方法 | GET / POST 等 |
#[RouteMapping]->paths | 请求路径 | 路径列表 |
| 控制器命名空间 | tags | 分类标签(自动从命名空间推断) |
完整示例
php
<?php
namespace App\Controller\Api\V1;
use Viswoole\HttpServer\AutoInject\{InjectGet, InjectPost, InjectHeader};
use Viswoole\Router\Annotation\{
Controller,
RouteMapping,
};
use Viswoole\Router\ApiDoc\Annotation\Returned;
#[Controller(prefix: '/api/v1')]
class UserController
{
#[RouteMapping(
paths: ['/user/login'],
method: ['POST'],
title: '用户登录',
description: '通过手机号和验证码登录',
)]
#[Returned(
title: '登录成功,返回 Token',
data: [
'code' => 200,
'data|访问令牌' => [
'token' => 'string',
'expires_in|有效期(秒)' => 3600,
],
],
)]
public function login(
#[InjectPost] string $phone,
#[InjectPost] string $code,
): array
{
return AuthService::login($phone, $code);
}
#[RouteMapping(
paths: ['/user/profile'],
method: ['GET'],
title: '个人信息',
description: '获取当前登录用户的详细信息',
)]
#[Returned(
title: '成功返回用户信息',
data: [
'code' => 200,
'data' => [
'id' => 1,
'name' => '张三',
'avatar?' => null,
],
],
)]
public function profile(
#[InjectHeader] string $authorization,
): array
{
return AuthService::user($authorization);
}
}生成的文档结构大致如下:
json
{
"paths": {
"/api/v1/user/login": {
"post": {
"summary": "用户登录",
"description": "通过手机号和验证码登录",
"tags": ["API", "V1", "User"],
"parameters": [
{
"name": "phone",
"in": "body",
"required": true,
"type": "string",
"description": "手机号"
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"properties": {
"code": { "type": "string", "description": "短信验证码" }
},
"required": ["code"]
}
}
}
},
"responses": {
"200": {
"description": "登录成功,返回 Token",
"schema": {
/* ... */
}
}
}
}
}
}
}最佳实践
- 保持注解与签名同步:自动注入注解的参数名应与 header/query 字段语义一致
- 必填参数明确标注:通过参数类型(非可空)来声明必填字段
- Returned 描述关键字段:使用
name|描述语法重点标注嵌套结构和可空字段 - title 简洁准确:作为文档目录中的显示名称,应一目了然
