axum错误处理

560871
2022/02/17 08:18:41

本章将简化官方的 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":"请检查你提交的数据"}';
    }
}

本章代码可以在代码库找到。