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

如何在Dio拦截器中处理401与403响应状态(含onError场景)

解决Flutter Dio拦截器无法处理401/403状态码的问题

问题根源

Dio默认会将4xx系列状态码(包括401、403)判定为请求错误,这类响应不会进入onResponse回调,而是直接触发onError。你当前把刷新逻辑写在onResponse中,自然无法被执行到。

解决方案

有两种可行的修改方式,根据你的需求选择:

方案1:修改Dio配置,让401/403进入onResponse

在初始化Dio实例时,通过validateStatus参数自定义错误判定规则,让401、403状态码的响应进入onResponse处理:

final dio = Dio(BaseOptions(
  baseUrl: baseUrl,
  validateStatus: (status) {
    // 允许2xx、401、403进入onResponse,其他状态码按错误处理
    return status != null && (status >= 200 && status < 300 || status == 401 || status == 403);
  },
));

这样你原有的onResponse中的刷新逻辑就能正常触发。

方案2:将刷新逻辑迁移到onError回调

既然Dio默认把4xx丢到onError,直接在该回调中处理认证错误更符合默认行为:

InterceptorsWrapper(
  onRequest: (options, handler) async {
    final token = await TokenStorage.getAccessToken();
    print("TOKEN SENT -> $token");
    if (token != null) {
      options.headers["Authorization"] = "Bearer $token";
    }
    handler.next(options);
  },
  onError: (DioException error, handler) async {
    // 仅处理401或403错误
    if (error.response?.statusCode == 401 || error.response?.statusCode == 403) {
      print("REFRESH TRIGGERED");
      final refreshToken = await TokenStorage.getRefreshToken();
      if (refreshToken == null) {
        return handler.next(error);
      }
      
      try {
        // 调用刷新接口,注意_refreshDio需是独立的无拦截器的Dio实例
        final refreshResponse = await _refreshDio.post(
          "$baseUrl/auth/refresh",
          data: {"refreshToken": refreshToken},
        );
        
        final newAccess = refreshResponse.data["accessToken"];
        final newRefresh = refreshResponse.data["refreshToken"];
        await TokenStorage.saveAccessToken(newAccess);
        await TokenStorage.saveRefreshToken(newRefresh);
        
        // 重新发起原请求
        final requestOptions = error.requestOptions;
        requestOptions.headers["Authorization"] = "Bearer $newAccess";
        final retryResponse = await dio.fetch(requestOptions);
        
        return handler.resolve(retryResponse);
      } catch (e) {
        // 刷新失败,清空令牌并返回原错误
        await TokenStorage.clear();
        return handler.next(error);
      }
    }
    // 其他错误直接传递
    handler.next(error);
  },
),

关键注意事项

  • 避免循环刷新:_refreshDio必须是一个独立的Dio实例,不要添加当前的认证拦截器,否则刷新请求若返回401会触发无限循环。
  • 防止重复刷新:如果多个请求同时触发401,建议添加一个全局锁(比如用Completer<bool>或布尔变量),确保同一时间只执行一次刷新逻辑,避免重复调用刷新接口。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.11 09:50:54