Rust Web框架Axum实战:构建高性能HTTP服务

引言
Axum是Rust生态中最流行的Web框架之一,以其高性能、类型安全和出色的异步支持而闻名。作为一名从Python转向Rust的后端开发者,我在实践中总结了Axum框架的最佳实践。本文将深入探讨Axum的核心概念和实战技巧,帮助你构建高性能的HTTP服务。
一、Axum核心概念
1.1 什么是Axum
Axum是一个基于Tokio运行时和Hyper HTTP库构建的Web框架,专注于:
- 高性能:利用Rust的异步能力和零成本抽象
- 类型安全:编译时检查确保API正确性
- 模块化:灵活的中间件系统和路由设计
1.2 Axum架构
请求
|
v
Router(路由)
|
v
Layer(中间件层)
|
v
Service(服务处理)
|
v
Handler(处理器)
1.3 核心组件
| 组件 | 作用 |
|---|---|
| Router | 路由定义和请求分发 |
| Handler | 请求处理函数 |
| Layer | 中间件层 |
| Service | 服务抽象 |
| Extractors | 请求数据提取 |
二、基础入门
2.1 项目初始化
cargo new axum-demo
cd axum-demo
Cargo.toml:
[package]
name = "axum-demo"
version = "0.1.0"
edition = "2021"
[dependencies]
axum = "0.7"
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
2.2 第一个Axum应用
use axum::{
routing::{get, post},
Router, Server,
};
async fn hello_world() -> &'static str {
"Hello, World!"
}
async fn greet(name: String) -> String {
format!("Hello, {}!", name)
}
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/", get(hello_world))
.route("/greet/:name", get(greet));
Server::bind(&"127.0.0.1:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
三、路由与提取器
3.1 路径参数提取
use axum::{
extract::Path,
routing::get,
Router,
};
async fn get_user(Path(user_id): Path<u64>) -> String {
format!("User ID: {}", user_id)
}
async fn get_user_post(Path((user_id, post_id)): Path<(u64, u64)>) -> String {
format!("User {} - Post {}", user_id, post_id)
}
let app = Router::new()
.route("/users/:user_id", get(get_user))
.route("/users/:user_id/posts/:post_id", get(get_user_post));
3.2 查询参数提取
use axum::{
extract::Query,
routing::get,
Router,
};
use serde::Deserialize;
#[derive(Deserialize)]
struct ListUsersParams {
page: Option<u32>,
page_size: Option<u32>,
}
async fn list_users(Query(params): Query<ListUsersParams>) -> String {
let page = params.page.unwrap_or(1);
let page_size = params.page_size.unwrap_or(10);
format!("Listing users: page={}, page_size={}", page, page_size)
}
let app = Router::new()
.route("/users", get(list_users));
3.3 请求体提取
use axum::{
extract::Json,
routing::post,
Router,
};
use serde::Deserialize;
#[derive(Deserialize)]
struct CreateUser {
username: String,
email: String,
}
async fn create_user(Json(payload): Json<CreateUser>) -> String {
format!("Created user: {} ({})", payload.username, payload.email)
}
let app = Router::new()
.route("/users", post(create_user));
3.4 自定义提取器
use axum::{
async_trait,
extract::{FromRequest, RequestParts},
routing::get,
Router,
};
use http::StatusCode;
struct ApiKey(String);
#[async_trait]
impl<B> FromRequest<B> for ApiKey
where
B: Send,
{
type Rejection = (StatusCode, &'static str);
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
let api_key = req
.headers()
.get("X-API-Key")
.and_then(|h| h.to_str().ok());
match api_key {
Some(key) => Ok(ApiKey(key.to_string())),
None => Err((StatusCode::UNAUTHORIZED, "Missing API key")),
}
}
}
async fn protected_route(api_key: ApiKey) -> String {
format!("Access granted with key: {}", api_key.0)
}
let app = Router::new()
.route("/protected", get(protected_route));
四、中间件系统
4.1 基础中间件
use axum::{
middleware::{self, Next},
response::Response,
routing::get,
Router,
};
use http::{Request, StatusCode};
async fn logging_middleware<B>(
request: Request<B>,
next: Next<B>,
) -> Result<Response, StatusCode> {
println!("Request: {} {}", request.method(), request.uri());
let response = next.run(request).await;
println!("Response status: {}", response.status());
Ok(response)
}
let app = Router::new()
.route("/", get(|| async { "Hello, World!" }))
.layer(middleware::from_fn(logging_middleware));
4.2 错误处理中间件
use axum::{
http::StatusCode,
middleware::{self, Next},
response::{IntoResponse, Response},
routing::get,
Router,
};
use std::fmt;
#[derive(Debug)]
enum AppError {
NotFound,
InternalError,
}
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AppError::NotFound => write!(f, "Not found"),
AppError::InternalError => write!(f, "Internal error"),
}
}
}
impl IntoResponse for AppError {
fn into_response(self) -> Response {
let (status, message) = match self {
AppError::NotFound => (StatusCode::NOT_FOUND, "Not Found"),
AppError::InternalError => (StatusCode::INTERNAL_SERVER_ERROR, "Internal Server Error"),
};
(status, message).into_response()
}
}
async fn error_handler<B>(
request: Request<B>,
next: Next<B>,
) -> Result<Response, AppError> {
let response = next.run(request).await;
if response.status() == StatusCode::NOT_FOUND {
Err(AppError::NotFound)
} else {
Ok(response)
}
}
let app = Router::new()
.route("/", get(|| async { "Hello, World!" }))
.layer(middleware::from_fn(error_handler));
4.3 认证中间件
use axum::{
http::{Request, StatusCode},
middleware::{self, Next},
response::Response,
routing::get,
Router,
};
async fn auth_middleware<B>(
mut request: Request<B>,
next: Next<B>,
) -> Result<Response, StatusCode> {
let auth_header = request.headers().get("Authorization");
if auth_header.map(|h| h == "Bearer secret-token").unwrap_or(false) {
let response = next.run(request).await;
Ok(response)
} else {
Err(StatusCode::UNAUTHORIZED)
}
}
let app = Router::new()
.route("/public", get(|| async { "Public endpoint" }))
.route("/private", get(|| async { "Private endpoint" }))
.route_layer(middleware::from_fn(auth_middleware));
五、响应处理
5.1 JSON响应
use axum::{
extract::Json,
routing::get,
Router,
};
use serde::Serialize;
#[derive(Serialize)]
struct User {
id: u64,
username: String,
email: String,
}
async fn get_user() -> Json<User> {
Json(User {
id: 1,
username: "john_doe".to_string(),
email: "john@example.com".to_string(),
})
}
let app = Router::new()
.route("/users/1", get(get_user));
5.2 自定义响应
use axum::{
http::{HeaderMap, StatusCode},
response::{IntoResponse, Response},
routing::get,
Router,
};
struct CustomResponse {
status: StatusCode,
headers: HeaderMap,
body: String,
}
impl IntoResponse for CustomResponse {
fn into_response(self) -> Response {
let mut response = Response::new(self.body.into());
*response.status_mut() = self.status;
*response.headers_mut() = self.headers;
response
}
}
async fn custom_response() -> CustomResponse {
let mut headers = HeaderMap::new();
headers.insert("X-Custom-Header", "custom-value".parse().unwrap());
CustomResponse {
status: StatusCode::OK,
headers,
body: "Custom response".to_string(),
}
}
let app = Router::new()
.route("/custom", get(custom_response));
5.3 文件响应
use axum::{
routing::get,
Router,
};
use tower_http::services::ServeFile;
let app = Router::new()
.route("/", get(|| async { "Hello, World!" }))
.nest_service("/static", ServeFile::new("static/index.html"));
六、状态管理
6.1 应用状态
use axum::{
extract::State,
routing::get,
Router,
};
use std::sync::Arc;
struct AppState {
database_url: String,
api_key: String,
}
async fn get_state(State(state): State<Arc<AppState>>) -> String {
format!("Database URL: {}", state.database_url)
}
#[tokio::main]
async fn main() {
let app_state = Arc::new(AppState {
database_url: "postgres://localhost/mydb".to_string(),
api_key: "secret".to_string(),
});
let app = Router::new()
.route("/state", get(get_state))
.with_state(app_state);
Server::bind(&"127.0.0.1:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
6.2 共享状态
use axum::{
extract::State,
routing::{get, post},
Router,
};
use std::sync::{Arc, Mutex};
struct Counter {
value: Mutex<i32>,
}
async fn increment(State(counter): State<Arc<Counter>>) -> String {
let mut value = counter.value.lock().unwrap();
*value += 1;
format!("Counter: {}", value)
}
async fn get_count(State(counter): State<Arc<Counter>>) -> String {
let value = counter.value.lock().unwrap();
format!("Counter: {}", value)
}
let counter = Arc::new(Counter {
value: Mutex::new(0),
});
let app = Router::new()
.route("/counter", get(get_count).post(increment))
.with_state(counter);
七、全栈实战:用户管理API
use axum::{
extract::{Json, Path, State},
http::StatusCode,
response::IntoResponse,
routing::{delete, get, post, put},
Router, Server,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
#[derive(Debug, Serialize, Deserialize, Clone)]
struct User {
id: u64,
username: String,
email: String,
}
#[derive(Debug, Deserialize)]
struct CreateUserRequest {
username: String,
email: String,
}
#[derive(Debug, Deserialize)]
struct UpdateUserRequest {
username: Option<String>,
email: Option<String>,
}
struct AppState {
users: Mutex<HashMap<u64, User>>,
next_id: Mutex<u64>,
}
async fn create_user(
State(state): State<Arc<AppState>>,
Json(payload): Json<CreateUserRequest>,
) -> impl IntoResponse {
let mut next_id = state.next_id.lock().unwrap();
let id = *next_id;
*next_id += 1;
let user = User {
id,
username: payload.username,
email: payload.email,
};
state.users.lock().unwrap().insert(id, user.clone());
(StatusCode::CREATED, Json(user))
}
async fn get_user(
State(state): State<Arc<AppState>>,
Path(user_id): Path<u64>,
) -> impl IntoResponse {
let users = state.users.lock().unwrap();
match users.get(&user_id) {
Some(user) => Json(user.clone()).into_response(),
None => (StatusCode::NOT_FOUND, "User not found").into_response(),
}
}
async fn update_user(
State(state): State<Arc<AppState>>,
Path(user_id): Path<u64>,
Json(payload): Json<UpdateUserRequest>,
) -> impl IntoResponse {
let mut users = state.users.lock().unwrap();
match users.get_mut(&user_id) {
Some(user) => {
if let Some(username) = payload.username {
user.username = username;
}
if let Some(email) = payload.email {
user.email = email;
}
Json(user.clone()).into_response()
}
None => (StatusCode::NOT_FOUND, "User not found").into_response(),
}
}
async fn delete_user(
State(state): State<Arc<AppState>>,
Path(user_id): Path<u64>,
) -> impl IntoResponse {
let mut users = state.users.lock().unwrap();
if users.remove(&user_id).is_some() {
StatusCode::NO_CONTENT.into_response()
} else {
(StatusCode::NOT_FOUND, "User not found").into_response()
}
}
async fn list_users(State(state): State<Arc<AppState>>) -> impl IntoResponse {
let users = state.users.lock().unwrap();
let users_list: Vec<User> = users.values().cloned().collect();
Json(users_list)
}
#[tokio::main]
async fn main() {
let app_state = Arc::new(AppState {
users: Mutex::new(HashMap::new()),
next_id: Mutex::new(1),
});
let app = Router::new()
.route("/users", get(list_users).post(create_user))
.route("/users/:user_id", get(get_user).put(update_user).delete(delete_user))
.with_state(app_state);
Server::bind(&"127.0.0.1:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
总结
Axum是一个强大的Rust Web框架,通过本文的学习,你应该掌握了以下核心要点:
- Axum基础:项目初始化、路由定义、处理器函数
- 提取器:路径参数、查询参数、请求体、自定义提取器
- 中间件:日志中间件、错误处理、认证中间件
- 响应处理:JSON响应、自定义响应、文件响应
- 状态管理:应用状态、共享状态
- 实战案例:完整的用户管理API
作为从Python转向Rust的后端开发者,Axum是构建高性能Web服务的首选框架。后续文章将深入探讨Axum与数据库的集成和性能优化。
1644



被折叠的 条评论
为什么被折叠?



