如何在Axum处理器中正确处理reqwest调用外部API的错误?
解决Axum处理器中reqwest调用外部API的错误处理问题
Axum处理器要求返回类型必须实现IntoResponse,直接用reqwest的?会因为reqwest的Error未实现该 trait 导致编译报错;而如果不用?,reqwest的Ok(Response)会包含所有HTTP状态(包括4xx/5xx),导致你的API不管外部调用结果如何都返回200,这显然不符合需求。
下面是具体的重构方案:
1. 定义自定义错误类型
先定义一个能被Axum识别的错误类型,实现IntoResponse,同时把reqwest的底层错误和外部API的状态码错误都包装进去:
use axum::{ http::StatusCode, response::{IntoResponse, Json}, }; use reqwest::Error as ReqwestError; use serde::Serialize; // 自定义错误枚举,覆盖两种核心错误场景 #[derive(Debug)] enum ApiError { Reqwest(ReqwestError), ExternalApiError(StatusCode, String), } // 实现IntoResponse,让Axum能把错误转换成标准HTTP响应 impl IntoResponse for ApiError { fn into_response(self) -> axum::response::Response { match self { ApiError::Reqwest(e) => ( StatusCode::INTERNAL_SERVER_ERROR, Json(ErrorResponse { message: format!("请求外部API失败: {}", e), }), ).into_response(), ApiError::ExternalApiError(status, msg) => ( status, Json(ErrorResponse { message: msg }), ).into_response(), } } } // 统一的错误响应JSON结构 #[derive(Serialize)] struct ErrorResponse { message: String, } // 实现From<ReqwestError>,让reqwest错误可以自动转换成ApiError,支持?运算符 impl From<ReqwestError> for ApiError { fn from(e: ReqwestError) -> Self { ApiError::Reqwest(e) } }
2. 重构Axum处理器
在处理器里,发送请求后先检查状态码,非2xx则返回自定义错误;同时用?处理reqwest的网络、解析等底层错误:
use axum::extract::{Query, Extension}; use reqwest::Client; use serde::Deserialize; // 你的API接收的请求参数 #[derive(Deserialize)] struct UserRequest { user_id: String, } // 外部API返回的响应结构 #[derive(Deserialize)] struct ExternalUserResponse { id: String, name: String, } // 返回给客户端的响应结构 #[derive(Serialize)] struct UserResponse { user_id: String, user_name: String, } async fn get_user_handler( Query(params): Query<UserRequest>, Extension(client): Extension<Client>, ) -> Result<Json<UserResponse>, ApiError> { // 调用外部API,用?自动处理reqwest底层错误 let response = client .get(format!("https://external-api.com/users/{}", params.user_id)) .send() .await?; // 检查外部API状态码,非2xx则返回对应错误 if !response.status().is_success() { let error_msg = response.text().await.unwrap_or_else(|_| "未知错误".to_string()); return Err(ApiError::ExternalApiError( response.status(), format!("外部API返回错误: {}", error_msg), )); } // 解析外部API响应,用?处理解析错误 let external_user = response.json::<ExternalUserResponse>().await?; // 构造并返回正常响应 Ok(Json(UserResponse { user_id: external_user.id, user_name: external_user.name, })) }
3. 核心逻辑说明
- 自定义错误
ApiError覆盖了两类错误:reqwest的底层异常(比如网络超时、DNS失败)和外部API的业务错误(400参数错误、404资源不存在等)。 - 实现
From<ReqwestError>后,?运算符可以直接将reqwest错误转换成ApiError,完全符合Axum的返回类型要求,不会编译报错。 - 显式检查
response.status().is_success(),把非2xx响应转为对应状态码的错误,确保你的API会返回和外部API一致的状态码,同时带上可读的错误信息。 - 所有错误都会被序列化为JSON格式响应,符合标准API设计规范。
内容的提问来源于stack exchange,提问作者deepsun
相关产品推荐
相关产品推荐

