Dio拦截器刷新Access Token:handler处理与请求重试疑问
Dio拦截器Token刷新问题修复方案
核心问题分析
你的代码能成功刷新Token,但重试请求时没有携带新的Access Token,且未处理Token刷新失败的异常场景,同时handler的异步处理逻辑存在遗漏,导致流程无法正常收尾。
修正后的完整代码
class AuthInterceptor extends QueuedInterceptor { final Dio dio; final AuthService authService; AuthInterceptor(this.dio, this.authService); @override void onRequest( RequestOptions options, RequestInterceptorHandler handler) async { final accessToken = await TokenRepository.getAccessToken(); if (accessToken != null) { logDebug('Access-Token: $accessToken'); options.headers['Authorization'] = 'Bearer $accessToken'; } handler.next(options); } @override void onError(DioError err, ErrorInterceptorHandler handler) async { // 仅处理401响应错误 if (err.type == DioErrorType.response && err.response?.statusCode == 401) { final currentAccessToken = await TokenRepository.getAccessToken(); // 如果当前没有Token,直接跳转登录页 if (currentAccessToken == null) { di<NavigationHelper>().navigateClearHistory(LoginPage()); return handler.reject(err); } try { final refreshToken = await TokenRepository.getRefreshToken(); if (refreshToken == null) { // 无可用刷新Token,跳转登录 di<NavigationHelper>().navigateClearHistory(LoginPage()); return handler.reject(err); } // 发起Token刷新请求 final refreshResult = await authService.refresh( RefreshRequest(currentAccessToken, refreshToken), ); final newAccessToken = refreshResult.accessToken; await TokenRepository.setAccessToken(newAccessToken); // 更新原请求的Authorization头为新Token err.requestOptions.headers['Authorization'] = 'Bearer $newAccessToken'; // 重试请求并返回结果 final retryResponse = await _retry(err.requestOptions); handler.resolve(retryResponse); } catch (refreshError) { // 刷新Token失败,跳转登录页 di<NavigationHelper>().navigateClearHistory(LoginPage()); // 将刷新失败的错误传递给上层 handler.reject(DioError(requestOptions: err.requestOptions, error: refreshError)); } } else { // 非401错误,正常传递错误信息 handler.next(err); } } Future<Response<dynamic>> _retry(RequestOptions requestOptions) async { return dio.request<dynamic>( requestOptions.path, data: requestOptions.data, queryParameters: requestOptions.queryParameters, options: Options( method: requestOptions.method, headers: requestOptions.headers, // 已携带新Token的请求头 ), ); } }
关键修正点
- 更新重试请求的Token:刷新Token后直接修改原请求
requestOptions的Authorization头,确保重试请求携带有效新Token。 - 异常场景处理:用
try-catch包裹Token刷新逻辑,覆盖刷新失败、刷新Token为空等异常情况,保证流程能正确收尾。 - handler调用完整性:每个分支都明确调用
handler.reject或handler.resolve,避免出现拦截器流程挂起的情况。 - 空值安全校验:增加
refreshToken的空值判断,避免空指针异常。
额外优化建议
- 增加并发请求锁:如果多个请求同时触发401,可通过全局
Completer控制同一时间仅发起一次刷新请求,避免重复调用刷新接口。 - 提前预判Token过期:在
onRequest阶段检查Token是否即将过期,主动发起刷新,减少401重试的场景。
内容的提问来源于stack exchange,提问作者Neigaard
相关产品推荐
相关产品推荐

