Flutter网络请求库Dio的封装(单例、动态baseUrl、拦截器、日志、请求loading)

本文主要是介绍Flutter网络请求库Dio的封装(单例、动态baseUrl、拦截器、日志、请求loading),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

封装网络请求的几个好处:

  1. 便于统一配置请求参数,如header,公共参数,加密规则等
  2. 方便调试,详细的日志打印信息
  3. 优化代码性能,避免到处滥new对象,构建全局单例
  4. 简化请求步骤,只暴露需要的响应数据,而对错误的响应统一回调
  5. 对接口数据的基类封装,简化解析流程
  6. 无侵入的,灵活的请求loading配置

请求loading自动化

只需要传递一个参数,就可以为请求加上Loading效果,没有任何的代码入侵

 

  var params = DataHelper.getBaseMap();params.clear();params["apikey"] = "0df993c66c0c636e29ecbb5344252a4a";params["start"] = "0";params["count"] = "10";
//withLoading也可以省略,默认就加上,会更简洁ResultData res = await HttpManager.getInstance().get(Address.TEST_API, params: params, withLoading: true);

请求时loading

清晰全面的日志打印

再也不需要额外地配置抓包了,接口调试效率大大提升

 

image.png

下面通过关键源码介绍下封装过程

HttpManager的定义

构造全局单例,配置请求参数,配置通用的GET\POST,支持baseUrl的切换

 

import 'package:dio/dio.dart';
import 'package:flutter_net/code.dart';
import 'package:flutter_net/dio_log_interceptor.dart';
import 'package:flutter_net/loading_utils.dart';
import 'response_interceptor.dart';
import 'result_data.dart';
import 'address.dart';class HttpManager {static HttpManager _instance = HttpManager._internal();Dio _dio;static const CODE_SUCCESS = 200;static const CODE_TIME_OUT = -1;factory HttpManager() => _instance;///通用全局单例,第一次使用时初始化HttpManager._internal({String baseUrl}) {if (null == _dio) {_dio = new Dio(new BaseOptions(baseUrl: Address.BASE_URL, connectTimeout: 15000));_dio.interceptors.add(new DioLogInterceptor());
//      _dio.interceptors.add(new PrettyDioLogger());_dio.interceptors.add(new ResponseInterceptors());}}static HttpManager getInstance({String baseUrl}) {if (baseUrl == null) {return _instance._normal();} else {return _instance._baseUrl(baseUrl);}}//用于指定特定域名HttpManager _baseUrl(String baseUrl) {if (_dio != null) {_dio.options.baseUrl = baseUrl;}return this;}//一般请求,默认域名HttpManager _normal() {if (_dio != null) {if (_dio.options.baseUrl != Address.BASE_URL) {_dio.options.baseUrl = Address.BASE_URL;}}return this;}///通用的GET请求get(api, {params, withLoading = true}) async {if (withLoading) {LoadingUtils.show();}Response response;try {response = await _dio.get(api, queryParameters: params);if (withLoading) {LoadingUtils.dismiss();}} on DioError catch (e) {if (withLoading) {LoadingUtils.dismiss();}return resultError(e);}if (response.data is DioError) {return resultError(response.data['code']);}return response.data;}///通用的POST请求post(api, {params, withLoading = true}) async {if (withLoading) {LoadingUtils.show();}Response response;try {response = await _dio.post(api, data: params);if (withLoading) {LoadingUtils.dismiss();}} on DioError catch (e) {if (withLoading) {LoadingUtils.dismiss();}return resultError(e);}if (response.data is DioError) {return resultError(response.data['code']);}return response.data;}
}ResultData resultError(DioError e) {Response errorResponse;if (e.response != null) {errorResponse = e.response;} else {errorResponse = new Response(statusCode: 666);}if (e.type == DioErrorType.CONNECT_TIMEOUT ||e.type == DioErrorType.RECEIVE_TIMEOUT) {errorResponse.statusCode = Code.NETWORK_TIMEOUT;}return new ResultData(errorResponse.statusMessage, false, errorResponse.statusCode);
}

响应基类

默认200的情况isSuccess为true,响应为response.data,赋值给data

 

class ResultData {var data;bool isSuccess;int code;var headers;ResultData(this.data, this.isSuccess, this.code, {this.headers});
}

Api的封装

请求的集中管理

 

class Api {///示例请求static request(String param) {var params = DataHelper.getBaseMap();params['param'] = param;return HttpManager.getInstance().get(Address.TEST_API, params);}
}

