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
相关产品推荐
相关产品推荐

