如何在Flutter中用Interceptor处理Header Token并解决Injectable循环依赖?
解决Flutter中Token拦截器与Injectable循环依赖问题
核心问题分析
你的代码形成了循环依赖链:TokenInterceptor → AuthRepository → ApiDatasource → Dio → TokenInterceptor,导致初始化时栈溢出。要解决这个问题,最干净的方式是拆分Token管理逻辑到独立类,打破循环链,同时保持Token拦截、存储的功能完整。
解决方案步骤
1. 拆分Token管理到独立类
创建TokenManager类,专门负责Token的读取、存储和缓存,让它成为无依赖的单一职责类:
const _accessTokenKey = 'access_token'; @lazySingleton class TokenManager { String? _cachedToken; /// 获取当前有效Token(优先用缓存,缓存为空则读本地存储) Future<String?> get accessToken async { if (_cachedToken != null) return _cachedToken; final token = await _readTokenFromStorage(); _cachedToken = token; return token; } /// 从本地安全存储读取Token Future<String?> _readTokenFromStorage() async { try { const secureStorage = FlutterSecureStorage(); return secureStorage.read(key: _accessTokenKey); } catch (_) { return null; } } /// 保存Token到缓存和本地存储 Future<bool> saveToken(String token) async { try { const secureStorage = FlutterSecureStorage(); await secureStorage.write(key: _accessTokenKey, value: token); _cachedToken = token; return true; } catch (_) { return false; } } /// 清除Token缓存和本地存储 Future<void> clearToken() async { _cachedToken = null; const secureStorage = FlutterSecureStorage(); await secureStorage.delete(key: _accessTokenKey); } }
2. 调整AuthRepository依赖
让AuthRepository依赖TokenManager而非自己处理Token,同时保留与ApiDatasource的交互:
@lazySingleton class AuthRepository { AuthRepository(this.apiDatasource, this.tokenManager); final ApiDatasource apiDatasource; final TokenManager tokenManager; Future<void> registerUser(RegisterModel registerModel) async { await apiDatasource.registerUser(registerModel); } /// 登录并保存返回的Token Future<void> loginUser(LoginModel loginModel) async { // 注意:需修改ApiDatasource的loginUser方法,返回包含Token的响应模型 final loginResponse = await apiDatasource.loginUser(loginModel); await tokenManager.saveToken(loginResponse.accessToken); } Future<void> resetPassword(ResetPasswordModel resetPasswordModel) async { await apiDatasource.resetPassword(resetPasswordModel); } Future<void> setNewPassword( String token, SetNewPasswordModel setNewPasswordModel) async { await apiDatasource.setNewPassword(token, setNewPasswordModel); } }
3. 修改TokenInterceptor依赖
让TokenInterceptor依赖TokenManager,直接从这里获取Token添加到请求头:
@injectable class TokenInterceptor extends Interceptor { TokenInterceptor({required this.tokenManager}); final TokenManager tokenManager; @override Future<void> onRequest( RequestOptions options, RequestInterceptorHandler handler) async { final token = await tokenManager.accessToken; if (token != null) { options.headers['Authorization'] = 'Bearer $token'; } handler.next(options); } }
4. 调整依赖注入配置
修改RegisterModule,确保Dio、ApiDatasource的依赖链正确,无循环:
@module abstract class RegisterModule { // 注入带拦截器的Dio实例 @lazySingleton Dio dio(TokenInterceptor tokenInterceptor) => Dio( BaseOptions(baseUrl: Const.baseUrl), )..interceptors.addAll( [ LogInterceptor(), tokenInterceptor, ], ); // 用上面的Dio生成ApiDatasource实例 @lazySingleton ApiDatasource apiDatasource(Dio dio) => ApiDatasource(dio); }
5. 更新ApiDatasource的登录接口
原loginUser方法返回void,需修改为返回包含Token的响应模型(比如LoginResponse):
part 'api_datasource.g.dart'; @lazySingleton @RestApi(baseUrl: Const.baseUrl) abstract class ApiDatasource { // ... 其他接口保持不变 @POST('/identity/Auth/userLogin') Future<LoginResponse> loginUser(@Body() LoginModel loginModel); }
为什么这样能解决循环依赖?
调整后的依赖链是单向无循环的:
TokenInterceptor→TokenManager(无依赖)AuthRepository→ApiDatasource+TokenManagerApiDatasource→DioDio→TokenInterceptor
所有类的依赖都指向无依赖的TokenManager,彻底打破了原来的循环。
内容的提问来源于stack exchange,提问作者Dominik
相关产品推荐
相关产品推荐

