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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 14:22:55