公共参数和加密等

 

class DataHelper{static SplayTreeMap getBaseMap() {var map = new SplayTreeMap<String, dynamic>();map["platform"] = AppConstants.PLATFORM;map["system"] = AppConstants.SYSTEM;map["channel"] = AppConstants.CHANNEL;map["time"] = new DateTime.now().millisecondsSinceEpoch.toString();return map;}static string2MD5(String data) {var content = new Utf8Encoder().convert(data);var digest = md5.convert(content);return hex.encode(digest.bytes);}
}

地址的配置

方便地址管理

 

class Address {static const String TEST_API =  "test_api";
}

响应拦截器

过滤正确的响应数据,对数据进行初步封装

 

import 'package:dio/dio.dart';
import 'package:exchange_flutter/common/net/code.dart';
import 'package:flutter/material.dart';
import '../result_data.dart';class ResponseInterceptors extends InterceptorsWrapper {@overrideonResponse(Response response) {RequestOptions option = response.request;try {if (option.contentType != null &&option.contentType.primaryType == "text") {return new ResultData(response.data, true, Code.SUCCESS);}///一般只需要处理200的情况,300、400、500保留错误信息if (response.statusCode == 200 || response.statusCode == 201) {int code = response.data["code"];if (code == 0) {return new ResultData(response.data, true, Code.SUCCESS,headers: response.headers);} else if (code == 100006 || code == 100007) {} else {Fluttertoast.showToast(msg: response.data["msg"]);return new ResultData(response.data, false, Code.SUCCESS,headers: response.headers);}}} catch (e) {print(e.toString() + option.path);return new ResultData(response.data, false, response.statusCode,headers: response.headers);}return new ResultData(response.data, false, response.statusCode,headers: response.headers);}
}

日志拦截器

打印请求参数和返回参数

 


import 'package:dio/dio.dart';///日志拦截器
class DioLogInterceptor extends Interceptor {@overrideFuture onRequest(RequestOptions options) async {String requestStr = "\n==================== REQUEST ====================\n""- URL:\n${options.baseUrl + options.path}\n""- METHOD: ${options.method}\n";requestStr += "- HEADER:\n${options.headers.mapToStructureString()}\n";final data = options.data;if (data != null) {if (data is Map)requestStr += "- BODY:\n${data.mapToStructureString()}\n";else if (data is FormData) {final formDataMap = Map()..addEntries(data.fields)..addEntries(data.files);requestStr += "- BODY:\n${formDataMap.mapToStructureString()}\n";} elserequestStr += "- BODY:\n${data.toString()}\n";}print(requestStr);return options;}@overrideFuture onError(DioError err) async {String errorStr = "\n==================== RESPONSE ====================\n""- URL:\n${err.request.baseUrl + err.request.path}\n""- METHOD: ${err.request.method}\n";errorStr +="- HEADER:\n${err.response.headers.map.mapToStructureString()}\n";if (err.response != null && err.response.data != null) {print('╔ ${err.type.toString()}');errorStr += "- ERROR:\n${_parseResponse(err.response)}\n";} else {errorStr += "- ERRORTYPE: ${err.type}\n";errorStr += "- MSG: ${err.message}\n";}print(errorStr);return err;}@overrideFuture onResponse(Response response) async {String responseStr ="\n==================== RESPONSE ====================\n""- URL:\n${response.request.uri}\n";responseStr += "- HEADER:\n{";response.headers.forEach((key, list) => responseStr += "\n  " + "\"$key\" : \"$list\",");responseStr += "\n}\n";responseStr += "- STATUS: ${response.statusCode}\n";if (response.data != null) {responseStr += "- BODY:\n ${_parseResponse(response)}";}printWrapped(responseStr);return response;}void printWrapped(String text) {final pattern = new RegExp('.{1,800}'); // 800 is the size of each chunkpattern.allMatches(text).forEach((match) => print(match.group(0)));}String _parseResponse(Response response) {String responseStr = "";var data = response.data;if (data is Map)responseStr += data.mapToStructureString();else if (data is List)responseStr += data.listToStructureString();elseresponseStr += response.data.toString();return responseStr;}
}extension Map2StringEx on Map {String mapToStructureString({int indentation = 2}) {String result = "";String indentationStr = " " * indentation;if (true) {result += "{";this.forEach((key, value) {if (value is Map) {var temp = value.mapToStructureString(indentation: indentation + 2);result += "\n$indentationStr" + "\"$key\" : $temp,";} else if (value is List) {result += "\n$indentationStr" +"\"$key\" : ${value.listToStructureString(indentation: indentation + 2)},";} else {result += "\n$indentationStr" + "\"$key\" : \"$value\",";}});result = result.substring(0, result.length - 1);result += indentation == 2 ? "\n}" : "\n${" " * (indentation - 1)}}";}return result;}
}extension List2StringEx on List {String listToStructureString({int indentation = 2}) {String result = "";String indentationStr = " " * indentation;if (true) {result += "$indentationStr[";this.forEach((value) {if (value is Map) {var temp = value.mapToStructureString(indentation: indentation + 2);result += "\n$indentationStr" + "\"$temp\",";} else if (value is List) {result += value.listToStructureString(indentation: indentation + 2);} else {result += "\n$indentationStr" + "\"$value\",";}});result = result.substring(0, result.length - 1);result += "\n$indentationStr]";}return result;}
}

示例请求

dart的json解析推荐使用json_serializable,其他的有些坑,慎用

 

void request() async {ResultData res = await Api.request("param");if (res.isSuccess) {//拿到res.data就可以进行Json解析了,这里一般用来构造实体类TestBean bean = TestBean.fromMap(res.data);}else{//处理错误}}

Demo地址 https://github.com/po1arbear/Flutter-Net.git

如果觉得有帮助,希望能给个star鼓励下,如果不能满足你的需求,欢迎提issue : )



作者:刺客的幻影
链接:https://www.jianshu.com/p/5ead0cf96642
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

这篇关于Flutter网络请求库Dio的封装(单例、动态baseUrl、拦截器、日志、请求loading)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/508763

相关文章

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解

《如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解》:本文主要介绍如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别的相关资料,描述了如何使用海康威视设备网络SD... 目录前言开发流程问题和解决方案dll库加载不到的问题老旧版本sdk不兼容的问题关键实现流程总结前言作为

Spring Boot整合log4j2日志配置的详细教程

《SpringBoot整合log4j2日志配置的详细教程》:本文主要介绍SpringBoot项目中整合Log4j2日志框架的步骤和配置,包括常用日志框架的比较、配置参数介绍、Log4j2配置详解... 目录前言一、常用日志框架二、配置参数介绍1. 日志级别2. 输出形式3. 日志格式3.1 PatternL

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表

Java使用POI-TL和JFreeChart动态生成Word报告

《Java使用POI-TL和JFreeChart动态生成Word报告》本文介绍了使用POI-TL和JFreeChart生成包含动态数据和图表的Word报告的方法,并分享了实际开发中的踩坑经验,通过代码... 目录前言一、需求背景二、方案分析三、 POI-TL + JFreeChart 实现3.1 Maven

Java导出Excel动态表头的示例详解

《Java导出Excel动态表头的示例详解》这篇文章主要为大家详细介绍了Java导出Excel动态表头的相关知识,文中的示例代码简洁易懂,具有一定的借鉴价值,有需要的小伙伴可以了解下... 目录前言一、效果展示二、代码实现1.固定头实体类2.动态头实现3.导出动态头前言本文只记录大致思路以及做法,代码不进

Go语言利用泛型封装常见的Map操作

《Go语言利用泛型封装常见的Map操作》Go语言在1.18版本中引入了泛型,这是Go语言发展的一个重要里程碑,它极大地增强了语言的表达能力和灵活性,本文将通过泛型实现封装常见的Map操作,感... 目录什么是泛型泛型解决了什么问题Go泛型基于泛型的常见Map操作代码合集总结什么是泛型泛型是一种编程范式,允

开启mysql的binlog日志步骤详解

《开启mysql的binlog日志步骤详解》:本文主要介绍MySQL5.7版本中二进制日志(bin_log)的配置和使用,文中通过图文及代码介绍的非常详细,需要的朋友可以参考下... 目录1.查看是否开启bin_log2.数据库会把日志放进logs目录中3.查看log日志总结 mysql版本5.71.查看

vue基于ElementUI动态设置表格高度的3种方法

《vue基于ElementUI动态设置表格高度的3种方法》ElementUI+vue动态设置表格高度的几种方法,抛砖引玉,还有其它方法动态设置表格高度,大家可以开动脑筋... 方法一、css + js的形式这个方法需要在表格外层设置一个div,原理是将表格的高度设置成外层div的高度,所以外层的div需要