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

Flutter中DIO拦截器处理401时请求循环问题解决咨询

解决DIO拦截器处理401后请求重复发送的问题

你的问题根源在于拦截器对所有401请求都执行了重试逻辑,包括登录接口本身。当你用错误密码发起登录请求返回401时,拦截器触发refreshToken(用正确密码登录成功),随后会重新发送原来的错误登录请求,导致不必要的重复请求。

具体修复步骤:

1. 修改拦截器,排除登录接口的401重试

在onError拦截逻辑中,判断当前请求路径是否为登录接口(/api/tokens),如果是则直接返回错误,不执行重试:

onError: (DioException e, handler) async {
  // 仅对非登录接口的401执行token刷新和重试
  if (e.response?.statusCode == 401 && e.requestOptions.path != '/api/tokens') {
    String? newAccessToken = await refreshToken();
    if (newAccessToken != null && newAccessToken.isNotEmpty) {
      // 修正Authorization头格式,添加Bearer前缀
      e.requestOptions.headers['Authorization'] = 'Bearer $newAccessToken';
      // 重试请求
      return handler.resolve(await dio.fetch(e.requestOptions));
    }
  }
  // 登录接口的401或其他错误,直接返回
  return handler.next(e);
}

2. 修正请求头中Authorization的格式

原来的onRequest逻辑中,Authorization头缺少Bearer前缀,这可能是导致正常请求触发401的原因之一,需要补上:

onRequest: (options, handler) async {
  final bearerToken = await _storage.read(key: 'token');
  final tenantId = await _storage.read(key: 'tenant');
  if (options.path != '/api/tokens') {
    if (bearerToken != null) {
      // 正确格式:Bearer + 令牌
      options.headers['Authorization'] = 'Bearer $bearerToken';
    }
    options.headers['tenant'] = tenantId;
    return handler.next(options);
  }
  options.headers['tenant'] = tenantId;
  return handler.next(options);
}

3. 完善refreshToken的返回值处理

确保refreshToken在失败时返回null,避免错误信息被当作token使用:

Future<String?> refreshToken() async {
  final savedUser = await _storage.read(key: 'username');
  var credsdata = {"email": savedUser, "password": 'TEMP HARDCODED PASSWORD'};
  print('refreshToken() Called');
  print('refreshToken() Body: $credsdata');

  String baseUrl = await getBaseUrl();
  dio.options.baseUrl = baseUrl;

  try {
    Response response = await dio.post('/api/tokens', data: credsdata);
    Map<String, dynamic> data = response.data;

    var token = data['token'];
    var refreshToken = data['refreshToken'];

    await storageService.saveStorageData('token', token);
    await storageService.saveStorageData('refreshtoken', refreshToken);

    return token;
  } on DioException catch (e) {
    print('刷新token失败: ${e.response?.statusMessage}');
    return null;
  }
}

额外提示

实际生产环境中,不要用保存用户名密码的方式刷新token,应该使用后端提供的专用refreshToken接口,用已保存的refreshToken去换取新的accessToken,这是更安全的实现方式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 03:26:14