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
相关产品推荐
相关产品推荐

