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

Flutter:如何获取API原始错误响应,规避DioError自动格式化?

问题

我想根据API返回的错误给用户展示不同提示,但现在遇到个问题:Dio会把所有409错误都拦截成通用的冲突错误,每次都显示一样的提示,比如日志输出:

I/flutter ( 4507): 🐛 12:06:44.286125 DEBUG    Global Loggy - DioError [bad response]: The request returned an invalid status code of 409.

但API实际返回的错误响应是:

{
    "title": "PALLET_NOT_CREATED",
    "status": 409,
    "detail": "Cannot create a new pallet, the preparer has already a pallet in status IN_PROGRESS",
    "timestamp": 1686218442060,
    "developerMessage": "SOME MESSAGE ERE",
    "code": "WMS_CLT_ERR_4",
    "errors": {}
}

有没有办法绕过Dio的默认错误拦截,直接获取并处理这些原始错误响应?

我的DioService代码片段:

...
Future<Response<JSON>> post({
    required String endpoint,
    JSON? data,
    Options? options,
    CancelToken? cancelToken,
  }) async {
    final response = await _dio.post<JSON>(
      baseUrl + endpoint,
      data: data,
      options: options,
      cancelToken: cancelToken ?? _cancelToken,
    );
    return response;
  }
...

注:这只是POST请求的实现,其他请求方法我已经处理好了

调用API的服务代码:

Future<PalletModel> createPallet(int batchId) async {
    try {
      final res = await _dioService.post(
        endpoint: CommonProperties.palletsByBatchId.replaceAll(
          ":batchId",
          batchId.toString(),
        ),
        data: {},
        options: Options(
          extra: {
            'requiresAuthToken': true,
          },
        ),
      );
      return PalletModel.fromJson(res.data!);
    } catch (e) {
      logDebug('error is:');
      logDebug(e);
      rethrow;
    }
  }
解决方案

有三种实用方法可以获取并处理API返回的原始错误响应:

方法一:在catch块中解析DioError的response属性

Dio抛出的DioError本身包含完整的响应数据,只需在catch分支中判断错误类型,就能提取原始错误信息:

Future<PalletModel> createPallet(int batchId) async {
    try {
      final res = await _dioService.post(
        endpoint: CommonProperties.palletsByBatchId.replaceAll(
          ":batchId",
          batchId.toString(),
        ),
        data: {},
        options: Options(
          extra: {
            'requiresAuthToken': true,
          },
        ),
      );
      return PalletModel.fromJson(res.data!);
    } on DioError catch (e) {
      // 检查是否存在响应数据
      if (e.response != null) {
        final errorData = e.response!.data as JSON;
        logDebug('原始错误响应: $errorData');
        // 根据API返回的detail字段生成用户提示,或者按title/code区分不同场景
        String userTip = errorData['detail'] ?? '创建托盘失败';
        // 这里可以直接抛出带提示的异常,或者调用UI组件展示提示
        throw Exception(userTip);
      }
      // 无响应的情况(如网络断开)
      logDebug('网络请求异常: ${e.message}');
      rethrow;
    } catch (e) {
      logDebug('其他错误: $e');
      rethrow;
    }
  }

方法二:配置Dio不拦截特定状态码

如果希望Dio不把409这类业务状态码当成错误抛出,可在初始化Dio时设置validateStatus规则,让Dio认为这些状态码是合法的,之后就能直接处理响应:

// Dio初始化代码
final _dio = Dio(BaseOptions(
  baseUrl: '你的基础API地址',
  // 自定义状态码验证逻辑,返回true则不触发错误抛出
  validateStatus: (status) {
    // 允许200-300的成功码,同时放行409状态码
    return (status != null && status >= 200 && status < 300) || status == 409;
  },
));

修改后,请求会正常返回结果,只需在业务代码中判断状态码即可处理错误:

Future<PalletModel> createPallet(int batchId) async {
    final res = await _dioService.post(
      endpoint: CommonProperties.palletsByBatchId.replaceAll(
        ":batchId",
        batchId.toString(),
      ),
      data: {},
      options: Options(
        extra: {
          'requiresAuthToken': true,
        },
      ),
    );
    if (res.statusCode == 409) {
      final errorData = res.data as JSON;
      String userTip = errorData['detail'] ?? '创建托盘失败';
      throw Exception(userTip);
    }
    return PalletModel.fromJson(res.data!);
  }

方法三:全局拦截器统一处理错误

如果多个接口都需要处理这类自定义错误,推荐添加Dio拦截器,统一解析错误响应并抛出自定义异常,减少重复代码:

// 添加全局错误拦截器
_dio.interceptors.add(InterceptorsWrapper(
  onError: (DioError e, ErrorInterceptorHandler handler) {
    if (e.response != null) {
      // 解析API返回的错误结构
      final errorData = e.response!.data as JSON;
      // 抛出自定义异常,携带业务错误信息
      handler.reject(CustomApiError(
        code: errorData['code'],
        message: errorData['detail'],
        title: errorData['title'],
      ));
    } else {
      // 无响应的情况,继续抛出原错误
      handler.next(e);
    }
  },
));

// 定义自定义异常类,方便业务层捕获
class CustomApiError implements Exception {
  final String? code;
  final String? message;
  final String? title;

  CustomApiError({this.code, this.message, this.title});

  @override
  String toString() => message ?? 'API请求错误';
}

之后在业务代码中,直接捕获自定义异常即可获取错误信息:

Future<PalletModel> createPallet(int batchId) async {
    try {
      final res = await _dioService.post(...);
      return PalletModel.fromJson(res.data!);
    } on CustomApiError catch (e) {
      logDebug('业务错误: ${e.message}');
      // 根据code或title展示不同提示,比如弹窗、Toast
      showToast(e.message!);
      rethrow;
    }
  }

内容的提问来源于stack exchange,提问作者DarkSide77

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 22:37:02