当前位置:   article > 正文

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

flutter dio封装

参考链接

https://github.com/cfug/dio/blob/main/dio/README-ZH.md

添加依赖

 手动添加到pubspec.yaml:

  1. dependencies:
  2. dio: ^替换为最新版本

在终端使用以下命令:

$ dart pub add dio

Dio

dio 是一个强大的 HTTP 网络请求库,支持全局配置、Restful API、FormData、拦截器、 请求取消、Cookie 管理、文件上传/下载、超时、自定义适配器、转换器等。

 使用单例模式封装网络工具类

单例模式详见:Flutter之单例模式的四种方法_YUFENGSHI.LJ的博客-CSDN博客

  1. class HttpManager{
  2. //1、通过静态方法 getInstance() 访问实例—————— getInstance() 构造、获取、返回实例
  3. /*通过工厂方法获取该类的实例,将实例对象按对应的方法返回出去
  4. *实例不存在时,调用命名构造方法获取一个新的实例 */
  5. static HttpManager getInstance(){
  6. if(_instance==null){
  7. _instance=HttpManager._internal();
  8. }
  9. return _instance!;
  10. }
  11. //2、静态属性——该类的实例
  12. static HttpManager? _instance=HttpManager._internal();
  13. //3、私有的命名构造函数,确保外部不能拿到它————初始化实例
  14. HttpManager._internal(){}
  15. //4.1、创建一个 Dio 实例
  16. late Dio dio;

创建一个Dio实例,并初始化

可以使用默认配置或传递一个可选 BaseOptions参数来创建一个Dio实例

请求配置

BaseOptions 描述的是 Dio 实例发起网络请求的的公共配置,而 Options 描述了每一个Http请求的配置信息,每一次请求都可以单独配置。

单次请求的 Options 中的配置信息可以覆盖 BaseOptions 中的配置。

BaseOptions :

基类请求配置

  1. //请求方式
  2. String? method,
  3. //连接超时时间
  4. Duration? connectTimeout,
  5. //接收超时
  6. Duration? receiveTimeout,
  7. //发送超时
  8. Duration? sendTimeout,
  9. //基本网址
  10. String baseUrl = '',
  11. //请求包头
  12. Map<String, dynamic>? headers,
  13. //以何种方式接收响应数据,默认是json
  14. ResponseType? responseType = ResponseType.json,
  15. //内容类型
  16. String? contentType,
  17. Map<String, dynamic>? queryParameters,
  18. Map<String, dynamic>? extra,
  19. ValidateStatus? validateStatus,
  20. bool? receiveDataWhenStatusError,
  21. bool? followRedirects,
  22. int? maxRedirects,
  23. bool? persistentConnection,
  24. RequestEncoder? requestEncoder,
  25. ResponseDecoder? responseDecoder,
  26. ListFormat? listFormat,

Options : 

单次请求配置

  1. /// 请求方式。
  2. String method;
  3. /// 请求基本地址,可以包含路径例如 https://dart.dev/api/
  4. String? baseUrl;
  5. /// HTTP 请求头。
  6. Map<String, dynamic>? headers;
  7. /// 连接服务器超时时间.
  8. Duration? connectTimeout;
  9. /// 两次数据流数据接收的最长间隔时间,注意不是请求的最长接收时间。
  10. Duration? receiveTimeout;
  11. /// 请求内容体,可以是任意类型。
  12. dynamic data;
  13. /// 请求路径,如果以 http(s)开始, 则 [baseURL] 会被忽略,
  14. /// 否则将会和 [baseUrl] 拼接出完整的地址。
  15. String path = '';
  16. /// 请求的 Content-Type。
  17. ///
  18. /// 默认值会由 [ImplyContentTypeInterceptor] 根据请求载荷类型进行推导。
  19. /// 可以调用 [Interceptors.removeImplyContentTypeInterceptor] 进行移除。
  20. ///
  21. /// 如果你想以 `application/x-www-form-urlencoded` 格式编码请求数据,
  22. /// 可以设置此选项为 `Headers.formUrlEncodedContentType`,
  23. /// [Dio] 会自动编码请求体。
  24. String? contentType;
  25. /// 期望以哪种格式(方式)接受响应数据,包括 `json`、`stream` 和 `plain`。
  26. ///
  27. /// 默认值是 `json`, 当响应头中 content-type 为 `application/json` 时,
  28. /// dio 会自动将响应内容转化为 json 对象。
  29. /// 如果想以二进制方式接受响应数据,如下载一个二进制文件,那么可以使用 `stream`。
  30. ///
  31. /// 如果想以文本(字符串)格式接收响应数据,请使用 `plain`。
  32. ResponseType? responseType;
  33. /// `validateStatus` 决定 HTTP 响应状态码是否被视为请求成功,
  34. /// 返回 `true` 请求结果就会按成功处理,否则会按失败处理.
  35. ValidateStatus? validateStatus;
  36. /// 用户自定义字段,可以在 [Interceptor]、[Transformer] 和 [Response] 中依次传递。
  37. Map<String, dynamic>? extra;
  38. /// 请求地址的参数。
  39. Map<String, dynamic /*String|Iterable<String>*/ >? queryParameters;
  40. /// 请求数据中数组的编码的方式,默认值为 `multiCompatible`。
  41. ListFormat? listFormat;

RequestOptions:

实际请求配置,即[BaseOptions]和[Options]组合后的最终结果。 

  1. /// The internal request option class that is the eventual result after
  2. /// [BaseOptions] and [Options] are composed.
  3. class RequestOptions extends _RequestConfig with OptionsMixin {
  4. RequestOptions({
  5. this.path = '',
  6. this.data,
  7. this.onReceiveProgress,
  8. this.onSendProgress,
  9. this.cancelToken,
  10. String? method,
  11. Duration? sendTimeout,
  12. Duration? receiveTimeout,
  13. Duration? connectTimeout,
  14. Map<String, dynamic>? queryParameters,
  15. String? baseUrl,
  16. Map<String, dynamic>? extra,
  17. Map<String, dynamic>? headers,
  18. ResponseType? responseType,
  19. String? contentType,
  20. ValidateStatus? validateStatus,
  21. bool? receiveDataWhenStatusError,
  22. bool? followRedirects,
  23. int? maxRedirects,
  24. bool? persistentConnection,
  25. RequestEncoder? requestEncoder,
  26. ResponseDecoder? responseDecoder,
  27. ListFormat? listFormat,
  28. bool? setRequestContentTypeWhenNoPayload,
  29. StackTrace? sourceStackTrace,
  30. }) : assert(connectTimeout == null || !connectTimeout.isNegative),
  31. super(
  32. method: method,
  33. sendTimeout: sendTimeout,
  34. receiveTimeout: receiveTimeout,
  35. extra: extra,
  36. headers: headers,
  37. responseType: responseType,
  38. contentType: contentType,
  39. validateStatus: validateStatus,
  40. receiveDataWhenStatusError: receiveDataWhenStatusError,
  41. followRedirects: followRedirects,
  42. maxRedirects: maxRedirects,
  43. persistentConnection: persistentConnection,
  44. requestEncoder: requestEncoder,
  45. responseDecoder: responseDecoder,
  46. listFormat: listFormat,
  47. ) {
  48. this.sourceStackTrace = sourceStackTrace ?? StackTrace.current;
  49. this.queryParameters = queryParameters ?? {};
  50. this.baseUrl = baseUrl ?? '';
  51. this.connectTimeout = connectTimeout;
  52. }
  53. /// Create a [RequestOptions] from current instance with merged attributes.
  54. RequestOptions copyWith({
  55. String? method,
  56. Duration? sendTimeout,
  57. Duration? receiveTimeout,
  58. Duration? connectTimeout,
  59. dynamic data,
  60. String? path,
  61. Map<String, dynamic>? queryParameters,
  62. String? baseUrl,
  63. ProgressCallback? onReceiveProgress,
  64. ProgressCallback? onSendProgress,
  65. CancelToken? cancelToken,
  66. Map<String, dynamic>? extra,
  67. Map<String, dynamic>? headers,
  68. ResponseType? responseType,
  69. String? contentType,
  70. ValidateStatus? validateStatus,
  71. bool? receiveDataWhenStatusError,
  72. bool? followRedirects,
  73. int? maxRedirects,
  74. bool? persistentConnection,
  75. RequestEncoder? requestEncoder,
  76. ResponseDecoder? responseDecoder,
  77. ListFormat? listFormat,
  78. bool? setRequestContentTypeWhenNoPayload,
  79. }) {
  80. final contentTypeInHeader = headers != null &&
  81. headers.keys
  82. .map((e) => e.toLowerCase())
  83. .contains(Headers.contentTypeHeader);
  84. assert(
  85. !(contentType != null && contentTypeInHeader),
  86. 'You cannot set both contentType param and a content-type header',
  87. );
  88. final ro = RequestOptions(
  89. method: method ?? this.method,
  90. sendTimeout: sendTimeout ?? this.sendTimeout,
  91. receiveTimeout: receiveTimeout ?? this.receiveTimeout,
  92. connectTimeout: connectTimeout ?? this.connectTimeout,
  93. data: data ?? this.data,
  94. path: path ?? this.path,
  95. baseUrl: baseUrl ?? this.baseUrl,
  96. queryParameters: queryParameters ?? Map.from(this.queryParameters),
  97. onReceiveProgress: onReceiveProgress ?? this.onReceiveProgress,
  98. onSendProgress: onSendProgress ?? this.onSendProgress,
  99. cancelToken: cancelToken ?? this.cancelToken,
  100. extra: extra ?? Map.from(this.extra),
  101. headers: headers ?? Map.from(this.headers),
  102. responseType: responseType ?? this.responseType,
  103. validateStatus: validateStatus ?? this.validateStatus,
  104. receiveDataWhenStatusError:
  105. receiveDataWhenStatusError ?? this.receiveDataWhenStatusError,
  106. followRedirects: followRedirects ?? this.followRedirects,
  107. maxRedirects: maxRedirects ?? this.maxRedirects,
  108. persistentConnection: persistentConnection ?? this.persistentConnection,
  109. requestEncoder: requestEncoder ?? this.requestEncoder,
  110. responseDecoder: responseDecoder ?? this.responseDecoder,
  111. listFormat: listFormat ?? this.listFormat,
  112. sourceStackTrace: sourceStackTrace,
  113. );
  114. if (contentType != null) {
  115. ro.headers.remove(Headers.contentTypeHeader);
  116. ro.contentType = contentType;
  117. } else if (!contentTypeInHeader) {
  118. ro.contentType = this.contentType;
  119. }
  120. return ro;
  121. }
  122. /// The source [StackTrace] which should always point to the invocation of
  123. /// [DioMixin.request] or if not provided, to the construction of the
  124. /// [RequestOptions] instance. In both instances the source context should
  125. /// still be available before it is lost due to asynchronous operations.
  126. @internal
  127. StackTrace? sourceStackTrace;
  128. /// Generate the requesting [Uri] from the options.
  129. Uri get uri {
  130. String url = path;
  131. if (!url.startsWith(RegExp(r'https?:'))) {
  132. url = baseUrl + url;
  133. final s = url.split(':/');
  134. if (s.length == 2) {
  135. url = '${s[0]}:/${s[1].replaceAll('//', '/')}';
  136. }
  137. }
  138. final query = Transformer.urlEncodeQueryMap(queryParameters, listFormat);
  139. if (query.isNotEmpty) {
  140. url += (url.contains('?') ? '&' : '?') + query;
  141. }
  142. // Normalize the url.
  143. return Uri.parse(url).normalizePath();
  144. }
  145. /// Request data in dynamic types.
  146. dynamic data;
  147. /// Defines the path of the request. If it starts with "http(s)",
  148. /// [baseUrl] will be ignored. Otherwise, it will be combined and resolved
  149. /// with the [baseUrl].
  150. String path;
  151. /// {@macro dio.CancelToken}
  152. CancelToken? cancelToken;
  153. /// {@macro dio.options.ProgressCallback}
  154. ProgressCallback? onReceiveProgress;
  155. /// {@macro dio.options.ProgressCallback}
  156. ProgressCallback? onSendProgress;
  157. }

初始化DIO 

  1. /*在私有构造方法中,在里面可以进行初始化dio实例*/
  2. HttpManager._internal(){
  3. //4.2、设置BaseOptions
  4. BaseOptions baseOptions=BaseOptions(
  5. //基本网址
  6. baseUrl:"https://lionstock-uat-new.chinaeast2.cloudapp.chinacloudapi.cn:8200/",
  7. //连接超时
  8. connectTimeout: Duration(milliseconds: 30000),
  9. //接收超时
  10. receiveTimeout: Duration(milliseconds: 5000),
  11. //包头
  12. headers: {
  13. "Content-Type": "application/json;Charset=UTF-8",
  14. "connect":"get"
  15. },
  16. //内容类型
  17. contentType: 'application/json;Charset=UTF-8',
  18. //响应类型——期待已那种方式接收数据,默认是json
  19. responseType: ResponseType.json,
  20. );
  21. //4.3 初始化dio实例
  22. dio=new Dio(baseOptions) ;
  23. //添加一个拦截器
  24. dio.interceptors.add(new DioLogInterceptor());
  25. }

响应数据 Response

当请求成功时会返回一个Response对象,它包含如下字段: 

  1. /// 响应数据。可能已经被转换了类型, 详情请参考 [ResponseType]。
  2. T? data;
  3. /// 响应对应的实际请求配置。
  4. RequestOptions requestOptions;
  5. /// 响应的 HTTP 状态码。
  6. int? statusCode;
  7. /// 响应对应状态码的详情信息。
  8. String? statusMessage;
  9. /// 响应是否被重定向
  10. bool isRedirect;
  11. /// 请求连接经过的重定向列表。如果请求未经过重定向,则列表为空。
  12. List<RedirectRecord> redirects;
  13. /// 在 [RequestOptions] 中构造的自定义字段。
  14. Map<String, dynamic> extra;
  15. /// 响应对应的头数据(响应头)
  16. Headers headers;

Get方法

get方法中只有路径是必填的,

  1. Future<Response<T>> get<T>(
  2. String path, {
  3. Object? data,
  4. Map<String, dynamic>? queryParameters,
  5. Options? options,
  6. CancelToken? cancelToken,
  7. ProgressCallback? onReceiveProgress,
  8. });

get方法使用指定的路径和查询参数向服务器发送 HTTP GET 请求。它还允许您传递其他选项,例如标头、响应类型和超时。该方法返回一个 Future 对象,该对象使用包含来自服务器的 HTTP 响应的 Response 对象进行解析。

封装get方法

  1. get(String url,{option,params}) async {
  2. Response response;
  3. try{
  4. response=await dio.get(url,options: Options(responseType: ResponseType.json));
  5. print("response.data:${response.data}");
  6. print("response.data:${response.statusCode}");
  7. print("response.data:${response.statusMessage}");
  8. print("response.data:${response.headers}");
  9. }
  10. on Exception catch(e){
  11. print("Get方法出错:${e.toString()}");
  12. }
  13. }

get请求无参数

 接口:https://api.github.com/orgs/flutterchina/repos 

  1. HttpManager.getInstance().get(
  2. "https://reqres.in/api/users",
  3. option: Options(responseType: ResponseType.plain),
  4. );

 get请求有参数

在请求链接中拼接参数

  1. // 获取id 法一
  2. HttpManager.getInstance().get("https://reqres.in/api/users/1");
  3. // 获取id 法二
  4. HttpManager.getInstance().get("https://reqres.in/api/users?id=2");

通过 queryParameters 配制参数 

  1. // 获取id 法三 用参数类型
  2. Map<String,dynamic> map = Map();
  3. map["id"]= 3;
  4. HttpManager.getInstance().get(
  5. "https://reqres.in/api/users",
  6. option: Options(responseType: ResponseType.json),
  7. params:map
  8. );

 Post方法 

在发出 GET 请求时,我们通常不传递任何数据。但是当发出 POST、PUT、DELETE 等请求时,我们需要传递正文/数据。

post方法与方法类似get,但增加了一个data参数,该参数代表请求正文。使用该方法获取请求标头getAuthorizationHeader并与提供的任何选项合并。使用发出请求dio.post,如果成功则返回响应数据。如果请求由于 Dio 错误而失败,ErrorEntity则会抛出异常。

封装post方法

  1. post(api,{params}) async {
  2. Response response;
  3. //请求参数 为空时,配置
  4. if(params==null){
  5. params["marketNo"] = "PC_Flutter";
  6. params["versionNo"] = '10105';/*版本号*/
  7. params["token"] = '6b2fc908787c428ab16559fce9d86bf2';
  8. params["uid"] = '201323';
  9. }
  10. try{
  11. response=await dio.post(
  12. api,
  13. queryParameters: params,
  14. );
  15. print("post response:${response.data}\n");
  16. }
  17. on Exception catch (e){
  18. print("post出错:${e.toString()}");
  19. }
  20. }

利用post方法注册一个用户

  1. HttpManager.getInstance().post(
  2. "https://www.wanandroid.com/user/register",
  3. params: {
  4. "username": "zsdhwiehfwo",
  5. "password": "123456",
  6. "repassword": "123456"}
  7. );

打印结果: 

post response:{data: {admin: false, chapterTops: [], coinCount: 0, collectIds: [], email: , icon: , id: 151550, nickname: zsdhwiehfwo, password: , publicName: zsdhwiehfwo, token: , type: 0, username: zsdhwiehfwo}, errorCode: 0, errorMsg: }

拦截器

每个 Dio 实例都可以添加任意多个拦截器,他们会组成一个队列,拦截器队列的执行顺序是先进先出。 通过使用拦截器,你可以在请求之前、响应之后和发生异常时(未被 then 或 catchError 处理) 做一些统一的预处理操作。

拦截器处理器

handler.next

  1. /// Deliver the [response] to the next interceptor.
  2. ///
  3. /// Typically, the method should be called once interceptors done
  4. /// manipulating the [response].
  5. ///将[响应]传递给下一个拦截器。通常,一旦拦截器完成操作[响应],就应该调用该方法。
  6. void next(Response response) {
  7. _completer.complete(
  8. InterceptorState<Response>(response),
  9. );
  10. _processNextInQueue?.call();
  11. }

如果不调用handler.next(response)方法,那么请求将被中止,也就是说后续的拦截器和回调函数将不会被执行。 

请求前 onRequest 

  1. /// Called when the request is about to be sent.
  2. void onRequest(
  3. RequestOptions options,
  4. RequestInterceptorHandler handler,
  5. ) {
  6. handler.next(options);
  7. }
  • RequestOptions options,表示本次请求的实际配置

  • RequestInterceptorHandler是一个拦截器处理器,用于处理请求拦截器中的逻辑。

  1. /// The handler for interceptors to handle after respond.
  2. class ResponseInterceptorHandler extends _BaseHandler {
  3. /// Deliver the [response] to the next interceptor.
  4. ///
  5. /// Typically, the method should be called once interceptors done
  6. /// manipulating the [response].
  7. void next(Response response) {
  8. _completer.complete(
  9. InterceptorState<Response>(response),
  10. );
  11. _processNextInQueue?.call();
  12. }
  13. /// Completes the request by resolves the [response] as the result.
  14. void resolve(Response response) {
  15. _completer.complete(
  16. InterceptorState<Response>(
  17. response,
  18. InterceptorResultType.resolve,
  19. ),
  20. );
  21. _processNextInQueue?.call();
  22. }
  23. /// Completes the request by reject with the [error] as the result.
  24. ///
  25. /// Invoking the method will make the rest of interceptors in the queue
  26. /// skipped to handle the request,
  27. /// unless [callFollowingErrorInterceptor] is true
  28. /// which delivers [InterceptorResultType.rejectCallFollowing]
  29. /// to the [InterceptorState].
  30. void reject(DioException error,
  31. [bool callFollowingErrorInterceptor = false]) {
  32. _completer.completeError(
  33. InterceptorState<DioException>(
  34. error,
  35. callFollowingErrorInterceptor
  36. ? InterceptorResultType.rejectCallFollowing
  37. : InterceptorResultType.reject,
  38. ),
  39. error.stackTrace,
  40. );
  41. _processNextInQueue?.call();
  42. }
  43. }
  • handler.next(options)方法将处理后的请求传递给下一个拦截器或者最终的请求回调函数。如果不调用handler.next(options)方法,那么请求将被中止,也就是说后续的拦截器和回调函数将不会被执行。
  • handler.resolve(options)表示直接将请求返回给请求回调函数
  • handler.reject(error)表示将错误信息返回给请求回调函数等

响应前 onResponse

  1. /// Called when the response is about to be resolved.
  2. ///当响应即将解决时调用。
  3. void onResponse(
  4. Response response,
  5. ResponseInterceptorHandler handler,
  6. ) {
  7. handler.next(response);
  8. }
  • response表示响应数据,包括响应状态码、响应头、响应数据等

  • ResponseInterceptorHandler是一个拦截器处理器,用于处理响应拦截器中的逻辑。

  1. /// The handler for interceptors to handle after respond.
  2. class ResponseInterceptorHandler extends _BaseHandler {
  3. /// Deliver the [response] to the next interceptor.
  4. ///
  5. /// Typically, the method should be called once interceptors done
  6. /// manipulating the [response].
  7. void next(Response response) {
  8. _completer.complete(
  9. InterceptorState<Response>(response),
  10. );
  11. _processNextInQueue?.call();
  12. }
  13. /// Completes the request by resolves the [response] as the result.
  14. void resolve(Response response) {
  15. _completer.complete(
  16. InterceptorState<Response>(
  17. response,
  18. InterceptorResultType.resolve,
  19. ),
  20. );
  21. _processNextInQueue?.call();
  22. }
  23. /// Completes the request by reject with the [error] as the result.
  24. ///
  25. /// Invoking the method will make the rest of interceptors in the queue
  26. /// skipped to handle the request,
  27. /// unless [callFollowingErrorInterceptor] is true
  28. /// which delivers [InterceptorResultType.rejectCallFollowing]
  29. /// to the [InterceptorState].
  30. void reject(DioException error,
  31. [bool callFollowingErrorInterceptor = false]) {
  32. _completer.completeError(
  33. InterceptorState<DioException>(
  34. error,
  35. callFollowingErrorInterceptor
  36. ? InterceptorResultType.rejectCallFollowing
  37. : InterceptorResultType.reject,
  38. ),
  39. error.stackTrace,
  40. );
  41. _processNextInQueue?.call();
  42. }
  43. }
  • 在响应拦截器中,我们可以通过 handler.next(response) 方法将响应传递给下一个拦截器或者最终的请求回调函数。如果不调用handler.next(response)方法,那么请求将被中止,也就是说后续的拦截器和回调函数将不会被执行。
  • handler.resolve(response)表示直接将响应返回给请求回调函数
  • handler.reject(error)表示将错误信息返回给请求回调函数等。

异常时 onError

  1. /// Called when an exception was occurred during the request.
  2. ///当请求过程中发生异常时调用。
  3. void onError(
  4. DioException err,
  5. ErrorInterceptorHandler handler,
  6. ) {
  7. handler.next(err);
  8. }
  • DioException 表示异常信息,包括错误类型、错误消息、错误堆栈等信息;
  • ErrorInterceptorHandler 拦截器的处理程序用于处理请求期间发生的错误。 

自定义拦截器:

  1. class DioLogInterceptor extends Interceptor{
  2. ///请求前
  3. @override
  4. Future onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
  5. String requestStr = "\n==================== 请求前拦截——REQUEST ====================\n"
  6. "- URL:\n${options.baseUrl + options.path}\n"
  7. "- METHOD: ${options.method}\n";
  8. requestStr += "- HEADER:\n${options.headers.mapToStructureString()}\n";
  9. final data = options.data;
  10. if (data != null) {
  11. if (data is Map)
  12. requestStr += "- BODY:\n${data.mapToStructureString()}\n";
  13. else if (data is FormData) {
  14. final formDataMap = Map()..addEntries(data.fields)..addEntries(data.files);
  15. requestStr += "- BODY:\n${formDataMap.mapToStructureString()}\n";
  16. } else
  17. requestStr += "- BODY:\n${data.toString()}\n";
  18. }
  19. print(requestStr);
  20. return handler.next(options);
  21. }
  22. }

添加拦截器

  1. //添加一个拦截器
  2. dio.interceptors.add(new DioLogInterceptor());

错误处理

 当请求过程中发生错误时, Dio 会将 Error/Exception 包装成一个 DioException:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/596879
推荐阅读
相关标签
  

闽ICP备14008679号