错误处理 #

良好的错误处理是高质量应用的重要标志。本节将介绍Rocket中的错误处理机制。

错误捕获器 #

基本捕获器 #

rust
use rocket::Request;
use rocket::response::content::RawHtml;

#[catch(404)]
fn not_found(_req: &Request) -> RawHtml<&'static str> {
    RawHtml("<h1>404 - Page Not Found</h1>")
}

#[catch(500)]
fn internal_error(_req: &Request) -> RawHtml<&'static str> {
    RawHtml("<h1>500 - Internal Server Error</h1>")
}

#[launch]
fn rocket() -> _ {
    rocket::build()
        .register("/", catchers![not_found, internal_error])
}

动态错误页面 #

rust
use rocket::Request;
use rocket_dyn_templates::Template;
use rocket::serde::Serialize;

#[derive(Serialize)]
struct ErrorContext {
    code: u16,
    message: String,
    path: String,
}

#[catch(404)]
fn not_found(req: &Request) -> Template {
    Template::render("error", &ErrorContext {
        code: 404,
        message: "Page not found".to_string(),
        path: req.uri().path().to_string(),
    })
}

#[catch(500)]
fn internal_error(_req: &Request) -> Template {
    Template::render("error", &ErrorContext {
        code: 500,
        message: "Internal server error".to_string(),
        path: String::new(),
    })
}

自定义错误类型 #

错误枚举 #

rust
use rocket::http::Status;
use rocket::response::{Responder, Result};
use rocket::serde::json::Json;
use rocket::serde::Serialize;
use std::fmt;

#[derive(Debug)]
pub enum ApiError {
    NotFound(String),
    Unauthorized,
    BadRequest(String),
    InternalError(String),
}

impl fmt::Display for ApiError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ApiError::NotFound(msg) => write!(f, "Not Found: {}", msg),
            ApiError::Unauthorized => write!(f, "Unauthorized"),
            ApiError::BadRequest(msg) => write!(f, "Bad Request: {}", msg),
            ApiError::InternalError(msg) => write!(f, "Internal Error: {}", msg),
        }
    }
}

#[derive(Serialize)]
struct ErrorResponse {
    error: String,
    code: u16,
}

impl<'r> Responder<'r, 'r> for ApiError {
    fn respond_to(self, _req: &'r rocket::Request<'_>) -> Result<'r> {
        let (status, message) = match self {
            ApiError::NotFound(msg) => (Status::NotFound, msg),
            ApiError::Unauthorized => (Status::Unauthorized, "Unauthorized".to_string()),
            ApiError::BadRequest(msg) => (Status::BadRequest, msg),
            ApiError::InternalError(msg) => (Status::InternalServerError, msg),
        };
        
        let body = Json(ErrorResponse {
            error: message,
            code: status.code,
        });
        
        rocket::response::Response::build_from(body.respond_to(_req)?)
            .status(status)
            .ok()
    }
}

使用自定义错误 #

rust
#[get("/users/<id>")]
async fn get_user(id: i32, db: &State<Db>) -> Result<Json<User>, ApiError> {
    db.find_user(id)
        .await
        .map(Json)
        .ok_or_else(|| ApiError::NotFound(format!("User {} not found", id)))
}

Result类型处理 #

Option到Result转换 #

rust
use rocket::http::Status;

#[get("/items/<id>")]
fn get_item(id: u32) -> Result<Json<Item>, Status> {
    find_item(id)
        .map(Json)
        .ok_or(Status::NotFound)
}

链式错误处理 #

rust
#[get("/users/<user_id>/posts/<post_id>")]
async fn get_user_post(
    user_id: u32,
    post_id: u32,
    db: &State<Db>,
) -> Result<Json<Post>, ApiError> {
    let user = db.find_user(user_id)
        .await
        .ok_or_else(|| ApiError::NotFound(format!("User {} not found", user_id)))?;
    
    let post = db.find_post(post_id)
        .await
        .ok_or_else(|| ApiError::NotFound(format!("Post {} not found", post_id)))?;
    
    if post.user_id != user.id {
        return Err(ApiError::Unauthorized);
    }
    
    Ok(Json(post))
}

表单验证错误 #

rust
use rocket::form::{self, Form, Contextual};

#[derive(FromForm)]
struct UserForm {
    #[field(validate = len(3..=20))]
    username: String,
    
    #[field(validate = len(8..))]
    password: String,
}

#[post("/register", data = "<form>")]
fn register(form: Result<Form<UserForm>, form::Error<'_>>) -> Result<Redirect, String> {
    let form = form.map_err(|e| format!("Form error: {}", e))?;
    
    Ok(Redirect::to("/success"))
}

完整示例 #

rust
#[macro_use] extern crate rocket;

use rocket::http::Status;
use rocket::response::{Responder, Result};
use rocket::serde::json::Json;
use rocket::serde::Serialize;
use rocket::response::content::RawHtml;

#[derive(Debug)]
enum ApiError {
    NotFound(String),
    BadRequest(String),
    Internal(String),
}

#[derive(Serialize)]
struct ErrorResponse {
    error: String,
    code: u16,
}

impl<'r> Responder<'r, 'r> for ApiError {
    fn respond_to(self, _req: &'r rocket::Request<'_>) -> Result<'r> {
        let (status, message) = match self {
            ApiError::NotFound(m) => (Status::NotFound, m),
            ApiError::BadRequest(m) => (Status::BadRequest, m),
            ApiError::Internal(m) => (Status::InternalServerError, m),
        };
        
        rocket::response::Response::build()
            .status(status)
            .header(rocket::http::ContentType::JSON)
            .sized_body(
                serde_json::to_string(&ErrorResponse {
                    error: message,
                    code: status.code,
                }).unwrap().len(),
                std::io::Cursor::new(
                    serde_json::to_string(&ErrorResponse {
                        error: String::new(),
                        code: status.code,
                    }).unwrap()
                )
            )
            .ok()
    }
}

#[catch(404)]
fn not_found() -> RawHtml<&'static str> {
    RawHtml("<h1>404 - Not Found</h1>")
}

#[catch(500)]
fn server_error() -> RawHtml<&'static str> {
    RawHtml("<h1>500 - Server Error</h1>")
}

#[get("/")]
fn index() -> &'static str {
    "API Running"
}

#[get("/user/<id>")]
fn get_user(id: u32) -> Result<Json<serde_json::Value>, ApiError> {
    if id == 0 {
        Err(ApiError::NotFound("User not found".to_string()))
    } else if id > 1000 {
        Err(ApiError::BadRequest("Invalid user ID".to_string()))
    } else {
        Ok(Json(serde_json::json!({ "id": id, "name": "User" })))
    }
}

#[launch]
fn rocket() -> _ {
    rocket::build()
        .register("/", catchers![not_found, server_error])
        .mount("/api", routes![index, get_user])
}

下一步 #

掌握了错误处理后,让我们继续学习 配置管理,了解Rocket的配置系统。

最后更新:2026-03-28