Skip to content

Commit a780768

Browse files
committed
docs(rust): 添加 Rust 后端 API 文档生成方案指南
添加完整的 utoipa 使用指南,涵盖: - Rust 生态中 API 文档方案对比 - utoipa 在 Axum 和 Actix-web 中的配置 - 数据模型定义和接口标注方法 - Swagger UI 集成和路由挂载 - 高级用法如安全认证和枚举类型处理
1 parent bd95701 commit a780768

1 file changed

Lines changed: 337 additions & 0 deletions

File tree

  • docs/cheatsheet/rust/ecosystem/apidoc
Lines changed: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
1+
# Rust 后端开发提供接口文档的方案
2+
3+
Rust 生态中,提供 API 接口文档主要有以下几种方案。我来为你详细介绍最主流的方式。
4+
5+
---
6+
7+
## 方案对比
8+
9+
| 方案 | 适配框架 | 特点 |
10+
|------|---------|------|
11+
| **utoipa** | Actix-web, Axum, Rocket 等 | ⭐ 最流行,通过宏自动生成 OpenAPI 文档 |
12+
| **aide** | Axum | 专为 Axum 设计,与路由深度集成 |
13+
| **paperclip** | Actix-web | Actix 专用,较成熟 |
14+
| 手写 OpenAPI YAML/JSON | 任意 | 灵活但维护成本高 |
15+
16+
让我为你查找 **utoipa**(最主流方案)的最新用法:
17+
18+
非常好,下面是 Rust 后端开发中提供接口文档的 **完整指南**
19+
20+
---
21+
22+
## 🏆 推荐方案:utoipa(最主流)
23+
24+
**utoipa** 是 Rust 生态中最流行的 OpenAPI 文档生成库,通过 **过程宏** 自动从代码生成 OpenAPI 3.x 文档,并搭配 Swagger UI 在线查看。
25+
26+
---
27+
28+
### 1. 添加依赖(`Cargo.toml`
29+
30+
**Axum** 框架为例:
31+
32+
```toml
33+
[dependencies]
34+
axum = "0.8"
35+
tokio = { version = "1", features = ["full"] }
36+
serde = { version = "1", features = ["derive"] }
37+
serde_json = "1"
38+
39+
# utoipa 核心 + Axum 集成 + Swagger UI
40+
utoipa = { version = "5", features = ["axum_extras"] }
41+
utoipa-axum = "0.2"
42+
utoipa-swagger-ui = { version = "9", features = ["axum"] }
43+
```
44+
45+
如果用 **Actix-web**,换成:
46+
47+
```toml
48+
utoipa-swagger-ui = { version = "9", features = ["actix-web"] }
49+
```
50+
51+
---
52+
53+
### 2. 定义数据模型(Schema)
54+
55+
使用 `#[derive(ToSchema)]` 宏让结构体自动生成 OpenAPI Schema:
56+
57+
```rust
58+
use serde::{Deserialize, Serialize};
59+
use utoipa::ToSchema;
60+
61+
/// 宠物信息
62+
#[derive(Serialize, Deserialize, ToSchema)]
63+
struct Pet {
64+
/// 宠物ID
65+
id: u64,
66+
/// 宠物名称
67+
name: String,
68+
/// 宠物年龄
69+
age: Option<i32>,
70+
}
71+
72+
/// 创建宠物请求
73+
#[derive(Deserialize, ToSchema)]
74+
struct CreatePetRequest {
75+
/// 宠物名称
76+
name: String,
77+
/// 宠物年龄
78+
age: Option<i32>,
79+
}
80+
81+
/// 通用响应
82+
#[derive(Serialize, ToSchema)]
83+
struct ApiResponse<T: ToSchema> {
84+
code: i32,
85+
message: String,
86+
data: Option<T>,
87+
}
88+
```
89+
90+
> 💡 **关键**:Rust 的 `///` 文档注释会自动成为 OpenAPI 的字段描述!
91+
92+
---
93+
94+
### 3. 定义 API 接口(Path)
95+
96+
使用 `#[utoipa::path]` 宏标注每个接口的元信息:
97+
98+
```rust
99+
use axum::{extract::Path, Json};
100+
use utoipa::OpenApi;
101+
102+
/// 根据 ID 获取宠物
103+
///
104+
/// 从数据库中根据宠物 ID 查询宠物信息
105+
#[utoipa::path(
106+
get,
107+
path = "/pets/{id}",
108+
tag = "宠物管理",
109+
params(
110+
("id" = u64, Path, description = "宠物 ID"),
111+
),
112+
responses(
113+
(status = 200, description = "查询成功", body = Pet),
114+
(status = 404, description = "宠物不存在")
115+
)
116+
)]
117+
async fn get_pet_by_id(Path(id): Path<u64>) -> Json<Pet> {
118+
Json(Pet {
119+
id,
120+
name: "小白".to_string(),
121+
age: Some(3),
122+
})
123+
}
124+
125+
/// 创建宠物
126+
///
127+
/// 创建一个新的宠物记录
128+
#[utoipa::path(
129+
post,
130+
path = "/pets",
131+
tag = "宠物管理",
132+
request_body(
133+
content = CreatePetRequest,
134+
description = "宠物信息",
135+
content_type = "application/json"
136+
),
137+
responses(
138+
(status = 201, description = "创建成功", body = Pet,
139+
example = json!({"id": 1, "name": "小白", "age": 3})
140+
),
141+
(status = 400, description = "参数错误")
142+
),
143+
// 可选:接口安全认证
144+
security(
145+
("bearer_token" = [])
146+
)
147+
)]
148+
async fn create_pet(Json(req): Json<CreatePetRequest>) -> Json<Pet> {
149+
Json(Pet {
150+
id: 1,
151+
name: req.name,
152+
age: req.age,
153+
})
154+
}
155+
156+
/// 获取所有宠物
157+
#[utoipa::path(
158+
get,
159+
path = "/pets",
160+
tag = "宠物管理",
161+
params(
162+
("page" = Option<u32>, Query, description = "页码"),
163+
("size" = Option<u32>, Query, description = "每页数量"),
164+
),
165+
responses(
166+
(status = 200, description = "查询成功", body = Vec<Pet>)
167+
)
168+
)]
169+
async fn list_pets() -> Json<Vec<Pet>> {
170+
Json(vec![])
171+
}
172+
```
173+
174+
---
175+
176+
### 4. 组装路由并挂载 Swagger UI
177+
178+
#### 方式一:使用 `OpenApiRouter`(推荐,自动收集路由)
179+
180+
```rust
181+
use utoipa::OpenApi;
182+
use utoipa_axum::{router::OpenApiRouter, routes};
183+
use utoipa_swagger_ui::SwaggerUi;
184+
185+
#[derive(OpenApi)]
186+
#[openapi(
187+
info(
188+
title = "宠物商店 API",
189+
version = "1.0.0",
190+
description = "这是一个示例宠物商店接口文档"
191+
),
192+
// 可选:定义安全方案
193+
// security(
194+
// ("bearer_token" = [])
195+
// ),
196+
// components(
197+
// security_schemes(
198+
// ("bearer_token" = SecurityScheme::Http(
199+
// HttpBuilder::new().scheme(HttpAuthScheme::Bearer).bearer_format("JWT").build()
200+
// ))
201+
// )
202+
// )
203+
)]
204+
struct ApiDoc;
205+
206+
#[tokio::main]
207+
async fn main() {
208+
// ✅ OpenApiRouter 自动收集路由 + 生成文档
209+
let (router, api) = OpenApiRouter::with_openapi(ApiDoc::openapi())
210+
.routes(routes!(get_pet_by_id))
211+
.routes(routes!(create_pet))
212+
.routes(routes!(list_pets))
213+
.split_for_parts();
214+
215+
// 挂载 Swagger UI
216+
let app = router.merge(
217+
SwaggerUi::new("/swagger-ui")
218+
.url("/api-docs/openapi.json", api)
219+
);
220+
221+
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
222+
println!("📖 Swagger UI: http://localhost:3000/swagger-ui/");
223+
axum::serve(listener, app).await.unwrap();
224+
}
225+
```
226+
227+
#### 方式二:手动注册(传统方式)
228+
229+
```rust
230+
#[derive(OpenApi)]
231+
#[openapi(
232+
paths(get_pet_by_id, create_pet, list_pets), // 手动列出所有接口
233+
components(schemas(Pet, CreatePetRequest)), // 手动列出所有 Schema
234+
info(title = "宠物商店 API", version = "1.0.0")
235+
)]
236+
struct ApiDoc;
237+
```
238+
239+
---
240+
241+
### 5. 效果
242+
243+
启动服务后访问:
244+
245+
| 地址 | 说明 |
246+
|------|------|
247+
| `http://localhost:3000/swagger-ui/` | 📖 **Swagger UI** 交互式文档界面 |
248+
| `http://localhost:3000/api-docs/openapi.json` | 📄 OpenAPI JSON 规范文件 |
249+
250+
Swagger UI 界面可以直接 **在线调试接口**,非常方便前后端联调。
251+
252+
---
253+
254+
### 6. 高级用法速查
255+
256+
#### 🔐 安全认证(JWT Bearer)
257+
258+
```rust
259+
use utoipa::openapi::security::{HttpAuthScheme, HttpBuilder, SecurityScheme};
260+
261+
#[derive(OpenApi)]
262+
#[openapi(
263+
components(schemas(Pet)),
264+
modifiers(&SecurityAddon)
265+
)]
266+
struct ApiDoc;
267+
268+
struct SecurityAddon;
269+
270+
impl utoipa::Modify for SecurityAddon {
271+
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
272+
let components = openapi.components.as_mut().unwrap();
273+
components.add_security_scheme(
274+
"bearer_token",
275+
SecurityScheme::Http(
276+
HttpBuilder::new()
277+
.scheme(HttpAuthScheme::Bearer)
278+
.bearer_format("JWT")
279+
.build()
280+
),
281+
);
282+
}
283+
}
284+
```
285+
286+
#### 📦 枚举类型
287+
288+
```rust
289+
#[derive(Serialize, ToSchema)]
290+
enum PetStatus {
291+
Available,
292+
Pending,
293+
Sold,
294+
}
295+
```
296+
297+
#### 🏷️ 分组 Tag
298+
299+
```rust
300+
#[utoipa::path(
301+
get,
302+
path = "/users",
303+
tag = "用户管理", // 接口分组
304+
// ...
305+
)]
306+
```
307+
308+
---
309+
310+
### 7. 完整项目结构参考
311+
312+
```
313+
my-api/
314+
├── Cargo.toml
315+
└── src/
316+
├── main.rs # 启动入口 + Swagger 挂载
317+
├── routes/
318+
│ ├── mod.rs
319+
│ └── pets.rs # 宠物相关接口 (#[utoipa::path])
320+
├── models/
321+
│ ├── mod.rs
322+
│ └── pet.rs # 数据模型 (#[derive(ToSchema)])
323+
└── doc.rs # OpenApi 定义 + SecurityAddon
324+
```
325+
326+
---
327+
328+
## 总结
329+
330+
| 步骤 | 做什么 |
331+
|------|--------|
332+
|| 给结构体加 `#[derive(ToSchema)]` |
333+
|| 给接口函数加 `#[utoipa::path(...)]` |
334+
||`#[derive(OpenApi)]` 汇总注册 |
335+
|| 挂载 `SwaggerUi` 到路由 |
336+
337+
这套方案的核心优势是 **文档即代码**——接口文档直接从代码生成,避免了手写文档和代码不同步的问题。修改了接口代码,文档自动更新。

0 commit comments

Comments
 (0)