iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >Flutter网络请求框架Dio源码分析以及封装(一)--请求流程分析
  • 549
分享到

Flutter网络请求框架Dio源码分析以及封装(一)--请求流程分析

flutter 2023-09-01 14:09:11 549人浏览 薄情痞子
摘要

Flutter网络请求框架Dio源码分析以及封装--请求流程分析 前言目的请求流程-构造Dio对象请求流程-构造请求参数请求流程-构建请求流并添加拦截器请求流程-请求分发总结 前言 利用

Flutter网络请求框架Dio源码分析以及封装--请求流程分析

前言

利用flutter开发app也已经有些时间了,这个过程中最多接触到的就是网络请求相关的代码。自己目前项目中使用的是现在市面上最流行的网络请求库-dio,相对于flutter自带的HttpClient来说,dio使用起来更简单,功能更强大,支持全局配置、Restful api、FORMData、拦截器、 请求取消、Cookie 管理、文件上传/下载、超时以及自定义适配器等。

目的

写这篇文章的目的是为了系统了解Dio的工作原理,之前碰到了一个网络请求Cookie持久化的问题,折腾了很久才解决掉,浪费了很多时间。这才发现虽然Dio使用了很长时间,但是底层原理还有很多地方没有搞明白。所以,趁这个机会,从头梳理一下Dio的源码,之后再基于新的理解重新封装下。

请求流程-构造Dio对象

首先我们需要导入Dio的库,基于项目中用到的4.0.6版本

dependencies:  dio: ^4.0.6 

首先,我们从官方文档上给出的一个简单示例开始着手:

import 'package:dio/dio.dart';final dio = Dio();void getHttp() async {  final response = await dio.get('https://dart.dev');  print(response);}

要利用Dio发出一个get请求,我们首先需要构造一个Dio的对象实例。我们看下Dio这个类及其构造方法:

abstract class Dio {  factory Dio([BaseOptions? options]) => createDio(options);  ...  }

可以看出,Dio是一个抽象类,Dio的构造方法实际上用了factory-工厂构造函数(当使用factory修饰一个构造器时,DartVM不会总是创建一个新的对象,而是返回一个在内存中已经存在的对象。比如它可能会从缓存中返回一个已有的实例,或者是返回子类的实例),实现的createDio是工厂方法,其实现类通过平台区分导入,在移动端中导入的是 dio_for_native.dart,这个文件中 createDio创建的是DioForNative对象。

