Skip to content

Rust Rocket Web 框架 ​

Rocket 是 Rust 生态非常流行的Web服务框架,主打开发体验好、类型安全、编译期检查,面向 REST API、Web应用。

注意:Rocket 分两个大版本:

  • Rocket 0.4:基于老版本 Rust,使用 nightly Rust,已经停止维护
  • Rocket 0.5‑rc(现在主流):稳定版,支持稳定 Rust,是目前项目使用版本

核心特点 ​

  1. 编译期安全:路由、参数、请求处理全部在编译时校验,很多错误运行前就暴露。
  2. 宏驱动,代码简洁:#[get]、#[post] 宏定义路由,写法非常直观。
  3. 内置全套能力:JSON序列化/反序列化、表单、Cookie、Session、静态文件、数据库集成、TLS、中间件、状态管理。
  4. 类型提取器(Guard):FromRequest 机制,自动从请求提取参数、身份校验,不用手动解析request。
  5. 异步支持:0.5 基于 tokio 异步运行时,高性能。
  6. 内置JSON:依赖 serde,直接返回结构体自动序列化为json。

简单Hello World示例(0.5) ​

Cargo.toml ​

toml
[package]
name = "rocket-demo"
version = "0.1.0"
edition = "2021"

[dependencies]
rocket = "0.5.0-rc.3"

src/main.rs ​

rust
#[macro_use]
extern crate rocket;

#[get("/")]
fn index() -> &'static str {
    "Hello Rocket!"
}

#[launch]
fn rocket() -> _ {
    rocket::build().mount("/", routes![index])
}

运行后访问 http://127.0.0.1:8000。

常用功能演示 ​

1. URL路径参数 ​

rust
#[get("/hello/<name>")]
fn hello(name: &str) -> String {
    format!("Hello, {}!", name)
}

2. 返回JSON(需要serde) ​

