You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust Rocket框架实现JSON格式422错误返回的方法

Rocket框架自定义422错误JSON响应方案

问题背景

使用Rocket开发API时,收到格式错误的JSON请求会返回HTML格式的422 Unprocessable Entity页面,希望返回指定结构的JSON错误信息:

{
  "error": {
    "message": "missing field `geoLocation`"
  }
}

现有实体与控制器代码如下:

#[derive(Deserialize, Serialize, Clone, Validate)]
#[serde(crate = "rocket::serde", rename_all = "camelCase")]
pub struct Point {
    #[validate(length(min = 10))]
    pub picture: String,
    pub geo_location: GeoLocation,
    pub description: String,
    pub address: String,
    pub place_name: String,
    pub user: String,
}

#[post("/point", format = "application/json", data = "<data>")]
pub fn new_point(
    data: Validated<Json<Point>>,
) -> Result<status::Custom<Json<Point>>, status::Custom<Json<ErrorType>>> {
    let point: Point = data.into_inner().into_inner();
    // 业务逻辑处理
}

尝试用#[catch(422)]捕获错误时无法获取错误详情,需解决如何提取解析/验证错误并返回指定JSON格式的问题。


解决方案

1. 定义统一错误响应结构

先实现符合要求的错误类型,确保能被Rocket序列化为JSON:

use rocket::serde::Serialize;

#[derive(Serialize)]
#[serde(crate = "rocket::serde")]
pub struct ErrorResponse {
    error: ErrorDetail,
}

#[derive(Serialize)]
#[serde(crate = "rocket::serde")]
pub struct ErrorDetail {
    message: String,
}

impl ErrorResponse {
    fn new(message: String) -> Self {
        ErrorResponse {
            error: ErrorDetail { message },
        }
    }
}

2. 实现422错误捕获器

Rocket的错误捕获器可通过request.local_cache()获取底层错误,需同时处理两种422场景:JSON解析错误和Validated的验证错误:

use rocket::{Request, response::status::Custom, serde::Json, catch};
use rocket::form::FormError;

#[catch(422)]
fn unprocessable_entity(request: &Request) -> Custom<Json<ErrorResponse>> {
    // 提取JSON解析错误
    if let Some(form_error) = request.local_cache::<Option<FormError>, _>(|| None) {
        let err_msg = match form_error {
            FormError::Parse(err) => err.to_string(),
            FormError::Validate(err) => err.to_string(),
            _ => "Invalid request data".to_string(),
        };
        return Custom(
            rocket::http::Status::UnprocessableEntity,
            Json(ErrorResponse::new(err_msg)),
        );
    }

    // 提取Validated验证错误
    if let Some(validation_err) = request.local_cache::<Option<rocket::validation::ValidationErrors>, _>(|| None) {
        let err_msg = validation_err
            .iter()
            .map(|e| format!("{}: {}", e.field(), e.message().unwrap_or("validation failed")))
            .collect::<Vec<_>>()
            .join(", ");
        return Custom(
            rocket::http::Status::UnprocessableEntity,
            Json(ErrorResponse::new(err_msg)),
        );
    }

    // 默认错误信息
    Custom(
        rocket::http::Status::UnprocessableEntity,
        Json(ErrorResponse::new("Invalid request".to_string())),
    )
}

3. 调整控制器处理验证错误

修改控制器参数为Result<Validated<Json<Point>>, ValidationErrors>,直接捕获验证错误并转换为统一格式:

use rocket::validation::ValidationErrors;

#[post("/point", format = "application/json", data = "<data>")]
pub fn new_point(
    data: Result<Validated<Json<Point>>, ValidationErrors>,
) -> Result<Custom<Json<Point>>, Custom<Json<ErrorResponse>>> {
    let data = data.map_err(|err| {
        Custom(
            rocket::http::Status::UnprocessableEntity,
            Json(ErrorResponse::new(
                err.iter()
                    .map(|e| format!("{}: {}", e.field(), e.message().unwrap_or("validation failed")))
                    .collect::<Vec<_>>()
                    .join(", ")
            ))
        )
    })?;

    let point: Point = data.into_inner().into_inner();
    // 业务逻辑处理
    Ok(Custom(rocket::http::Status::Created, Json(point)))
}

4. 挂载捕获器到Rocket应用

启动Rocket时,将自定义的422捕获器注册到应用中:

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

内容的提问来源于stack exchange,提问作者Renato Cassino

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 10:30:58