Skip to content

ApiServiceWrapper.init 静默覆盖注入者的 Dio 配置;DeviceAuthService 单例持有 wrapper 难生命周期管理 #42

Description

@yunkst

文件: lib/services/api_service_wrapper.dart:34, 53-98;lib/services/device/device_auth_service.dart:36-48

问题:

  1. ApiServiceWrapper 的接口设计有一个隐式契约 bug:构造函数 ApiServiceWrapper([Dio? dio]) 允许测试时注入自定义 Dio 实例(注释"通过依赖注入创建实例",test/unit/services/api_service_wrapper_test.dart:11-16 也基于此断言),但 init() 总会无条件地:

    • _dio.options.baseUrl = host 覆盖注入者的 baseUrl;
    • _dio.httpClientAdapter = IOHttpClientAdapter(...) 覆盖注入者设置的 adapter(86-98 行);
    • LogInterceptor 注入到 _dio 上(注入者的拦截器链因此被破坏)。
      结果:测试里注入的 mock adapter / 自定义 interceptor 全部被 init() 清掉,唯一"安全"用法是不调用 init(),但 init() 是真实业务路径必调。Provider 注入的 dio(ref.watch(apiServiceWrapperProvider) 实际不会注入 dio)没暴露点。
  2. DeviceAuthService 类似:构造 private + useWrapper(ApiServiceWrapper) 一次性注入,单例持有;同时 ApiServiceWrapper._apiOverride 字段(device_auth_service.dart:43-48)允许 override,但 register() 内部直接 _api.dio.post(...),override 只换 getHost/getToken/dispose 等高层方法,dio 配置完全没法换。两个单例的依赖管理是双轨并行,容易在 Provider 树重组后出现"主 ApiServiceWrapper 已 dispose,DeviceAuthService 还在用旧引用"的悬挂场景。

影响:

  • 测试困难(已经反映在 api_service_wrapper_test.dart 只有构造测试,缺少 init/调用测试);
  • 真实业务里"用户改 backend 配置"调用 setConfig → init(),但若 device_auth_service 注入过 useWrapper,DeviceAuthService 持有的旧 wrapper 永远不会感知 host 变化。

修复建议:

  • ApiServiceWrapper 改 immutable builder 模式:build({host, token}) 一次性生成新 wrapper,避免 init 副作用;
  • 或 init() 接受 {bool replaceAdapter, bool replaceInterceptors} 开关,让测试可以保留自定义 dio;
  • DeviceAuthService 改为 Ref-scoped(不持单例),依赖注入 ApiServiceWrapper,由 Riverpod 负责生命周期。

忽略指南:在 lib/services/api_service_wrapper.dart:34 添加注释 // cr-ignore <CR_IGNORE_IID_HASH>: <你的理由>,下次审查会自动关闭。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions