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

Flutter中Dio调用API时200正常但403请求崩溃,求错误处理方案

问题根源

Dio 默认会把非2xx范围的HTTP状态码(比如403)当作异常抛出,所以你的403响应根本不会走到if(response.statusCode == 403)的判断逻辑里,而是直接进入on DioException catch (e)块。另外你每次调用接口都重复添加CookieManager拦截器,这会导致拦截器重复注册,可能引发额外的崩溃问题。

修复步骤

1. 全局配置Dio拦截器(避免重复添加)

把CookieManager拦截器的初始化放到Dio实例的全局配置里,不要每次调用接口都添加。比如在你的网络初始化类里:

// 全局初始化Dio和CookieJar
final cookieJar = CookieJar();
final dio = Dio()
  ..interceptors.add(CookieManager(cookieJar));

2. 处理403状态码(两种可选方法)

方法一:让Dio允许403状态码不抛出异常

在请求的Options里设置validateStatus,指定哪些状态码视为有效响应:

final response = await dio.post(
  '$baseUrl/durian/scan-durian',
  options: Options(
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer $accessToken',
    },
    // 允许200和403都视为有效响应,不抛出异常
    validateStatus: (status) => status == 200 || status == 403,
  ),
  data: {
    'durian_tag': durianTag,
    'durian_grade': durianGrade,
  },
);

这样403响应会进入后续的if判断,你的原有状态码处理逻辑就能正常执行。

方法二:在DioException中捕获并处理403

如果不想修改validateStatus,可以在异常块里解析响应:

} on DioException catch (e) {
  print('Dio异常: ${e.message}');
  // 检查是否有响应且状态码是403
  if (e.response != null && e.response!.statusCode == 403) {
    print('403状态码捕获');
    return {'success': true, 'message': 'pass'};
  }
  // 处理其他Dio异常
  return {'success': false, 'message': '请求异常: ${e.message}'};
} catch (e) {
  print('其他异常: $e');
  return {'success': false, 'message': '未知错误'};
}

完整修改后的代码示例

推荐结合全局Dio配置+方法一的实现:

// 全局Dio配置(放在单独的网络管理类或全局初始化处)
final cookieJar = CookieJar();
final dio = Dio()
  ..interceptors.add(CookieManager(cookieJar));

Future<dynamic> scanDurian(String durianTag, String durianGrade) async {
  // Retrieve tokens from SharedPreferences
  final accessToken = await AuthService.getAccessToken();
  final refreshToken = await AuthService.getRefreshToken();
  // Save cookies to the cookie jar
  List<Cookie> cookies = [
    Cookie('accessToken', accessToken!),
    Cookie('refreshToken', refreshToken!),
  ];
  cookieJar.saveFromResponse(Uri.parse(baseUrl), cookies);

  try {
    final response = await dio.post(
      '$baseUrl/durian/scan-durian',
      options: Options(
        headers: {
          'Content-Type': 'application/json',
          'Authorization': 'Bearer $accessToken',
        },
        // 允许200和403作为有效响应
        validateStatus: (status) => status == 200 || status == 403,
      ),
      data: {
        'durian_tag': durianTag,
        'durian_grade': durianGrade,
      },
    );

    if (response.statusCode == 200) {
      print(response.statusCode);
      final responseData = response.data;
      return {'success': true, 'data': responseData};
    } else if (response.statusCode == 403) {
      print(response.statusCode);
      return {'success': true, 'message': 'pass'};
    } else {
      print(response.statusCode);
      return {'success': false, 'message': 'Failed to scan durian'};
    }

  } on DioException catch (e) {
    print('Dio请求异常: $e');
    return {'success': false, 'message': '请求失败: ${e.message}'};
  } catch (e) {
    print('未知异常: ${e.toString()}');
    return {'success': false, 'message': '未知错误'};
  }
}

额外说明

  • 重复添加拦截器会导致Dio实例的拦截器链越来越长,可能引发请求重复处理、内存泄漏或逻辑混乱,所以一定要全局初始化一次。
  • 物理设备和NGROK的使用不影响这个问题的修复,核心是Dio的异常处理规则。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 15:45:13