Rocket Rust Web 框架

FreeGuideOnline 最新 2026-07-12

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> 内部会以不可变引用形式提供访问,你需要根据需求使用内部可变性(如 MutexRwLock)来同步数据。对于真正的异步共享状态(如数据库连接池),请使用 r2d2deadpool 等库,并在状态中存储对应的池。

数据库集成实战(以 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 提供了动态模板渲染的官方支持,内置对 TeraHandlebars 的集成。以 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