本文主要是介绍Flutter网络请求库Dio的封装(单例、动态baseUrl、拦截器、日志、请求loading),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
封装网络请求的几个好处:
- 便于统一配置请求参数,如header,公共参数,加密规则等
- 方便调试,详细的日志打印信息
- 优化代码性能,避免到处滥new对象,构建全局单例
- 简化请求步骤,只暴露需要的响应数据,而对错误的响应统一回调
- 对接口数据的基类封装,简化解析流程
- 无侵入的,灵活的请求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)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!