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

相关文章

python运用requests模拟浏览器发送请求过程

《python运用requests模拟浏览器发送请求过程》模拟浏览器请求可选用requests处理静态内容,selenium应对动态页面,playwright支持高级自动化,设置代理和超时参数,根据需... 目录使用requests库模拟浏览器请求使用selenium自动化浏览器操作使用playwright

深度解析Nginx日志分析与499状态码问题解决

《深度解析Nginx日志分析与499状态码问题解决》在Web服务器运维和性能优化过程中,Nginx日志是排查问题的重要依据,本文将围绕Nginx日志分析、499状态码的成因、排查方法及解决方案展开讨论... 目录前言1. Nginx日志基础1.1 Nginx日志存放位置1.2 Nginx日志格式2. 499

SpringBoot监控API请求耗时的6中解决解决方案

《SpringBoot监控API请求耗时的6中解决解决方案》本文介绍SpringBoot中记录API请求耗时的6种方案,包括手动埋点、AOP切面、拦截器、Filter、事件监听、Micrometer+... 目录1. 简介2.实战案例2.1 手动记录2.2 自定义AOP记录2.3 拦截器技术2.4 使用Fi

go动态限制并发数量的实现示例

《go动态限制并发数量的实现示例》本文主要介绍了Go并发控制方法,通过带缓冲通道和第三方库实现并发数量限制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录带有缓冲大小的通道使用第三方库其他控制并发的方法因为go从语言层面支持并发,所以面试百分百会问到

SpringBoot集成MyBatis实现SQL拦截器的实战指南

《SpringBoot集成MyBatis实现SQL拦截器的实战指南》这篇文章主要为大家详细介绍了SpringBoot集成MyBatis实现SQL拦截器的相关知识,文中的示例代码讲解详细,有需要的小伙伴... 目录一、为什么需要SQL拦截器?二、MyBATis拦截器基础2.1 核心接口:Interceptor

使用Python构建一个高效的日志处理系统

《使用Python构建一个高效的日志处理系统》这篇文章主要为大家详细讲解了如何使用Python开发一个专业的日志分析工具,能够自动化处理、分析和可视化各类日志文件,大幅提升运维效率,需要的可以了解下... 目录环境准备工具功能概述完整代码实现代码深度解析1. 类设计与初始化2. 日志解析核心逻辑3. 文件处

C++ Log4cpp跨平台日志库的使用小结

《C++Log4cpp跨平台日志库的使用小结》Log4cpp是c++类库,本文详细介绍了C++日志库log4cpp的使用方法,及设置日志输出格式和优先级,具有一定的参考价值,感兴趣的可以了解一下... 目录一、介绍1. log4cpp的日志方式2.设置日志输出的格式3. 设置日志的输出优先级二、Window

一文详解SpringBoot中控制器的动态注册与卸载

《一文详解SpringBoot中控制器的动态注册与卸载》在项目开发中,通过动态注册和卸载控制器功能,可以根据业务场景和项目需要实现功能的动态增加、删除,提高系统的灵活性和可扩展性,下面我们就来看看Sp... 目录项目结构1. 创建 Spring Boot 启动类2. 创建一个测试控制器3. 创建动态控制器注

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Linux中压缩、网络传输与系统监控工具的使用完整指南

《Linux中压缩、网络传输与系统监控工具的使用完整指南》在Linux系统管理中,压缩与传输工具是数据备份和远程协作的桥梁,而系统监控工具则是保障服务器稳定运行的眼睛,下面小编就来和大家详细介绍一下它... 目录引言一、压缩与解压:数据存储与传输的优化核心1. zip/unzip:通用压缩格式的便捷操作2.