将Riverpod的Ref传入RestClient服务是否为最佳实践?
问题描述
我有Xamarin.Forms开发经验,目前正在学习Flutter,尝试复现过往项目中的常见场景。我编写了一个基于Dio实现的RestClient服务类,用来处理所有底层Rest请求。当请求返回401 - Unauthorized状态时,我希望触发应用登出逻辑(此处已做简化处理)。
我使用Riverpod管理业务逻辑,但RestClient内部没有Ref来与providers交互,因此我将Ref传入RestClient的构造函数,以此更新authenticationProvider的状态。目前该方案运行正常,但我担心这种方式会造成组件间过度耦合,不确定这是否是合理的做法。
现有代码
rest_client.dart
class RestClient { final Dio dio; final Ref ref; RestClient._({required this.dio, required this.ref}); factory RestClient({required Ref ref}) { var dio = Dio(BaseOptions( baseUrl: "https://myurl.com/", connectTimeout: const Duration(seconds: 10), receiveTimeout: const Duration(seconds: 10), )); dio.interceptors.add(InterceptorsWrapper( onError: (e, handler) { if (e.response != null && e.response!.statusCode == 401) { ref.read(authenticationProvider.notifier).invalidateToken(); } }, )); return RestClient._(dio: dio, ref: ref); } /**** ... *****/ /**** Methods to call api endpoints *****/ /**** ... *****/ }
authentication_provider.dart
class AutenthicationNotifier extends StateNotifier<String?> { // *** Constructor and other methods *** void invalidateToken() { // body } } final authenticationProvider = StateNotifierProvider<AutenthicationNotifier, String?>((ref) { return AutenthicationNotifier(); });
RestClient Provider
final restClientProvider = Provider((ref) => RestClient(ref: ref));
分析与改进建议
当前方案的问题
你担心的耦合问题确实存在:
RestClient直接依赖Riverpod的Ref和具体的authenticationProvider,导致它无法脱离Riverpod复用,一旦更换状态管理库或重构authenticationProvider,RestClient必须跟着修改。- 单元测试难度提升,需要模拟
Ref和authenticationProvider的行为才能测试RestClient的网络逻辑。
推荐改进方案:回调函数解耦
让RestClient只专注网络请求逻辑,把401的处理逻辑通过回调函数的方式交给外部,彻底解除和Riverpod的耦合:
修改后的rest_client.dart
class RestClient { final Dio dio; final void Function() onUnauthorized; RestClient._({required this.dio, required this.onUnauthorized}); factory RestClient({required void Function() onUnauthorized}) { var dio = Dio(BaseOptions( baseUrl: "https://myurl.com/", connectTimeout: const Duration(seconds: 10), receiveTimeout: const Duration(seconds: 10), )); dio.interceptors.add(InterceptorsWrapper( onError: (e, handler) { if (e.response != null && e.response!.statusCode == 401) { onUnauthorized(); } // 记得继续传递错误,避免拦截器阻塞错误处理 handler.next(e); }, )); return RestClient._(dio: dio, onUnauthorized: onUnauthorized); } // ... API请求方法 }
修改后的RestClient Provider
final restClientProvider = Provider((ref) => RestClient( onUnauthorized: () => ref.read(authenticationProvider.notifier).invalidateToken(), ));
其他可选方案
- 事件总线:通过事件总线发布
Unauthorized事件,由上层业务逻辑监听并处理登出。但这种方式会让代码流向变得不直观,适合更复杂的跨组件通信场景。 - 外部添加拦截器:把Dio拦截器的逻辑移到Provider中,
RestClient只负责创建Dio实例和提供API方法。这种方式会拆分RestClient的职责,适合需要灵活配置拦截器的场景。
总结
当前方案功能正常但耦合性较高,推荐使用回调函数的方式解耦,让RestClient保持单一职责,专注于网络请求逻辑,业务相关的登出操作交给上层状态管理处理,既提升了代码的可维护性,也方便后续的单元测试和复用。
内容的提问来源于stack exchange,提问作者camionkraken
相关产品推荐
相关产品推荐

