Flutter网络请求框架Dio源码分析以及封装(二)--Cookie管理分析

这篇具有很好参考价值的文章主要介绍了Flutter网络请求框架Dio源码分析以及封装(二)--Cookie管理分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

上一篇文章我们简单分析了一下Dio发出请求时的大致工作流程,这个只是Dio最基本的功能,而且我们还没有分析走到httpClientAdapter之后的内容。不过不用着急,这次我们先接着上一次的内容,看一下Dio当中Cookie管理的问题,因为之前在项目中碰到了这个问题,回过头来再从源码的角度去分析一下,算是复盘。

问题

之前碰到的问题是这样的:登录接口在登陆成功后会返回一个Cookie给客户端,包含在Response的
“set-cookie”Headers属性里。之后在客户端这边调用某些接口时后台需要对Cookie进行验证,所以需要把Cookie存到内存甚至硬盘里,在请求头里面带上传给后台,否则会后台会报请求失败。
由于以前的项目基本上是采取token的方式去做接口鉴权,所以这种cookie持久化的方法对我来说有点陌生,不过最后还是能想到利用拦截器去做这个事情。Dio允许我们自定义拦截器,对请求与返回的参数进行调整,我们在onResponse回调方法里,从Response里面的headers拿到Cookie,再在onRequest里面加到RequestOptions里。原理很简单,但是之前自己实现的时候很多细节没有处理好,造成了代码的冗余以及不够健壮。
实际上,Dio提供了配套的Plugin:dio_cookie_manager 来帮助我们进行Cookie的管理,使用起来也非常的方便。那么这次我们就一起来看一下它是怎么处理这些问题的。

如何使用

首先我们需要导入cookie_manager的库,基于项目中用到的2.0.0版本
它又间接依赖cookie_jar3.0.0这个版本

dependencies:
  dio_cookie_manager: ^2.0.0
  cookie_jar: ^3.0.0
  final dio = Dio();
  final cookieJar = CookieJar();
  dio.interceptors.add(CookieManager(cookieJar));

如示例中所示,我们需要构造一个CookieJar的实例,然后将它传入CookieManager的构造方法中,最后添加到Dio的拦截器列表中。

CookieJar

我们先来看看CookieJar这个类:

/// CookieJar is a cookie manager for http requests。
abstract class CookieJar {
  factory CookieJar({bool ignoreExpires = false}) {
    return DefaultCookieJar(ignoreExpires: ignoreExpires);
  }
  /// Save the cookies for specified uri.
  Future<void> saveFromResponse(Uri uri, List<Cookie> cookies);
  /// Load the cookies for specified uri.
  Future<List<Cookie>> loadForRequest(Uri uri);
  Future<void> deleteAll();
  Future<void> delete(Uri uri, [bool withDomainSharedCookie = false]);
  final bool ignoreExpires = false;
}

CookieJar是抽象类,最终还是调用了DefaultCookieJar这个类的构造方法

/// [DefaultCookieJar] is a default cookie manager which implements the standard
/// cookie policy declared in RFC. [DefaultCookieJar] saves the cookies in RAM, so if the application
/// exit, all cookies will be cleared.
class DefaultCookieJar implements CookieJar {
  /// [ignoreExpires]: save/load even cookies that have expired.
  DefaultCookieJar({this.ignoreExpires = false});
  ...
 }

DefaultCookieJar是CookieJar的一种默认实现,按照http的格式将cookie从Request与Response中解析出来,并保存在Ram中。

CookieManager

接着来看看CookieManager这个类:

  /// Don't use this class in Browser environment
class CookieManager extends Interceptor {
  final CookieJar cookieJar;
  CookieManager(this.cookieJar);
```、
```dart
  
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    cookieJar.loadForRequest(options.uri).then((cookies) {
      var cookie = getCookies(cookies);
      if (cookie.isNotEmpty) {
        options.headers[HttpHeaders.cookieHeader] = cookie;
      }
      handler.next(options);
    }).catchError((e, stackTrace) {
      var err = DioError(requestOptions: options, error: e);
      err.stackTrace = stackTrace;
      handler.reject(err, true);
    });
  }
  static String getCookies(List<Cookie> cookies) {
    return cookies.map((cookie) => '${cookie.name}=${cookie.value}').join('; ');
  }

构造的cookieJar传进来后,在onRequest方法里面调用loadForRequest方法获取格式化后的Cookie,将他们转化为请求需要的格式然后设置在请求头里,之后继续执行下面的拦截器的逻辑。

  
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    _saveCookies(response)
        .then((_) => handler.next(response))
        .catchError((e, stackTrace) {
      var err = DioError(requestOptions: response.requestOptions, error: e);
      err.stackTrace = stackTrace;
      handler.reject(err, true);
    });
  }
  Future<void> _saveCookies(Response response) async {
    var cookies = response.headers[HttpHeaders.setCookieHeader];
    if (cookies != null) {
      await cookieJar.saveFromResponse(
        response.requestOptions.uri,
        cookies.map((str) => Cookie.fromSetCookieValue(str)).toList(),
      );
    }
  }

