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

Flutter使用http包发起请求时如何实现过期token的自动刷新重试

基于http包实现401自动刷新token并重发原请求

前置修正

你当前写的代码存在两个基础问题,必须先调整才能正常运行:

  • http.post 本身是异步返回Future的方法,无法同步赋值给response变量,需要给方法加上async修饰,并用await等待请求返回
  • 401分支如果直接无限递归调用getUser(),一旦刷新token失败会直接导致栈溢出崩溃,需要加状态判断避免无意义循环

完整可运行实现

import 'dart:convert';
import 'dart:async';
import 'package:http/http.dart' as http;

// 全局状态存储
String? currentToken;
String? refreshToken;
// 刷新状态锁:避免多接口同时触发401时重复调用刷新接口
bool _isRefreshing = false;
// 等待token刷新完成后需要重发的请求队列
final _pendingRequests = <Function>[];

const String BASE_URL = "你的接口域名";

// 统一的刷新token请求逻辑
Future<void> doRefreshToken() async {
  final refreshResponse = await http.post(
    Uri.https(BASE_URL, '/api/refresh-token'),
    headers: {'Refresh-Token': refreshToken},
  );
  if (refreshResponse.statusCode == 200) {
    final resData = jsonDecode(refreshResponse.body);
    currentToken = resData['access_token'];
    refreshToken = resData['refresh_token'];
    return;
  }
  // 刷新失败直接清空登录态
  currentToken = null;
  refreshToken = null;
  throw Exception("登录已过期,请重新登录");
}

// 修改后的getUser方法
Future<User> getUser() async {
  final response = await http.post(
    Uri.https(BASE_URL, '/api/user'),
    headers: {'Authorization': 'Bearer $currentToken'},
  );

  if (response.statusCode == 200) {
    return User.fromJson(jsonDecode(response.body)['user']);
  }

  if (response.statusCode == 401) {
    // 若当前没有正在执行的刷新流程,就启动刷新
    if (!_isRefreshing) {
      _isRefreshing = true;
      try {
        await doRefreshToken();
        // 刷新完成后,依次执行所有等待队列里的重发请求
        for (final pendingTask in _pendingRequests) {
          pendingTask();
        }
        _pendingRequests.clear();
      } catch (e) {
        // 刷新失败直接清空队列,抛出异常
        _pendingRequests.clear();
        rethrow;
      } finally {
        _isRefreshing = false;
      }
    }

    // 当前请求加入等待队列,等刷新完成后自动重发
    final completer = Completer<User>();
    _pendingRequests.add(() async {
      final retryResponse = await http.post(
        Uri.https(BASE_URL, '/api/user'),
        headers: {'Authorization': 'Bearer $currentToken'},
      );
      if (retryResponse.statusCode == 200) {
        completer.complete(User.fromJson(jsonDecode(retryResponse.body)['user']));
      } else {
        completer.completeError(Exception("请求重发失败"));
      }
    });
    return completer.future;
  }

  throw Exception("请求异常,状态码:${response.statusCode}");
}

逻辑说明

  • 用_isRefreshing锁保证同一时间只会执行一次刷新token请求,避免多个接口同时收到401时重复打刷新接口,触发接口频控或者导致新token被旧刷新请求覆盖的问题
  • 用队列存储等待重发的请求,等token刷新完成后统一执行,不需要每个接口单独写重复的重试逻辑
  • 重发请求时直接使用最新的currentToken,不再使用过期的旧token

新手注意点

  • 刷新token的接口如果返回401,直接判定为登录失效,不要再走刷新逻辑,避免死循环
  • 如果项目里有多个接口请求,可以把这套401处理逻辑抽成通用的请求封装方法,不用在每个接口里重复写判断分支
  • 不要为了图省事在401分支里直接写await getUser()的递归逻辑,一旦刷新流程出问题会直接造成内存泄漏

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.24 15:45:10