// ignore: uri_does_not_exist    if (dart.library.html) 'entry/dio_for_browser.dart'// ignore: uri_does_not_exist    if (dart.library.io) 'entry/dio_for_native.dart';
Dio createDio([BaseOptions? baseOptions]) => DioForNative(baseOptions);class DioForNative with DioMixin implements Dio {  /// Create Dio instance with default [BaseOptions].  /// It is recommended that an application use only the same DIO singleton.  DioForNative([BaseOptions? baseOptions]) {    options = baseOptions ?? BaseOptions();    httpClientAdapter = DefaultHttpClientAdapter();  }

可以看到,DioForNative除了实现Dio这个抽象类以外还混入了DioMixin这个类,通过Dart中Mixins的原理可以直接调用DioMixin里的方法,Dio提供的get、post等方法主要实现在这个类中,可以理解为除去平台差异通用的逻辑都在这里实现。
构造方法里面有一个可选参数baseOptions,这个是所有网络请求的基础配置信息(每次请求可单独配置会覆盖此配置,主要有baseUrl,connectTimeout,receiveTimeout等等),还有一个httpClientAdapter,这个是Dio与flutter自带的HttpClient的适配器,最终通过httpClientAdapter调用HttpClient发起请求,我们先不深究这个,下面来看dio的get方法:

请求流程-构造请求参数

    Future<Response<T>> get<T>(    String path, {    Map<String, dynamic>? queryParameters,    Options? options,    CancelToken? cancelToken,    ProgressCallback? onReceiveProgress,  }) {    return request<T>(      path,      queryParameters: queryParameters,      options: checkOptions('GET', options),      onReceiveProgress: onReceiveProgress,      cancelToken: cancelToken,    );  }

get是由DioMixin实现的,最终调用了request方法,其他如post等方法也是如此,我们先来看看传入的几个参数:

  1. path: 请求的url链接
  2. data: 请求数据,例如上传用到的FromData(post)
  3. queryParameters: 查询参数
  4. options:请求配置
  5. cancelToken: 用来取消发送请求的token
  6. onSendProgress: 网络请求发送的进度
  7. onReceiveProgress: 网络请求接收的进度

request方法里面,主要是执行compose方法将初始化时输入的BaseOptions对象和调用时传入的Options对象合并成RequestOptions对象,接着调用fetch方法并传入生成的RequestOptions对象。

    var requestOptions = options.compose(      this.options,      path,      data: data,      queryParameters: queryParameters,      onReceiveProgress: onReceiveProgress,      onSendProgress: onSendProgress,      cancelToken: cancelToken,    );    ...     return fetch<T>(requestOptions);

请求流程-构建请求流并添加拦截器

构造一个异步的请求流,并循环遍历向请求流中添加请求拦截器:

    // Start the request flow    var future = Future<dynamic>(() => InterceptorState(requestOptions));    // Add request interceptors to request flow    interceptors.forEach((Interceptor interceptor) {      var fun = interceptor is QueuedInterceptor          ? interceptor._handleRequest          : interceptor.onRequest;      future = future.then(_requestInterceptorWrapper(fun));    });

Interceptor是通过队列保存的,队列是“FIFO”模式,也就是先添加的Interceptor会先处理,后续添加的会覆盖之前的处理,通常会在onRequest中添加一些headers等操作,onResponse或onError中对结果处理成调用者想要的方式,onResponse和onError是互斥的

class Interceptor {  // 发送请求前拦截    void onRequest(    RequestOptions options,    RequestInterceptorHandler handler,  ) =>      handler.next(options);    //在结果返回给调用者前拦截  void onResponse(    Response response,    ResponseInterceptorHandler handler,  ) =>      handler.next(response);    //发生错误返回给调用者时拦截  void onError(    DioError err,    ErrorInterceptorHandler handler,  ) =>      handler.next(err);}

可以看到,在遍历拦截器时会判断是否是QueuedInterceptor这个类型,可以理解为一种串行机制。在多个请求同时进入拦截器时只允许一个请求先执行,常用在请求某种token时,因为其他请求可以复用,不用重复请求,这个暂时不深究,正常情况下直接执行onRequest:

  /// The callback will be executed before the request is initiated.  ///  /// If you want to continue the request, call [handler.next].  ///  /// If you want to complete the request with some custom data,  /// you can resolve a [Response] object with [handler.resolve].  ///  /// If you want to complete the request with an error message,  /// you can reject a [DioError] object with [handler.reject].  void onRequest(    RequestOptions options,    RequestInterceptorHandler handler,  ) =>      handler.next(options);

onRequest执行了RequestInterceptorHandler的next方法,实际上是一个Completer对象的complete方法。

/// Handler for request interceptor.class RequestInterceptorHandler extends _BaseHandler {  /// Continue to call the next request interceptor.  void next(RequestOptions requestOptions) {    _completer.complete(InterceptorState<RequestOptions>(requestOptions));    _processNextInQueue?.call();  }  ...}
class _BaseHandler {  final _completer = Completer<InterceptorState>();  void Function()? _processNextInQueue;  Future<InterceptorState> get future => _completer.future;  bool get isCompleted => _completer.isCompleted;}

接着来看_requestInterceptorWrapper这个方法:

    // Convert the request interceptor to a functional callback in which    // we can handle the return value of interceptor callback.    FutureOr Function(dynamic) _requestInterceptorWrapper(      InterceptorSendCallback interceptor,    ) {      return (dynamic _state) async {        var state = _state as InterceptorState;        if (state.type == InterceptorResultType.next) {          return listenCancelForAsyncTask(            requestOptions.cancelToken,            Future(() {              return checkIfNeedEnqueue(interceptors.requestLock, () {                var requestHandler = RequestInterceptorHandler();                interceptor(state.data as RequestOptions, requestHandler);                return requestHandler.future;              });            }),          );        } else {          return state;        }      };    }
typedef InterceptorSendCallback = void Function(  RequestOptions options,  RequestInterceptorHandler handler,);

这里是把函数的回调作为方法的参数,这样就实现了把拦截器转换为函数回调,这里做了一层判断,如果state.type 等于 next 的话,那么会增加一个监听取消的异步任务listenCancelForAsyncTask,并把cancelToken传递给了这个任务,接下来他会检查当前的这个拦截器请求是否入队,最后定义了一个请求拦截器RequestInterceptorHandler,并赋值给InterceptorSendCallback的handler,它的future属性,也就是_completer对象的complete方法,即执行拦截器的onRequest。

请求流程-请求分发

之后继续对请求流进行操作,添加请求分发。

    // Add dispatching callback to request flow    future = future.then(_requestInterceptorWrapper((      RequestOptions reqOpt,      RequestInterceptorHandler handler,    ) {      requestOptions = reqOpt;      _dispatchRequest(reqOpt)          .then((value) => handler.resolve(value, true))          .catchError((e) {        handler.reject(e as DioError, true);      });    }));
  // Initiate Http requests  Future<Response<T>> _dispatchRequest<T>(RequestOptions reqOpt) async {    var cancelToken = reqOpt.cancelToken;    ResponseBody responseBody;    try {      var stream = await _transformData(reqOpt);      responseBody = await httpClientAdapter.fetch(        reqOpt,        stream,        cancelToken?.whenCancel,      );      responseBody.headers = responseBody.headers;      var headers = Headers.fromMap(responseBody.headers);      var ret = Response<T>(        headers: headers,        requestOptions: reqOpt,        redirects: responseBody.redirects ?? [],        isRedirect: responseBody.isRedirect,        statusCode: responseBody.statusCode,        statusMessage: responseBody.statusMessage,        extra: responseBody.extra,      );      var statusOk = reqOpt.validateStatus(responseBody.statusCode);      if (statusOk || reqOpt.receiveDataWhenStatusError == true) {        var forceConvert = !(T == dynamic || T == String) &&            !(reqOpt.responseType == ResponseType.bytes ||                reqOpt.responseType == ResponseType.stream);        String? contentType;        if (forceConvert) {          contentType = headers.value(Headers.contentTypeHeader);          headers.set(Headers.contentTypeHeader, Headers.JSONContentType);        }        ret.data =            (await transformer.transformResponse(reqOpt, responseBody)) as T?;        if (forceConvert) {          headers.set(Headers.contentTypeHeader, contentType);        }      } else {        await responseBody.stream.listen(null).cancel();      }      checkCancelled(cancelToken);      if (statusOk) {        return checkIfNeedEnqueue(interceptors.responseLock, () => ret);      } else {        throw DioError(          requestOptions: reqOpt,          response: ret,          error: 'Http status error [${responseBody.statusCode}]',          type: DioErrorType.response,        );      }    } catch (e) {      throw assureDioError(e, reqOpt);    }  }

对每一次Http请求进行初始化:

  1. _dispatchRequest里面会调用_transfromData 进行数据转换,最终转换出来的数据是一个 Stream 流。
  2. 调用httpClientAdapter进行网络请求 fetch 方法,最终采用系统请求库HttpClient进行网络请求。
  3. 对响应数据进行格式转换,最后返回

总结

在我们调用get/post等方法时,都会进入到request方法,request 方法主要负责对请求配置参数的统一处理,并调用了fetch 方法,而 fetch 中是构建请求流、添加拦截器、请求分发的操作。下一次我们接着这个流程,看下设置cookie的原理。

来源地址:https://blog.csdn.net/Yaoobs/article/details/131168035

--结束END--

本文标题: Flutter网络请求框架Dio源码分析以及封装(一)--请求流程分析

本文链接: https://www.lsjlt.com/news/388560.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Flutter网络请求框架Dio源码分析以及封装(一)--请求流程分析
    Flutter网络请求框架Dio源码分析以及封装--请求流程分析 前言目的请求流程-构造Dio对象请求流程-构造请求参数请求流程-构建请求流并添加拦截器请求流程-请求分发总结 前言 利用...
    99+
    2023-09-01
    flutter
  • Flutter网络请求Dio库的使用及封装方法
    这篇文章主要讲解了“Flutter网络请求Dio库的使用及封装方法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Flutter网络请求Dio库的使用及封装方法”吧!Dart语言内置的Http...
    99+
    2023-06-30
  • Flutter网络请求Dio库的使用及封装详解
    目录一、项目目录结构二、封装思路:三、添加依赖四、简单实现网络请求五、实现登录注册服务六、使用service服务Dart语言内置的HttpClient实现了基本的网络请求相关的操作。...
    99+
    2024-04-02
  • fetch网络请求封装示例分析
    本篇内容主要讲解“fetch网络请求封装示例分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“fetch网络请求封装示例分析”吧!export default ({ ...
    99+
    2023-06-21
  • ajax网络请求封装的示例分析
    这篇文章主要为大家展示了“ajax网络请求封装的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“ajax网络请求封装的示例分析”这篇文章吧。实例代码://...
    99+
    2024-04-02
  • 微信小程序网络请求封装的示例分析
    这篇文章给大家分享的是有关微信小程序网络请求封装的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。在这里首先声明一个小程序文档的bug,导致大伙们在请求的时候,服务器收到不...
    99+
    2024-04-02
  • Retrofit网络请求和响应处理源码分析
    本篇内容主要讲解“Retrofit网络请求和响应处理源码分析”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Retrofit网络请求和响应处理源码分析”吧!网络请求在使用 Retrofit 发起网...
    99+
    2023-07-05
  • Python接口自动化之request请求封装源码分析
    目录1. 源码分析2. requests请求封装3. 总结前言: 我们在做自动化测试的时候,大家都是希望自己写的代码越简洁越好,代码重复量越少越好。那么,我们可以考虑将request...
    99+
    2024-04-02
  • 如何用源码分析Struts2请求处理及过程
    这期内容当中小编将会给大家带来有关如何用源码分析Struts2请求处理及过程,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1.1 Struts2请求处理1. 一个请求在Struts2框架中的处理步骤:a)...
    99+
    2023-06-17
  • 微信小程序HTTP请求从0到1封装的示例分析
    这篇文章给大家分享的是有关微信小程序HTTP请求从0到1封装的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。HTTP库1、jQuery的$.ajax调用了XMLHttpR...
    99+
    2024-04-02
  • axios全局注册,设置token,以及全局设置url请求网段的示例分析
    这篇文章将为大家详细讲解有关axios全局注册,设置token,以及全局设置url请求网段的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。a1.axios全局注册...
    99+
    2024-04-02
  • 全网多种方法分析解决HTTP Status 404资源未找到的错误,TCP的3次握手,dns域名解析,发起http请求以及cookie和session的区别
    文章目录 1. 文章引言2. 简述URL3. http完整请求3.1 DNS域名解析3.2 TCP的3次握手3.3 发起http请求3.4 浏览器解析html代码3.5 浏览器对页面进行渲染呈...
    99+
    2023-10-28
    http 服务器 tcp 后端 前端
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作