最近TG 群很多朋友讨论到,在接收 JSON 数据时,如何对用户错误提交的数据回复自定义的信息。在axum 官方示例中,有一个通过自定义 extractor 的示例,同时axum 文档里有一篇专门介绍的错误处理的文档。
本章将简化官方的 extractor 以及结合 nginx 来实现。
自定义 extractor
extractor 的定义
axum 官方已经提供了很多 extractor,其中包括 axum::Json
。现在,我们要实现自己的 Json extractor——当然,为了避免混乱,建议取别的名字,比如MyJson
等。
// src/extract.rs
// 定义自己的Json extract
pub struct Json<T>(pub T);
// 实现FromRequest
#[async_trait]
impl<B, T> FromRequest<B> for Json<T>
where
B: axum::body::HttpBody + Send,
T: DeserializeOwned,
B::Data: Send,
B::Error: Into<BoxError>,
{
type Rejection = (StatusCode, axum::Json<serde_json::Value>);
async fn from_request(req: &mut RequestParts<B>) -> Result<Self, Self::Rejection> {
match axum::Json::<T>::from_request(req).await {
Ok(value) => Ok(Self(value.0)),
Err(err) => {
let body: Cow<'_, str> = match err {
JsonRejection::InvalidJsonBody(err) => {
format!("缺少所需的字段:{}", err).into()
}
JsonRejection::MissingJsonContentType(err) => {
format!("请使用JSON请求:{}", err).into()
}
err => format!("发生错误:{}", err).into(),
};
Err((
StatusCode::BAD_REQUEST,
axum::Json(json!({ "error": body })),
))
}
}
}
}
在 handler 中使用
// src/handler.rs
use crate::extract::Json;
use serde::Deserialize;
#[derive(Deserialize, Debug)]
pub struct User {
pub username: String,
pub email: String,
}
// 使用的是我们自定义的Json
pub async fn login(Json(user): Json<User>) {
dbg!(&user);
}
nginx
我们可以使用 nginx 来反代 axum 应用,并根据不同的 HTTP 响应码来定义不同的 JSON 响应:
server {
listen 443 ssl;
server_name axum.rs;
error_page 400 401 403 404 405 500 501 502 503 504 /msg.json;
location /msg.json {
internal;
default_type application/json;
charset utf-8;
return 400 '{"error":"请检查你提交的数据"}';
}
}