onResponse也类似,拿到Cookie之后,调用saveFromResponse格式化保存起来,之后继续执行下面的拦截器的逻辑。
当然,正常的顺序应该是先在onResponse拿到Cookie之后再在onResponse使用。

PersistCookieJar

如果想对Cookie进行持久化处理,可以考虑使用PersistCookieJar:

    final Directory appDocDir = await getApplicationDocumentsDirectory();
    final String appDocPath = appDocDir.path;
    final jar = PersistCookieJar(
      ignoreExpires: true,
      storage: FileStorage(appDocPath + "/.cookies/"),
    );
    dio!.interceptors.add(CookieManager(jar));
/// [PersistCookieJar] is a cookie manager which implements the standard
/// cookie policy declared in RFC. [PersistCookieJar]  persists the cookies in files,
/// so if the application exit, the cookies always exist unless call [delete] explicitly.
class PersistCookieJar extends DefaultCookieJar {
  ///
  /// [persistSession]: Whether persisting the cookies that without
  /// "expires" or "max-age" attribute;
  /// If false, the session cookies will be discarded;
  /// otherwise, the session cookies will be persisted.
  ///
  /// [ignoreExpires]: save/load even cookies that have expired.
  ///
  /// [storage]: Defaults to FileStorage

  PersistCookieJar(
      {this.persistSession = true,
      bool ignoreExpires = false,
      Storage? storage})
      : super(ignoreExpires: ignoreExpires) {
    this.storage = storage ?? FileStorage();
  }

为了实现持久化,引入了一个Storage类,默认实现是FileStorage,文件存储,也可以自己实现其他方式。

  
  Future<List<Cookie>> loadForRequest(Uri uri) async {
    await _checkInitialized();
    await _load(uri);
    return super.loadForRequest(uri);
  }

PersistCookieJar最终也调用了DefaultCookieJar的loadForRequest方法,但是在那之前还执行了两个方法,我们一个一个看:

