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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 05:09:20