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

