Backend
rust-web
Rust Web 开发技能 — axum 框架(路由、提取器、状态、响应体、中间件)、serde 序列化/反序列化、HTTP 客户端(reqwest)、数据库集成(sqlx:POSTGRESQL/MySQL/SQLite、查询、连接池、迁移)、WebSocket、CORS、认证(JWT)、RESTful API 设计模式。基于社区 crate 文档(axum、tokio、serde、sqlx、reqwest)。当用户需要构建 Web 服务或 HTTP 应用时激活。
Rust Web 开发
基于社区主流 crate:axum、tokio、serde、sqlx、reqwest、tower-http 文档。
Capability Boundaries
✅ 强项
- axum 路由(Router::new、route、nest、layer、fallback)
- 提取器(Path、Query、Json、Form、State、Extension、String、Bytes)
- 响应体实现(Json、Html、StatusCode、Redirect、自定义 IntoResponse)
- 共享状态(axum::extract::State、FromRef 模式)
- 中间件(tower 中间件栈、tower-http 工具集)
- serde 序列化(#[derive(Serialize, Deserialize)]、#[serde(rename/skip/tag)])
- HTTP 客户端(reqwest:GET/POST、JSON、headers、文件上传)
- 数据库(sqlx:编译时 query!、query_as!、连接池、迁移)
- WebSocket(axum::extract::ws、发送/接收消息)
- CORS(tower-http CorsLayer)
- JWT 认证(jsonwebtoken crate)
- 错误处理(应用错误 → HTTP 响应、anyhow 集成)
⚠️ 前置要求
- 理解 async/await(rust-concurrency)
- Rust 基础(rust-1.93)
❌ 不适用范围
- CLI 应用 → 使用
rust-cli技能 - 纯异步编程 → 使用
rust-concurrency技能
何时使用
- "用 axum 创建 REST API"
- "连接数据库"
- "JSON 序列化"
- "WebSocket 处理"
- "HTTP 客户端请求"
Data Privacy
本技能不收集、存储或传输任何用户数据。
一、axum Web 框架
[dependencies]
axum = "0.8"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tower-http = { version = "0.6", features = ["cors", "trace", "compression"] }
use axum::{
Router,
extract::{Path, Query, State},
http::StatusCode,
response::Json,
routing::get,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(Serialize, Deserialize, Clone)]
struct User {
id: u64,
name: String,
}
type AppState = Arc<SharedState>;
struct SharedState {
db: Mutex<Vec<User>>,
}
async fn list_users(
State(state): State<AppState>,
) -> Json<Vec<User>> {
let users = state.db.lock().unwrap();
Json(users.clone())
}
async fn get_user(
Path(id): Path<u64>,
State(state): State<AppState>,
) -> Result<Json<User>, StatusCode> {
let users = state.db.lock().unwrap();
users.iter()
.find(|u| u.id == id)
.map(|u| Json(u.clone()))
.ok_or(StatusCode::NOT_FOUND)
}
#[tokio::main]
async fn main() {
let state = Arc::new(SharedState {
db: Mutex::new(vec![]),
});
let app = Router::new()
.route("/users", get(list_users).post(create_user))
.route("/users/:id", get(get_user))
.with_state(state)
.layer(tower_http::cors::CorsLayer::permissive());
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
二、数据库(sqlx)
[dependencies]
sqlx = { version = "0.8", features = ["runtime-tokio", "postgres", "mysql", "sqlite", "migrate"] }
use sqlx::PgPool;
#[derive(sqlx::FromRow, serde::Serialize, serde::Deserialize)]
struct User {
id: i64,
name: String,
email: String,
}
// 连接池
let pool = PgPool::connect("postgres://user:pass@localhost/db").await?;
// 编译时检查查询
let user = sqlx::query_as::<_, User>(
"SELECT id, name, email FROM users WHERE id = $1"
)
.bind(user_id)
.fetch_one(&pool)
.await?;
// 迁移
sqlx::migrate!("./migrations").run(&pool).await?;
三、HTTP 客户端(reqwest)
let client = reqwest::Client::new();
// GET
let resp = client.get("https://api.github.com/users")
.header("User-Agent", "my-app")
.send()
.await?;
let body = resp.text().await?;
let json: serde_json::Value = resp.json().await?;
// POST JSON
let user = serde_json::json!({"name": "Alice"});
let resp = client.post("https://api.example.com/users")
.json(&user)
.send()
.await?;
四、中间件模式
use tower::ServiceBuilder;
use tower_http::{
cors::CorsLayer,
trace::TraceLayer,
compression::CompressionLayer,
timeout::TimeoutLayer,
};
let middleware = ServiceBuilder::new()
.layer(TraceLayer::new_for_http())
.layer(CompressionLayer::new())
.layer(CorsLayer::permissive())
.layer(TimeoutLayer::new(Duration::from_secs(30)));
let app = Router::new()
.route("/api/:path", get(handler))
.layer(middleware);
五、WebSocket
use axum::extract::ws::{WebSocket, WebSocketUpgrade, Message};
async fn ws_handler(
ws: WebSocketUpgrade,
) -> impl axum::response::IntoResponse {
ws.on_upgrade(handle_socket)
}
async fn handle_socket(mut socket: WebSocket) {
while let Some(msg) = socket.recv().await {
let msg = msg.unwrap();
match msg {
Message::Text(text) => {
socket.send(Message::Text(text)).await.unwrap();
}
Message::Close(_) => break,
_ => {}
}
}
}
常用 Crate 速查
| 用途 | crate | 说明 |
|---|---|---|
| Web 框架 | axum | 基于 tokio/tower,推荐 |
| 运行时 | tokio | async 运行时 |
| 序列化 | serde / serde_json | JSON 序列化 |
| HTTP 客户端 | reqwest | async HTTP client |
| 数据库 | sqlx | 编译时检查 SQL |
| ORM | sea-orm | 异步 ORM |
| WebSocket | axum::extract::ws | 内置 |
| JWT | jsonwebtoken | JWT 编解码 |
| 模板 | askama | 编译时模板 |
Workflow
Step 1. 选择框架 — 确定使用 axum(推荐)或其他框架 Step 2. 设计路由 — 规划 RESTful API 路由和 handler 签名 Step 3. 连接数据库 — 配置 sqlx 连接池,编写编译时检查的查询 Step 4. 添加中间件 — 配置 CORS、跟踪、压缩、超时等中间件 Step 5. 错误处理 — 统一错误类型,将应用错误转为 HTTP 响应 Step 6. 测试 — 编写集成测试,使用 axum::test 或 reqwest 测试端点
Gotchas
- axum extract::Path 顺序依赖路由参数名 - /:id 必须匹配 Path<u32> 类型
- tokio::spawn 的 Future 必须 Send - AppState 含 Rc/RefCell 会编译失败
- sqlx::query! 需要运行时数据库验证 - CI 中需启动数据库或改用 query_as
- serde #[serde(flatten)] 性能开销 - 创建临时 Value 中间表示
- axum WebSocket::recv() 返回 None 表示连接已关闭