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

如何在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 + TokenManager
  • ApiDatasource → Dio
  • Dio → TokenInterceptor

所有类的依赖都指向无依赖的TokenManager,彻底打破了原来的循环。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 17:45:56