关于 utoipa-axum
utoipa-axum 是用于为 Axum 构建的 Rust REST API 编写文档的代码优先技能,介绍如何用 utoipa 派生 OpenAPI 3.1 模式与路径,并通过 Scalar 提供交互式文档。
主要功能
- 01使用 ToSchema 派生模式
- 02为处理器标注路径元数据
- 03组合路径与模式生成 OpenAPI
- 04描述查询参数和枚举
- 05通过 Scalar 提供交互式文档
适用场景
- 01记录 Axum 请求与响应类型
- 02根据处理器生成 OpenAPI 规范
- 03提供交互式 API 文档
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ suryavirkapur/ skills --skill utoipa-axum安装后在 Claude Code 中输入/utoipa-axum确认已加载。
附带文件references/api_reference.mdreferences/patterns.md
详细介绍
使用 utoipa + Axum 编写 OpenAPI
utoipa 为 Rust REST API 提供编译时 OpenAPI 文档。采用代码优先的方式:为处理器和类型添加注解,即可自动生成 OpenAPI 3.1 规范。
核心概念
- ToSchema — 为请求/响应体类型派生
- #[utoipa::path] — 为处理器添加路径、方法、参数和响应注解
- OpenApi — 将所有路径和架构合并为规范
- Scalar — 提供交互式 API 文档
Cargo.toml 配置
[dependencies]
axum = "0.8"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
uuid = { version = "1", features = ["serde", "v4"] }
chrono = { version = "0.4", features = ["serde"] }
# utoipa with axum integration
utoipa = { version = "5", features = ["axum_extras", "uuid", "chrono"] }
utoipa-scalar = { version = "0.3", features = ["axum"] }
ToSchema — 请求/响应类型
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use uuid::Uuid;
#[derive(Debug, Deserialize, ToSchema)]
pub struct CreateUserRequest {
/// User's email address
pub email: String,
/// Password (min 8 characters)
#[schema(min_length = 8)]
pub password: String,
}
#[derive(Debug, Serialize, ToSchema)]
pub struct UserResponse {
pub id: Uuid,
pub email: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}
#[derive(Debug, Serialize, ToSchema)]
pub struct ErrorResponse {
pub error: String,
}
#[utoipa::path] — 为处理器编写文档
use axum::{extract::{Path, State}, http::StatusCode, Json};
#[utoipa::path(
get,
path = "/api/users/{id}",
tag = "users",
params(("id" = Uuid, Path, description = "User ID")),
responses(
(status = 200, description = "User found", body = UserResponse),
(status = 404, description = "Not found", body = ErrorResponse)
),
security(("bearer" = []))
)]
pub async fn get_user(
State(state): State<AppState>,
Path(id): Path<Uuid>,
) -> Result<Json<UserResponse>, (StatusCode, Json<ErrorResponse>)> {
// handler logic
}
#[utoipa::path(
post,
path = "/api/users",
tag = "users",
request_body = CreateUserRequest,
responses(
(status = 201, description = "Created", body = UserResponse),
(status = 400, description = "Bad request", body = ErrorResponse)
)
)]
pub async fn create_user(
State(state): State<AppState>,
Json(req): Json<CreateUserRequest>,
) -> Result<(StatusCode, Json<UserResponse>), (StatusCode, Json<ErrorResponse>)> {
// handler logic
}
OpenApi — 合并所有内容
use utoipa::openapi::security::{Http, HttpAuthScheme, SecurityScheme};
use utoipa::{Modify, OpenApi};
#[derive(OpenApi)]
#[openapi(
info(
title = "My API",
version = "1.0.0",
description = "API description"
),
tags(
(name = "users", description = "User management"),
(name = "health", description = "Health checks")
),
paths(
get_user,
create_user,
health_check
),
components(
schemas(
CreateUserRequest,
UserResponse,
ErrorResponse
)
),
modifiers(&SecurityAddon)
)]
pub struct ApiDoc;
struct SecurityAddon;
impl Modify for SecurityAddon {
fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
if let Some(components) = openapi.components.as_mut() {
components.add_security_scheme(
"bearer",
SecurityScheme::Http(Http::new(HttpAuthScheme::Bearer)),
);
}
}
}
提供 Scalar UI
use axum::{routing::get, Router};
use utoipa::OpenApi;
use utoipa_scalar::{Scalar, Servable};
pub fn create_router(state: AppState) -> Router {
Router::new()
.route("/health", get(health_check))
.route("/api/users", get(list_users).post(create_user))
.route("/api/users/{id}", get(get_user).put(update_user).delete(delete_user))
// Scalar UI at /api/docs
.merge(Scalar::with_url("/api/docs", ApiDoc::openapi()))
.with_state(state)
}
IntoParams — 查询参数
use utoipa::IntoParams;
#[derive(Debug, Deserialize, IntoParams)]
pub struct PaginationParams {
/// Page number (1-indexed)
#[param(minimum = 1, default = 1)]
pub page: Option<u32>,
/// Items per page
#[param(minimum = 1, maximum = 100, default = 20)]
pub per_page: Option<u32>,
}
#[derive(Debug, Deserialize, IntoParams)]
pub struct UserFilters {
/// Filter by email contains
pub email: Option<String>,
/// Filter by status
pub status: Option<UserStatus>,
}
#[utoipa::path(
get,
path = "/api/users",
tag = "users",
params(PaginationParams, UserFilters),
responses((status = 200, body = Vec<UserResponse>))
)]
pub async fn list_users(
Query(pagination): Query<PaginationParams>,
Query(filters): Query<UserFilters>,
) -> Json<Vec<UserResponse>> {
// handler logic
}
架构中的枚举
#[derive(Debug, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum UserStatus {
Active,
Inactive,
Pending,
}
#[derive(Debug, Serialize, Deserialize, ToSchema)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Event {
UserCreated { user_id: Uuid },
UserDeleted { user_id: Uuid, reason: String },
}
常见模式
错误处理、身份验证中间件和响应类型请参阅 references/patterns.md。
API 参考
完整的 ToSchema 和路径属性选项请参阅 references/api_reference.md。
评论
支持 Markdown