Rocket Rust Web 框架
bash
检查 Rust 版本
rustc --version
### 创建新项目
使用 Cargo 创建一个二进制项目,并在 `Cargo.toml` 中添加 Rocket 依赖:
```bash
cargo new rocket-hello --bin
cd rocket-hello
编辑 Cargo.toml:
[package]
name = "rocket-hello"
version = "0.1.0"
edition = "2021"
[dependencies]
rocket = "0.5.1"
注意:Rocket 0.5 依赖
tokio异步运行时,无需额外配置,框架会替你管理。
第一个 Rocket 应用:Hello, world!
让我们用最少的代码启动一个 Web 服务。打开 src/main.rs,替换为以下内容:
#[macro_use] extern crate rocket;
#[get("/")]
fn index() -> &'static str {
"Hello, Rocket!"
}
#[launch]
fn rocket() -> _ {
rocket::build().mount("/", routes![index])
}
解释部分关键元素:
#[get("/")]是一个属性宏,它将函数注册为处理GET /请求的路由处理器。index函数返回一个静态字符串切片,Rocket 会自动将其转换为 HTTP 响应。#[launch]宏将标记的rocket函数作为入口点,它返回一个Rocket<Build>实例。rocket::build()创建 Rocket 应用构建器,mount将路由挂载到指定路径下。
运行程序:
cargo run
在浏览器中访问 http://localhost:8000,你就能看到 Hello, Rocket! 的字样。Rocket 默认监听 8000 端口,你可以通过 ROCKET_PORT 环境变量或配置文件更改。
路由与请求处理进阶
Rocket 的路由系统极其灵活,支持静态路径、动态参数以及多种请求方法。
动态路径参数
使用尖括号捕获 URL 中的变量,并通过函数参数直接提取:
#[get("/hello/<name>")]
fn hello(name: &str) -> String {
format!("你好,{}!", name)
}
当你访问 /hello/World 时,name 的值为 "World"。Rocket 还支持类型安全的多段路径,例如 <param..> 可以捕获剩余路径。参数类型可以是任何实现了 FromParam 特型的类型,内置支持 String、&str、整数等。
查询参数与表单处理
对于查询字符串(如 ?id=10)或 POST 表单数据,Rocket 利用 请求守卫(Request Guards) 来自动解析。你可以定义与请求体结构对应的结构体:
use rocket::serde::Deserialize;
#[derive(Deserialize)]
#[serde(crate = "rocket::serde")]
struct UserInput {
username: String,
age: u8,
}
#[post("/submit", data = "<input>")]
fn submit(input: rocket::form::Form<UserInput>) -> String {
format!("收到用户:{},年龄:{}", input.username, input.age)
}
Form<UserInput> 守卫会读取表单数据(或 JSON,当 Content-Type 为 application/json 时)并尝试反序列化。如果数据不合法,Rocket 会自动返回 422 Unprocessable Entity 错误,无需手动编写校验代码。
JSON 原生支持
Rocket 内置对 JSON 的序列化与反序列化支持,基于 serde。你只需将数据类型包裹在 rocket::serde::json::Json<T> 中:
use rocket::serde::{json::Json, Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
#[serde(crate = "rocket::serde")]
struct Task {
id: u64,
description: String,
}
#[post("/task", format = "json", data = "<task>")]
fn create_task(task: Json<Task>) -> Json<Task> {
// 这里可以直接操作 task.0,或进行数据库存储
task
}
注意 format = "json" 限制了路由仅匹配请求头中 Content-Type 为 application/json 的请求。如果你想同时支持多个格式,可以省略该限制,但请求守卫会尝试解析 JSON,失败则返回相应错误。
状态管理与资源共享
Web 应用通常需要共享数据库连接池、配置对象等有状态资源。Rocket 通过**状态(State)**机制管理这些资源,它是类型驱动且线程安全的。
添加状态
在构建 Rocket 实例时使用 manage 方法注册一个状态对象:
use std::sync::Mutex;
struct AppConfig {
visitor_count: Mutex<u64>,
}
#[launch]
fn rocket() -> _ {
rocket::build()
.manage(AppConfig { visitor_count: Mutex::new(0) })
.mount("/", routes![count])
}
在路由中访问状态
你可以在任何路由处理器中通过 &State<T> 参数获取状态引用:
use rocket::State;
#[get("/count")]
fn count(config: &State<AppConfig>) -> String {
let mut count = config.visitor_count.lock().unwrap();
*count += 1;
format!("本页已被访问 {} 次", count)
}
Rocket 保证状态的生命周期与应用程序一致,并且 State<T> 内部会以不可变引用形式提供访问,你需要根据需求使用内部可变性(如 Mutex 或 RwLock)来同步数据。对于真正的异步共享状态(如数据库连接池),请使用 r2d2 或 deadpool 等库,并在状态中存储对应的池。
数据库集成实战(以 SQLx 为例)
Rocket 并未强制绑定某个 ORM,你可以自由选择异步数据库驱动。这里展示如何搭配 sqlx 和 PostgreSQL 构建一个简单的待办事项 API。
首先,在 Cargo.toml 中添加依赖:
[dependencies]
rocket = "0.5.1"
sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres"] }
tokio = { version = "1", features = ["full"] }
创建数据库连接池
启动时创建连接池并交给 Rocket 管理:
use sqlx::postgres::PgPoolOptions;
use rocket::fairing::AdHoc;
#[launch]
fn rocket() -> _ {
let pool = PgPoolOptions::new()
.max_connections(5)
.connect_lazy("postgres://user:pass@localhost/todo_db")
.expect("数据库连接失败");
rocket::build()
.manage(pool)
.attach(AdHoc::on_ignite("DB Migrations", |rocket| async {
// 可选:在这里运行数据库迁移
Ok(rocket)
}))
.mount("/api", routes![list_tasks, create_task])
}
编写异步路由处理器
Rocket 0.5 原生支持 async 路由,可以直接在处理器中执行 .await:
use rocket::State;
use sqlx::PgPool;
#[get("/tasks")]
async fn list_tasks(pool: &State<PgPool>) -> Json<Vec<Task>> {
let tasks = sqlx::query_as!(Task, "SELECT id, description FROM tasks")
.fetch_all(pool.inner())
.await
.unwrap_or_default();
Json(tasks)
}
状态中的连接池是线程安全的,pool.inner() 返回 &PgPool,可用于执行查询。这种方式让你能够充分利用 Rust 异步生态的高性能特性。
模板渲染与前端集成
Rocket 提供了动态模板渲染的官方支持,内置对 Tera 和 Handlebars 的集成。以 Handlebars 为例:
rocket_dyn_templates = { version = "0.2", features = ["handlebars"] }
在 Cargo.toml 添加后,将模板引擎附着到 Rocket 实例:
use rocket_dyn_templates::Template;
use serde::Serialize;
#[derive(Serialize)]
struct HomeContext {
title: String,
messages: Vec<String>,
}
#[get("/")]
fn home() -> Template {
let context = HomeContext {
title: "Rocket 搭把手".into(),
messages: vec!["你好!".into(), "欢迎学习 Rocket".into()],
};
Template::render("index", &context)
}
#[launch]
fn rocket() -> _ {
rocket::build()
.attach(Template::fairing())
.mount("/", routes![home])
}
模板文件默认放在 templates/ 目录下,需创建 templates/index.html.hbs:
<h1>{{ title }}</h1>
<ul>
{{#each messages}}
<li>{{ this }}</li>
{{/each}}
</ul>
这样,Rocket 就能根据请求渲染动态 HTML 页面,与前后端分离的 JSON API 互为补充。
中间件与 Fairing
Rocket 的中间件体系称为 Fairing,它是附着于请求生命周期的一种钩子机制,可用于日志记录、性能监控、CORS 处理等。Rocket 已内置了如 Shield(安全头)、AdHoc 等 Fairing。
添加自定义 Fairing
例如,实现一个简单的请求计时器:
use rocket::fairing::{Fairing, Info, Kind};
use rocket::{Request, Response};
use std::time::Instant;
pub struct RequestTimer;
#[rocket::async_trait]
impl Fairing for RequestTimer {
fn info(&self) -> Info {
Info {
name: "请求计时器",
kind: Kind::Request | Kind::Response,
}
}
async fn on_request(&self, req: &mut Request<'_>, _: &mut rocket::Data<'_>) {
req.local_cache(|| Instant::now());
}
async fn on_response<'r>(&self, req: &'r Request<'_>, res: &mut Response<'r>) {
let start = req.local_cache(|| Instant::now());
let duration = start.elapsed();
println!("{} 耗时 {}ms", req.uri(), duration.as_millis());
}
}
// 随后在 rocket() 中 .attach(RequestTimer)
Fairing 让你能够以声明方式扩展框架能力,而不会侵入业务代码。
测试
Rocket 提供了内置的测试客户端,让你可以轻松编写集成测试。在 tests 目录下创建测试文件或直接在 main.rs 同模块中使用 #[cfg(test)]:
#[cfg(test)]
mod tests {
use super::rocket;
use rocket::local::blocking::Client;
use rocket::http::Status;
#[test]
fn test_hello() {
let client = Client::tracked(rocket()).unwrap();
let response = client.get("/").dispatch();
assert_eq!(response.status(), Status::Ok);
assert_eq!(response.into_string().unwrap(), "Hello, Rocket!");
}
}
运行 cargo test 即可验证所有端点。测试客户端不会真正绑定端口,执行速度很快,适合持续集成。
部署建议
开发完成后,使用 cargo build --release 编译优化版本。生成的二进制文件可直接部署到任何支持 Linux 的服务器或容器中。Rocket 的默认异步运行时 tokio 能够充分利用多核 CPU,通常单个实例就能处理数万并发连接。
你可以通过 Rocket.toml 配置文件调整地址、端口、秘钥等参数:
[default]
address = "0.0.0.0"
port = 8080
workers = 4