  Future<void> _checkInitialized({bool force = false}) async {
    if (force || !_initialized) {
      await storage.init(persistSession, ignoreExpires);
      // Load domain cookies
      var str = await storage.read(DomainsKey);
      ...
      }

执行了storage的init与read方法

  
  Future<void> init(bool persistSession, bool ignoreExpires) async {
    _curDir = dir ?? './.cookies/';
    if (!_curDir!.endsWith('/')) {
      _curDir = _curDir! + '/';
    }
    _curDir = _curDir! + 'ie${ignoreExpires ? 1 : 0}_ps${persistSession ? 1 : 0}/';
    await _makeCookieDir();
  }
  Future<void> _makeCookieDir() async {
    final directory = Directory(_curDir!);
    if (!directory.existsSync()) {
      await directory.create(recursive: true);
    }
  }

init方法主要是检查传入的保存目录是否存在,不存在就创建。
read方法与load方法分别将文件中数据读取并存入内存中,domainCookies与hostCookies

  Future<void> _load(Uri uri) async {
    final host = uri.host;
    if (_hostSet.contains(host) && hostCookies[host] == null) {
      var str = await storage.read(host);
  
  Future<void> saveFromResponse(Uri uri, List<Cookie> cookies) async {
    await _checkInitialized();
    if (cookies.isNotEmpty) {
      await super.saveFromResponse(uri, cookies);
      if (cookies.every((Cookie e) => e.domain == null)) {
        await _save(uri);
      } else {
        await _save(uri, true);
      }
    }
  }

先检查有无保存目录,再看是否传入cookies,若有,直接调用DefaultCookieJar的saveFromResponse,然后调用_save方法:

  Future<void> _save(Uri uri, [bool withDomainSharedCookie = false]) async {
    final host = uri.host;
    if (!_hostSet.contains(host)) {
      _hostSet.add(host);
      await storage.write(IndexKey, json.encode(_hostSet.toList()));
    }
    final cookies = hostCookies[host];
    if (cookies != null) {
      await storage.write(host, json.encode(_filter(cookies)));
    }
    if (withDomainSharedCookie) {
      var filterDomainCookies =
          domainCookies.map((key, value) => MapEntry(key, _filter(value)));
      await storage.write(DomainsKey, json.encode(filterDomainCookies));
    }
  }

根据Cookie层级,依次写入到文件中。

总结

CookieManager与CookieJar的整个工作流程就基本分析完了,可以看到,整个流程非常清晰,代码也很简洁,健壮性也很好,这次源码学习让我对代码编写的理解有深入了一些。那么,下次我们一起来看看如何封装Dio,让我们平时使用起来更便捷。文章来源地址https://www.toymoban.com/news/detail-481071.html

到了这里,关于Flutter网络请求框架Dio源码分析以及封装(二)--Cookie管理分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Flutter之Dio封装+实例(自己梳理)

    https://github.com/cfug/dio/blob/main/dio/README-ZH.md  手动添加到pubspec.yaml: 在终端使用以下命令: dio 是一个强大的 HTTP 网络请求库,支持全局配置、Restful API、FormData、拦截器、 请求取消、Cookie 管理、文件上传/下载、超时、自定义适配器、转换器等。 单例模式详见:Flutter之单例模式

    2024年02月08日
    浏览(89)
  • Flutter 极简 Dio 组件二次封装文档

    本文档介绍了如何通过二次封装 Flutter Dio 组件来简化网络请求的过程。通过封装,我们可以提高代码复用性,简化调用方式,并添加一些常用的功能,使网络请求更加易于管理和维护。 首先,确保你的 Flutter 项目已经添加了 Dio 的依赖。在项目的 pubspec.yaml 文件中,添加以下

    2024年02月11日
    浏览(54)
  • flutter开发实战-请求dio设置Cookie

    flutter开发实战-请求dio设置Cookie 在最近开发中碰到了需要websocket长链接收到响应的auth,在之后的请求中需要将其设置为cookie中。 如Cookie:auth=DHSfQQSAXf89xZqJTLdEDVI2hwzc7p2lUmSNNdUSlgW2MyfQIN+pYr7jUbkX/; 设置cookie用到了dio_cookie_manager组件 在pubspec.yaml引入dio_cookie_manager 2.1 使用CookieJar Cookie

    2024年02月15日
    浏览(57)
  • Flutter dio Http请求之Cookie管理

    在应用开发过程中,我们进行Http通讯时会使用 Cookie 进行验证,今天我们就着重讲解Flutter 网络请求插件 dio 的 cookie 使用。 首先,我们要进行插件引用 这里为什么要使用 path_provider 这个插件呢,下面在 cookie 的储存时会做介绍。 引用完,我们执行以下命令 dio 的使用网上有很

    2024年02月20日
    浏览(35)
  • flutter dio使用proxyman抓包进行网络调试

    证书 wifi 手机和电脑连上同一个wifi,并且手机wifi使用代理,代理地址为电脑的ip和proxyman设置的监听端口 代码 使用方式 proxyIP 为电脑ip

    2024年02月03日
    浏览(44)
  • Kafka源码分析(四) - Server端-请求处理框架

    Kafka源码分析-目录 先给一张概览图: 服务端请求处理过程涉及到两个模块: kafka.network 和 kafka.server 。 该包是kafka底层模块,提供了服务端NIO通信能力基础。 有4个核心类:SocketServer、Acceptor、Processor、RequestChannel。各自角色如下: SocketServer:服务端的抽象,是服务端通信的

    2024年04月28日
    浏览(26)
  • MCAL配置之Port和Dio模块及IO抽象层源码分析

    Port及Dio模块是独立于MCU时钟的两个模块,因此最容易上手,不过在配置前需要充分了解硬件原理图以及硬件手册中的接口相关内容。 在Port界面中,分为General及PortContainer两个配置选项卡。其中Genaral选项卡中可配置是否使用DET监控(DevErrorDetect)、是否使能SafetyCheck(PortSafe

    2024年02月16日
    浏览(36)
  • Flutter 使用 dio 遇到的问题合集

    泪流满面啊,,,,, 1. postHttpLogin-异常-----DioException [bad response]: The request returned an invalid status code of 500. 2. post请求失败 DioException [bad response]: The request returned an invalid status code of 415. 这个问题有些离谱,415,415都说是请求头的问题,但结果却不是

    2024年02月03日
    浏览(46)
  • 【Flutter】Dio 强大的Dart/Flutter HTTP客户端

    Dio是一个强大的Dart/Flutter HTTP客户端,支持全局配置、拦截器、FormData、请求取消、文件上传/下载、超时等功能。 首先,

    2024年02月11日
    浏览(46)
  • flutter开发实战-dio文件下载实现

    flutter开发实战-dio文件下载实现 在开发中,需要下载文件,这里使用的是dio dio 是一个强大的 Dart HTTP 请求库,支持全局配置、Restful API、FormData、拦截器、 请求取消、Cookie 管理、文件上传/下载、超时以及自定义适配器等。 在工程中pubspec.yaml引入dio 我们对dio进行封装 文件下

    2024年02月11日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包