Flutter中如何为http:^0.13.4包实现全局HTTP拦截器
结论
http: ^0.13.4 完全支持实现全局HTTP拦截器,不需要替换为其他网络依赖,通过自定义继承http.BaseClient的实现类即可统一覆盖两类核心需求,不需要在每个接口单独写重复逻辑。
实现原理
http库的所有请求最终都会走Client.send()方法,重写该方法即可在请求发起前、响应返回后插入统一处理逻辑:
- 统一附加Token逻辑:在请求转发前,复制原请求对象,将存储的有效Token写入请求头后再发起实际请求
- 401统一处理逻辑:拿到响应结果后先判断状态码,若为401则清空本地登录态、触发全局跳转登录页的流程,不需要每个接口单独判断状态码
完整代码示例
首先编写自定义的拦截客户端:
import 'package:http/http.dart' as http; import 'dart:async'; class GlobalHttpClient extends http.BaseClient { // 内部持有默认客户端做实际请求转发 final http.Client _innerClient = http.Client(); // 不需要携带Token的接口白名单,比如登录、注册、公开内容接口 final List<String> _noTokenPathList = ['/api/login', '/api/register', '/api/public']; // 401触发重登的防抖标记,避免多个并发请求同时401时重复触发跳转 bool _isRedirectingToLogin = false; @override Future<http.StreamedResponse> send(http.BaseRequest request) async { // 1. 请求前拦截:自动附加Token final needToken = !_noTokenPathList.any((path) => request.url.path.contains(path)); if (needToken) { // 从本地缓存/全局状态中获取存储的Token,替换为项目实际的Token获取逻辑 final String? token = await _getStoredToken(); if (token != null && token.isNotEmpty) { // 原请求是不可变对象,通过change方法复制新请求写入全局Header request = request.change(headers: { ...request.headers, 'Authorization': 'Bearer $token', // 可在此处追加其他全局Header,比如设备标识、App版本号等 }); } } // 发起实际网络请求 final response = await _innerClient.send(request); // 2. 响应后拦截:统一捕获401状态 if (response.statusCode == 401 && !_isRedirectingToLogin) { _isRedirectingToLogin = true; await _handle401Unauthorized(); _isRedirectingToLogin = false; } return response; } // 从本地存储取Token的示例方法,实际项目替换为SharedPreferences/状态管理取值逻辑 Future<String?> _getStoredToken() async { // 示例实现:final sp = await SharedPreferences.getInstance(); return sp.getString('token'); return 'your_local_stored_token'; } // 401统一处理逻辑 Future<void> _handle401Unauthorized() async { // 1. 清空本地存储的Token、用户信息等登录态数据 // 2. 清除全局路由栈,跳转至登录页,根据项目使用的路由框架调整即可 // 示例:Navigator.of(globalContext).pushNamedAndRemoveUntil('/login', (route) => false); } // App退出时调用释放资源 void dispose() { _innerClient.close(); } }
使用方式
全局初始化一次客户端单例,后续所有网络请求都通过该实例发起,不要使用http.get()/http.post()这类顶层方法(这类顶层方法会内部创建默认Client,不会走拦截逻辑):
// 在main函数runApp前初始化全局客户端 final globalHttp = GlobalHttpClient(); // 发起请求示例,get/post/put/delete等方法和原生http库用法完全一致 void fetchUserInfo() async { final response = await globalHttp.get(Uri.parse('https://your-api-domain.com/api/user/info')); // 后续业务逻辑正常编写即可 }
注意事项
- 务必保证整个App的网络请求都使用同一个
GlobalHttpClient单例,否则拦截逻辑不会生效 - 不需要携带Token的接口记得加入
_noTokenPathList白名单,避免登录类接口也带上无效Token触发异常 - 401处理必须加防抖标记,避免页面同时发起多个请求全部返回401时,重复触发多次登录页跳转
内容的提问来源于stack exchange,提问作者Arash Ghazi
相关产品推荐
相关产品推荐