toml
rocket = { version = "0.5.0-rc.3", features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
rust
use rocket::serde::json::Json;
use serde::Serialize;

#[derive(Serialize)]
struct User {
    id: u64,
    name: String,
}

#[get("/user")]
fn get_user() -> Json<User> {
    Json(User {
        id: 1,
        name: "Alice".into(),
    })
}

3. POST接收JSON ​

rust
#[post("/user", data = "<user>")]
fn create_user(user: Json<User>) -> Json<User> {
    user
}

4. 状态共享(全局状态) ​

rust
#[launch]
fn rocket() -> _ {
    rocket::build()
        .manage(String::from("全局配置"))
        .mount("/", routes![show_state])
}

#[get("/state")]
fn show_state(state: &State<String>) -> String {
    state.to_string()
}

5. 请求Guard(权限校验) ​

实现FromRequest,可以做登录鉴权,自动拦截非法请求,不需要每个接口写if判断。

重要概念 ​

  1. Routes(路由):#[get] #[post] #[put] #[delete]
  2. Mount:挂载路由,设置路由前缀,例如 .mount("/api", routes![xxx])
  3. State:全局应用状态,数据库连接池、配置等。
  4. Guard(提取器):FromRequest,从Request里提取数据,鉴权、解析token。
  5. Fairing:火箭的中间件,全局钩子,请求前后处理,日志、CORS、初始化数据库。
  6. Responder:响应实现,自定义返回类型。

生态与数据库 ​

Rocket 官方配套 rocket_sync_db_pools 数据库连接池,支持 PostgreSQL、MySQL、SQLite。

toml
rocket_sync_db_pools = { version = "0.1.0-rc.3", features = ["postgres"] }

配置在 Rocket.toml 文件中写数据库连接。

Rocket 优缺点 ​

✅ 优点

  • Rust Web框架里开发体验最好,语法简洁,上手快
  • 编译期校验,少运行时bug
  • 内置丰富功能,不用大量找第三方库
  • 异步tokio,性能优秀
  • 文档非常完善

❌ 缺点

  • 0.5 版本长期rc,正式版迟迟没出(但是rc版本生产可用)
  • 相比 axum,抽象层更高,性能略低一点;axum更偏底层,Rocket更偏应用层
  • 部分高级自定义需要理解Rocket内部抽象

和其他Rust Web框架对比 ​

框架特点
Rocket高层封装,开发友好,内置功能多,适合快速写API、业务服务
axumtokio生态,轻量底层,性能极高,生态灵活,适合高性能服务
warp基于hyper,函数式风格,组合式路由

选型建议:

  • 快速开发业务API、后台服务,想要少写样板代码 → Rocket
  • 追求极致性能、高度自定义、微服务 → axum

部署 ​

Rocket支持:

  • 直接编译二进制,无依赖,部署简单
  • 支持TLS,可搭配nginx反向代理
  • 环境变量配置,Rocket.toml 区分开发/生产环境。

REST API 完整示例 ​

版本:0.5.0‑rc.3,使用稳定 Rust,包含:CORS、JWT登录鉴权、PostgreSQL数据库、用户CRUD、请求Guard、配置文件。

1. Cargo.toml ​

toml
[package]
name = "rocket-rest-demo"
version = "0.1.0"
edition = "2021"

[dependencies]
rocket = { version = "0.5.0-rc.3", features = ["json"] }
rocket_sync_db_pools = { version = "0.1.0-rc.3", features = ["postgres"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
jsonwebtoken = "9.2"
chrono = { version = "0.4", features = ["serde"] }
dotenv = "0.15"
rocket_cors = "0.5.0-rc.3"

2. Rocket.toml(配置文件,项目根目录) ​

toml
[default]
address = "127.0.0.1"
port = 8000
log_level = "debug"

[default.databases]
db = { url = "postgres://postgres:123456@localhost:5432/rocket_demo" }

[production]
log_level = "info"

[development]
log_level = "debug"

3. 数据库迁移 SQL ​

创建数据库 rocket_demo,执行:

sql
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    password_hash VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE
);

4. src/main.rs 完整代码 ​

rust
#[macro_use]
extern crate rocket;

use rocket::{
    http::Status,
    serde::json::Json,
    State,
    FromRequest,
    Request,
    Outcome,
};
use rocket_sync_db_pools::database;
use rocket_cors::{AllowedOrigins, CorsOptions};
use serde::{Deserialize, Serialize};
use jsonwebtoken::{encode, EncodingKey, Header, Validation, decode, DecodingKey};
use chrono::{Utc, Duration};

// ===================== 数据库连接 =====================
#[database("db")]
pub struct DbConn(rocket_sync_db_pools::diesel::PgConnection);

// ===================== 模型 =====================
#[derive(Debug, Serialize, Deserialize)]
pub struct User {
    pub id: i32,
    pub username: String,
    pub email: String,
}

#[derive(Debug, Deserialize)]
pub struct RegisterReq {
    pub username: String,
    pub password: String,
    pub email: String,
}

#[derive(Debug, Deserialize)]
pub struct LoginReq {
    pub username: String,
    pub password: String,
}

#[derive(Debug, Serialize)]
pub struct TokenResp {
    pub token: String,
}

// ===================== JWT 配置 全局状态 =====================
#[derive(Clone)]
pub struct JwtConfig {
    pub secret: String,
}

// ===================== Guard:鉴权提取器,需要登录的接口自动校验token =====================
#[derive(Debug)]
pub struct AuthUser {
    pub user_id: i32,
}

#[rocket::async_trait]
impl<'r> FromRequest<'r> for AuthUser {
    type Error = ();

    async fn from_request(req: &'r Request<'_>) -> Outcome<Self, Self::Error> {
        let auth_header = match req.headers().get_one("Authorization") {
            Some(h) => h,
            None => return Outcome::Error((Status::Unauthorized, ())),
        };

        let token = match auth_header.strip_prefix("Bearer ") {
            Some(t) => t,
            None => return Outcome::Error((Status::Unauthorized, ())),
        };

        let jwt_config = match req.rocket().state::<JwtConfig>() {
            Some(c) => c,
            None => return Outcome::Error((Status::InternalServerError, ())),
        };

        let decoding_key = DecodingKey::from_secret(jwt_config.secret.as_bytes());
        let token_data = match decode::<serde_json::Value>(token, &decoding_key, &Validation::default()) {
            Ok(d) => d,
            Err(_) => return Outcome::Error((Status::Unauthorized, ())),
        };

        let user_id = match token_data.claims["user_id"].as_i64() {
            Some(id) => id as i32,
            None => return Outcome::Error((Status::Unauthorized, ())),
        };

        Outcome::Success(AuthUser { user_id })
    }
}

// ===================== 接口路由 =====================

/// 注册用户
#[post("/register", data = "<req>")]
async fn register(conn: DbConn, req: Json<RegisterReq>) -> Result<Json<User>, Status> {
    // 这里简单演示,生产环境务必使用bcrypt做密码哈希,不要明文存储
    let user: User = conn.run(move |c| {
        diesel::insert_into(users::table)
            .values((
                users::username.eq(&req.username),
                users::password_hash.eq(&req.password),
                users::email.eq(&req.email),
            ))
            .returning((users::id, users::username, users::email))
            .get_result(c)
            .map(|(id, username, email)| User { id, username, email })
    }).await.map_err(|_| Status::InternalServerError)?;

    Ok(Json(user))
}

/// 登录获取JWT Token
#[post("/login", data = "<req>")]
async fn login(conn: DbConn, jwt_cfg: &State<JwtConfig>, req: Json<LoginReq>) -> Result<Json<TokenResp>, Status> {
    let user: Option<User> = conn.run(move |c| {
        users::table
            .filter(users::username.eq(&req.username))
            .filter(users::password_hash.eq(&req.password))
            .select((users::id, users::username, users::email))
            .first(c)
            .optional()
    }).await.map_err(|_| Status::InternalServerError)?;

    let user = user.ok_or(Status::Unauthorized)?;

    let exp = Utc::now() + Duration::hours(24);
    let claims = serde_json::json!({
        "user_id": user.id,
        "exp": exp.timestamp()
    });

    let token = encode(
        &Header::default(),
        &claims,
        &EncodingKey::from_secret(jwt_cfg.secret.as_bytes()),
    ).map_err(|_| Status::InternalServerError)?;

    Ok(Json(TokenResp { token }))
}

/// 需要登录才能访问:获取当前用户信息
#[get("/me")]
async fn get_me(conn: DbConn, auth: AuthUser) -> Result<Json<User>, Status> {
    let user = conn.run(move |c| {
        users::table
            .filter(users::id.eq(auth.user_id))
            .select((users::id, users::username, users::email))
            .first(c)
    }).await.map_err(|_| Status::InternalServerError)?;

    Ok(Json(user))
}

/// 公开接口,无需登录
#[get("/health")]
fn health() -> &'static str {
    "ok"
}

// ===================== 启动服务:CORS、全局状态、数据库、挂载路由 =====================
#[launch]
fn rocket() -> _ {
    // CORS配置
    let cors = CorsOptions::default()
        .allowed_origins(AllowedOrigins::all())
        .allow_credentials(true);

    rocket::build()
        .attach(DbConn::fairing())
        .attach(cors.to_cors().unwrap())
        .manage(JwtConfig {
            secret: "my_super_secret_key_123456".to_string(),
        })
        .mount("/api", routes![register, login, get_me, health])
}

// diesel 表定义,放在文件末尾
mod users {
    use diesel::table;
    table! {
        users (id) {
            id -> Int4,
            username -> Varchar,
            password_hash -> Varchar,
            email -> Varchar,
        }
    }
}

5. 运行与测试 ​

  1. 启动PostgreSQL,建好数据库 rocket_demo,执行上面建表SQL
  2. 修改 Rocket.toml 里数据库账号密码
  3. cargo run,服务启动 http://127.0.0.1:8000/api

接口测试示例 ​

  1. 注册
POST /api/register
{
  "username":"test",
  "password":"123456",
  "email":"test@example.com"
}
  1. 登录拿token
POST /api/login
{
  "username":"test",
  "password":"123456"
}
  1. 访问需要鉴权接口,请求头带上: Authorization: Bearer 你的token
GET /api/me

⚠️生产环境重要提醒 ​

  1. 密码不要明文存储,示例只是演示,实际使用 bcrypt 做哈希
  2. JWT密钥不要硬编码,使用环境变量传入
  3. 调整CORS:不要使用AllowedOrigins::all(),限定允许的前端域名
  4. 数据库错误处理完善,增加参数校验
  5. Rocket 0.5‑rc 虽然可以上生产,但留意官方更新

关键知识点回顾 ​

  1. DbConn:rocket_sync_db_pools 数据库连接池,run() 执行数据库操作
  2. FromRequest Guard:鉴权提取器,函数参数写AuthUser就自动做token校验,不用每个接口写重复鉴权代码
  3. Fairing:中间件,这里挂载CORS、数据库初始化
  4. State<T>:全局单例状态,存放JWT密钥、配置等全局对象

如果你需要,我可以给你:

  • 改造为axum版本对比
  • 增加分页、错误统一返回、日志、完整的crud
  • 或者docker部署配置