望牛墩仿做网站,万维网网站,什么是网站前置审批,答题网站怎么做Flutter网络请求框架Dio源码分析以及封装--Cookie管理分析 前言问题如何使用CookieJarCookieManagerPersistCookieJar总结 前言
上一篇文章我们简单分析了一下Dio发出请求时的大致工作流程#xff0c;这个只是Dio最基本的功能#xff0c;而且我们还没有分析走到httpClientA… Flutter网络请求框架Dio源码分析以及封装--Cookie管理分析 前言问题如何使用CookieJarCookieManagerPersistCookieJar总结 前言
上一篇文章我们简单分析了一下Dio发出请求时的大致工作流程这个只是Dio最基本的功能而且我们还没有分析走到httpClientAdapter之后的内容。不过不用着急这次我们先接着上一次的内容看一下Dio当中Cookie管理的问题因为之前在项目中碰到了这个问题回过头来再从源码的角度去分析一下算是复盘。
问题
之前碰到的问题是这样的登录接口在登陆成功后会返回一个Cookie给客户端包含在Response的 “set-cookie”Headers属性里。之后在客户端这边调用某些接口时后台需要对Cookie进行验证所以需要把Cookie存到内存甚至硬盘里在请求头里面带上传给后台否则会后台会报请求失败。 由于以前的项目基本上是采取token的方式去做接口鉴权所以这种cookie持久化的方法对我来说有点陌生不过最后还是能想到利用拦截器去做这个事情。Dio允许我们自定义拦截器对请求与返回的参数进行调整我们在onResponse回调方法里从Response里面的headers拿到Cookie再在onRequest里面加到RequestOptions里。原理很简单但是之前自己实现的时候很多细节没有处理好造成了代码的冗余以及不够健壮。 实际上Dio提供了配套的Plugindio_cookie_manager 来帮助我们进行Cookie的管理使用起来也非常的方便。那么这次我们就一起来看一下它是怎么处理这些问题的。
如何使用
首先我们需要导入cookie_manager的库基于项目中用到的2.0.0版本 它又间接依赖cookie_jar3.0.0这个版本
dependencies:dio_cookie_manager: ^2.0.0cookie_jar: ^3.0.0final 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.Futurevoid saveFromResponse(Uri uri, ListCookie cookies);/// Load the cookies for specified uri.FutureListCookie loadForRequest(Uri uri);Futurevoid deleteAll();Futurevoid 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这个类 /// Dont use this class in Browser environment
class CookieManager extends Interceptor {final CookieJar cookieJar;CookieManager(this.cookieJar);
、
dartoverridevoid 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(ListCookie cookies) {return cookies.map((cookie) ${cookie.name}${cookie.value}).join(; );}构造的cookieJar传进来后在onRequest方法里面调用loadForRequest方法获取格式化后的Cookie将他们转化为请求需要的格式然后设置在请求头里之后继续执行下面的拦截器的逻辑。 overridevoid 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);});}Futurevoid _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 FileStoragePersistCookieJar({this.persistSession true,bool ignoreExpires false,Storage? storage}): super(ignoreExpires: ignoreExpires) {this.storage storage ?? FileStorage();}为了实现持久化引入了一个Storage类默认实现是FileStorage文件存储也可以自己实现其他方式。 overrideFutureListCookie loadForRequest(Uri uri) async {await _checkInitialized();await _load(uri);return super.loadForRequest(uri);}PersistCookieJar最终也调用了DefaultCookieJar的loadForRequest方法但是在那之前还执行了两个方法我们一个一个看 Futurevoid _checkInitialized({bool force false}) async {if (force || !_initialized) {await storage.init(persistSession, ignoreExpires);// Load domain cookiesvar str await storage.read(DomainsKey);...}执行了storage的init与read方法 overrideFuturevoid 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();}Futurevoid _makeCookieDir() async {final directory Directory(_curDir!);if (!directory.existsSync()) {await directory.create(recursive: true);}}init方法主要是检查传入的保存目录是否存在不存在就创建。 read方法与load方法分别将文件中数据读取并存入内存中domainCookies与hostCookies Futurevoid _load(Uri uri) async {final host uri.host;if (_hostSet.contains(host) hostCookies[host] null) {var str await storage.read(host);overrideFuturevoid saveFromResponse(Uri uri, ListCookie 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方法 Futurevoid _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让我们平时使用起来更便捷。