langShift

Web 开发实战

学习使用 Rust 进行 Web 开发,包括 Axum 框架、数据库操作和 API 设计,对比 JavaScript 的 Web 开发

Web 开发实战

📖 学习目标

学会使用 Rust 构建 Web 应用程序,掌握 Axum 框架、数据库操作、API 设计和部署,对比 JavaScript 的 Web 开发生态。


🎯 Web 框架对比

JavaScript 的 Web 开发

JavaScript 使用 Express.js 等框架:

正在加载...

Rust 的 Web 开发

Rust 使用 Axum 框架:

正在加载...

Web 框架差异

  1. 性能: Rust 编译到机器码,性能更高
  2. 类型安全: Rust 编译时检查,JavaScript 运行时检查
  3. 内存安全: Rust 无数据竞争,JavaScript 单线程事件循环
  4. 生态系统: JavaScript 生态更成熟,Rust 生态正在快速发展

🗄️ 数据库操作

使用 SQLx 进行数据库操作

正在加载...

生产环境错误处理提示

在生产环境中,数据库连接和初始化等操作应该进行更健壮的错误处理,例如使用 ResultBox<dyn std::error::Error> 来捕获和传播所有可能的错误,而不是简单地使用 unwrap()expect()。这样可以确保应用程序在遇到数据库问题时能够优雅地失败或提供有意义的错误信息。

🔐 认证与授权

JWT 认证实现

正在加载...

🎯 练习题

练习 1: 创建简单的 API

创建一个简单的待办事项 API:

查看答案
use axum::{
extract::{Path, State},
http::StatusCode,
response::Json,
routing::{get, post, put, delete},
Router,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
#[derive(Debug, Serialize, Deserialize, Clone)]
struct Todo {
id: u32,
title: String,
completed: bool,
}
#[derive(Debug, Deserialize)]
struct CreateTodoRequest {
title: String,
}
#[derive(Clone)]
struct AppState {
todos: Arc<RwLock<HashMap<u32, Todo>>>,
}
impl AppState {
fn new() -> Self {
Self {
todos: Arc::new(RwLock::new(HashMap::new())),
}
}
}
async fn get_todos(State(state): State<AppState>) -> Json<Vec<Todo>> {
let todos = state.todos.read().await;
let todo_list: Vec<Todo> = todos.values().cloned().collect();
Json(todo_list)
}
async fn create_todo(
State(state): State<AppState>,
Json(payload): Json<CreateTodoRequest>,
) -> Json<Todo> {
let mut todos = state.todos.write().await;
let new_id = todos.keys().max().unwrap_or(&0) + 1;
let new_todo = Todo {
id: new_id,
title: payload.title,
completed: false,
};
todos.insert(new_id, new_todo.clone());
Json(new_todo)
}
async fn toggle_todo(
State(state): State<AppState>,
Path(id): Path<u32>,
) -> Result<Json<Todo>, StatusCode> {
let mut todos = state.todos.write().await;
if let Some(todo) = todos.get_mut(&id) {
todo.completed = !todo.completed;
Ok(Json(todo.clone()))
} else {
Err(StatusCode::NOT_FOUND)
}
}
async fn delete_todo(
State(state): State<AppState>,
Path(id): Path<u32>,
) -> StatusCode {
let mut todos = state.todos.write().await;
if todos.remove(&id).is_some() {
StatusCode::NO_CONTENT
} else {
StatusCode::NOT_FOUND
}
}
#[tokio::main]
async fn main() {
let state = AppState::new();
let app = Router::new()
.route("/api/todos", get(get_todos))
.route("/api/todos", post(create_todo))
.route("/api/todos/:id/toggle", put(toggle_todo))
.route("/api/todos/:id", delete(delete_todo))
.with_state(state);
println!("待办事项 API 运行在 http://localhost:3000");
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
.await
.unwrap();
axum::serve(listener, app).await.unwrap();
}

练习 2: 添加中间件

为 API 添加日志中间件和错误处理:

查看答案
use axum::{
extract::{Request, State},
http::StatusCode,
middleware::Next,
response::Response,
routing::{get, post},
Router,
};
use std::time::Instant;
use tower_http::trace::TraceLayer;
async fn logging_middleware(
request: Request,
next: Next,
) -> Response {
let start = Instant::now();
let method = request.method().clone();
let uri = request.uri().clone();
let response = next.run(request).await;
let duration = start.elapsed();
println!(
"{} {} - {} - {:?}",
method,
uri,
response.status(),
duration
);
response
}
async fn error_handler(
State(state): State<AppState>,
request: Request,
next: Next,
) -> Result<Response, StatusCode> {
match next.run(request).await {
response if response.status().is_success() => Ok(response),
response => {
println!("错误响应: {}", response.status());
Ok(response)
}
}
}
fn create_router_with_middleware() -> Router {
let state = AppState::new();
Router::new()
.route("/", get(|| async { "Hello World" }))
.route("/api/test", post(|| async { "Test endpoint" }))
.layer(TraceLayer::new_for_http())
.route_layer(axum::middleware::from_fn(logging_middleware))
.with_state(state)
}

练习 3: 数据库集成

创建一个简单的用户管理 API,集成 SQLite 数据库:

查看答案
use axum::{
extract::{Path, State},
http::StatusCode,
response::Json,
routing::{get, post},
Router,
};
use serde::{Deserialize, Serialize};
use sqlx::sqlite::SqlitePool;
#[derive(Debug, Serialize, Deserialize)]
struct User {
id: Option<i32>,
name: String,
email: String,
}
#[derive(Clone)]
struct AppState {
pool: SqlitePool,
}
async fn create_user(
State(state): State<AppState>,
Json(user): Json<User>,
) -> Result<Json<User>, StatusCode> {
let user = sqlx::query_as!(
User,
r#"
INSERT INTO users (name, email)
VALUES (?, ?)
RETURNING id, name, email
"#,
user.name,
user.email
)
.fetch_one(&state.pool)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(Json(user))
}
async fn get_users(State(state): State<AppState>) -> Result<Json<Vec<User>>, StatusCode> {
let users = sqlx::query_as!(
User,
r#"
SELECT id, name, email
FROM users
"#
)
.fetch_all(&state.pool)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(Json(users))
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let pool = SqlitePool::connect("sqlite:users.db").await?;
// 创建表
sqlx::query(
r#"
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
)
"#
)
.execute(&pool)
.await?;
let state = AppState { pool };
let app = Router::new()
.route("/api/users", post(create_user))
.route("/api/users", get(get_users))
.with_state(state);
println!("用户 API 运行在 http://localhost:3000");
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await?;
axum::serve(listener, app).await?;
Ok(())
}

📝 总结

在这一章中,我们学习了 Rust 的 Web 开发:

  1. Axum 框架: 高性能的 Rust Web 框架
  2. 数据库操作: 使用 SQLx 进行类型安全的数据库操作
  3. 认证授权: JWT 认证和中间件实现
  4. API 设计: RESTful API 设计和错误处理
  5. 与 JavaScript 对比: 性能、类型安全和生态系统差异

关键要点

  • Rust Web 开发提供更好的性能和类型安全
  • Axum 框架简洁高效,适合构建 API
  • SQLx 提供编译时 SQL 查询验证
  • JWT 认证确保 API 安全性

下一步学习

在下一章中,我们将学习 Rust 的系统级编程和高级主题,包括 unsafe 代码、宏系统和性能优化。


继续学习: 系统级编程与高级主题