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

相关文章

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

动态规划---打家劫舍

题目: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 思路: 动态规划五部曲: 1.确定dp数组及含义 dp数组是一维数组,dp[i]代表

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)

ASIO网络调试助手之一:简介

多年前,写过几篇《Boost.Asio C++网络编程》的学习文章,一直没机会实践。最近项目中用到了Asio,于是抽空写了个网络调试助手。 开发环境: Win10 Qt5.12.6 + Asio(standalone) + spdlog 支持协议: UDP + TCP Client + TCP Server 独立的Asio(http://www.think-async.com)只包含了头文件,不依

poj 3181 网络流,建图。

题意: 农夫约翰为他的牛准备了F种食物和D种饮料。 每头牛都有各自喜欢的食物和饮料,而每种食物和饮料都只能分配给一头牛。 问最多能有多少头牛可以同时得到喜欢的食物和饮料。 解析: 由于要同时得到喜欢的食物和饮料,所以网络流建图的时候要把牛拆点了。 如下建图: s -> 食物 -> 牛1 -> 牛2 -> 饮料 -> t 所以分配一下点: s  =  0, 牛1= 1~

poj 3068 有流量限制的最小费用网络流

题意: m条有向边连接了n个仓库,每条边都有一定费用。 将两种危险品从0运到n-1,除了起点和终点外,危险品不能放在一起,也不能走相同的路径。 求最小的费用是多少。 解析: 抽象出一个源点s一个汇点t,源点与0相连,费用为0,容量为2。 汇点与n - 1相连,费用为0,容量为2。 每条边之间也相连,费用为每条边的费用,容量为1。 建图完毕之后,求一条流量为2的最小费用流就行了

poj 2112 网络流+二分

题意: k台挤奶机,c头牛,每台挤奶机可以挤m头牛。 现在给出每只牛到挤奶机的距离矩阵,求最小化牛的最大路程。 解析: 最大值最小化,最小值最大化,用二分来做。 先求出两点之间的最短距离。 然后二分匹配牛到挤奶机的最大路程,匹配中的判断是在这个最大路程下,是否牛的数量达到c只。 如何求牛的数量呢,用网络流来做。 从源点到牛引一条容量为1的边,然后挤奶机到汇点引一条容量为m的边

Flutter 进阶:绘制加载动画

绘制加载动画:由小圆组成的大圆 1. 定义 LoadingScreen 类2. 实现 _LoadingScreenState 类3. 定义 LoadingPainter 类4. 总结 实现加载动画 我们需要定义两个类:LoadingScreen 和 LoadingPainter。LoadingScreen 负责控制动画的状态,而 LoadingPainter 则负责绘制动画。