基础库
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

net_util.dart 12 KiB

4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
4 년 전
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. import 'package:dio/dio.dart';
  2. import 'package:dio/adapter.dart';
  3. import 'package:zhiying_comm/util/empty_util.dart';
  4. import 'dart:io';
  5. import 'dart:ui';
  6. import 'dart:convert';
  7. import 'package:zhiying_comm/zhiying_comm.dart';
  8. import 'package:package_info/package_info.dart';
  9. import 'package:device_info/device_info.dart';
  10. import 'package:fluttertoast/fluttertoast.dart';
  11. import 'global_config.dart';
  12. import 'shared_prefe_util.dart';
  13. import 'time_util.dart';
  14. typedef OnSuccess = dynamic Function(dynamic data);
  15. typedef OnError = dynamic Function(String e);
  16. typedef OnCache = dynamic Function(dynamic data);
  17. enum NetMethod {
  18. GET,
  19. POST,
  20. PUT,
  21. DELETE,
  22. OPTIONS,
  23. PATCH,
  24. UPDATE,
  25. HEAD,
  26. }
  27. class NetUtil {
  28. Dio _dio;
  29. NetUtil._();
  30. static NetUtil _instance;
  31. static NetUtil getInstance() {
  32. if (_instance == null) {
  33. _instance = NetUtil._();
  34. }
  35. return _instance;
  36. }
  37. get dio async {
  38. if (_dio == null) {
  39. var setting = await NativeUtil.getSetting();
  40. String domain = setting['domain']; //'http://www.hairuyi.com/';
  41. _config(domain, proxyUrl: '192.168.0.66:8866');
  42. }
  43. return _dio;
  44. }
  45. /// 配置网络请求,基础地址,代理地址,如:192.168.0.123:8888(代理地址生产环境无效)
  46. /// apiVersion 接口版本
  47. void _config(String baseUrl, {String proxyUrl}) {
  48. _dio = Dio(BaseOptions(
  49. method: "post",
  50. baseUrl: baseUrl,
  51. connectTimeout: 15000,
  52. receiveTimeout: 15000,
  53. contentType: Headers.jsonContentType,
  54. followRedirects: true,
  55. headers: {'device': 'wx_applet', 'Platform': 'wx_applet'},
  56. ));
  57. _dio.interceptors.add(_NetInterceptors());
  58. // _dio.interceptors.add(LogInterceptor());
  59. const bool inProduction = const bool.fromEnvironment("dart.vm.product");
  60. if (proxyUrl != null && proxyUrl != '' && !inProduction) {
  61. _setProxy(proxyUrl);
  62. }
  63. }
  64. /// 设置代理
  65. void _setProxy(String proxyUrl) {
  66. (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
  67. (HttpClient client) {
  68. client.findProxy = (uri) {
  69. return "PROXY $proxyUrl";
  70. };
  71. client.badCertificateCallback =
  72. (X509Certificate cert, String host, int port) => true;
  73. };
  74. }
  75. /// 同步请求
  76. static Future<dynamic> post(String path,
  77. {Map<String, dynamic> params, NetMethod method = NetMethod.POST, bool cache = false}) async {
  78. if (params == null) {
  79. params = {};
  80. }
  81. // 根据请求参数,获取缓存的Key
  82. String cacheKey = getRequestParamsCachedKey(params, path);
  83. // 参数签名 TODO 加密?
  84. Map<String, dynamic> sign = await signParams(params);
  85. Response response;
  86. try {
  87. Dio dio = await NetUtil.getInstance().dio;
  88. response = await dio.request(path, data: sign, options: Options(method: enumToString(method)));
  89. } on DioError catch (e) {
  90. _formatError(e);
  91. }
  92. try {
  93. var result = response.data is Map ? response.data : jsonDecode(response.data);
  94. // TODO 解密?
  95. if (isSuccess(result)) {
  96. // 缓存成功的数据
  97. if(cache) {
  98. _setCallBackCacheData(cacheKey, response.data is Map ? jsonEncode(response.data) : response.data);
  99. }
  100. return result;
  101. // 缓存返回的数据
  102. }
  103. Logger.error('error: ' + result[GlobalConfig.HTTP_RESPONSE_KEY_MSG]);
  104. Fluttertoast.showToast(msg: result[GlobalConfig.HTTP_RESPONSE_KEY_MSG], toastLength: Toast.LENGTH_SHORT, gravity: ToastGravity.BOTTOM,);
  105. return result;
  106. } catch (e) {
  107. return null;
  108. }
  109. }
  110. /// 异步请求
  111. static void request(String path,
  112. {NetMethod method = NetMethod.GET,
  113. Map<String, dynamic> params,
  114. Map<String, File> multiFiles,
  115. OnSuccess onSuccess,
  116. OnError onError,
  117. OnCache onCache}) async {
  118. if (params == null) {
  119. params = {};
  120. }
  121. // 根据请求参数,获取缓存的Key
  122. String cacheKey = getRequestParamsCachedKey(params, path);
  123. // // 读取缓存回调
  124. if (onCache != null) {
  125. await _onCallBackCacheData(onCache, cacheKey);
  126. }
  127. // 参数签名
  128. Map<String, dynamic> sign = await signParams(params);
  129. Response response;
  130. try {
  131. Dio dio = await NetUtil.getInstance().dio;
  132. response = await dio.request(path,
  133. data: sign, options: Options(method: enumToString(method)));
  134. } on DioError catch (e) {
  135. _formatError(e);
  136. }
  137. try {
  138. var result =
  139. response.data is Map ? response.data : jsonDecode(response.data);
  140. // TODO 解密?
  141. if (isSuccess(result)) {
  142. if (onSuccess != null) {
  143. onSuccess(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]);
  144. // 缓存返回的数据
  145. _setCallBackCacheData(cacheKey,
  146. response.data is Map ? jsonEncode(response.data) : response.data);
  147. }
  148. return;
  149. }
  150. Logger.error('error: ' + result[GlobalConfig.HTTP_RESPONSE_KEY_MSG]);
  151. Fluttertoast.showToast(
  152. msg: result[GlobalConfig.HTTP_RESPONSE_KEY_MSG],
  153. toastLength: Toast.LENGTH_SHORT,
  154. gravity: ToastGravity.BOTTOM,
  155. );
  156. if (onError != null) {
  157. onError( !EmptyUtil.isEmpty(result) ? !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_MSG]) ? result[GlobalConfig.HTTP_RESPONSE_KEY_MSG] : '未知错误' : '未知错误');
  158. }
  159. } catch (e) {
  160. Logger.error('error: ' + e.toString());
  161. if (onError != null) {
  162. onError(e?.toString() ?? '未知错误');
  163. }
  164. }
  165. return;
  166. }
  167. /// 统一添加必要的参数
  168. static Future<Map<String, dynamic>> signParams(
  169. Map<String, dynamic> params) async {
  170. // 应用信息
  171. PackageInfo packageInfo = await PackageInfo.fromPlatform();
  172. // 原生传的信息
  173. Map setting = await NativeUtil.getSetting();
  174. if (Platform.isIOS) {
  175. IosDeviceInfo iosInfo = await DeviceInfoPlugin().iosInfo;
  176. // 设备
  177. params["platform"] = "ios";
  178. // 设备系统版本
  179. params["system_version"] = iosInfo?.systemVersion;
  180. // 设备型号 如:iPhone 11pro
  181. params['device_model'] = iosInfo?.name;
  182. // 设备型号,如:MLMF2LL/A
  183. // params['device_number'] = iosInfo?.model;
  184. // 设备ID
  185. params['device_id'] = iosInfo?.identifierForVendor;
  186. } else if (Platform.isAndroid) {
  187. AndroidDeviceInfo androidInfo = await DeviceInfoPlugin().androidInfo;
  188. // 设备
  189. params["platform"] = "android";
  190. // 设备系统版本
  191. params["system_version"] = "Android ${androidInfo?.version?.release}";
  192. // 设备型号 如:iPhone 11pro
  193. params['device_model'] = androidInfo?.model;
  194. // 设备型号,如:MLMF2LL/A
  195. // params['device_number'] = androidInfo?.id;
  196. // 设备Id
  197. params['device_id'] = androidInfo?.androidId;
  198. }
  199. // 应用版本号
  200. params["app_version"] = packageInfo.version;
  201. // 分辨率
  202. params["solution"] =
  203. "${window.physicalSize.width.floor()}*${window.physicalSize.height.floor()}";
  204. // 站长ID
  205. String masterId = setting['master_id'];
  206. if (null != masterId && masterId != '' && (!params.containsKey('master_id') || params['master_id'] == '')) {
  207. params['master_id'] = masterId;
  208. }
  209. // secret_key
  210. params['secret_key'] = setting['secret_key'];
  211. // token
  212. String token = setting['token'];
  213. if (token != null &&
  214. token != '' &&
  215. (!params.containsKey('token') || params['token'] == '')) {
  216. params['token'] = token;
  217. }
  218. // 当前时间戳:秒
  219. params["time"] = TimeUtil.getNowTime();
  220. // 过滤空字段,过滤首尾空格
  221. Map<String, dynamic> filters = Map<String, dynamic>();
  222. params.forEach((key, value) {
  223. if (key != '' && value != '') {
  224. filters[key] = (value is String) ? value.trim() : value;
  225. }
  226. });
  227. params = filters;
  228. List<String> list = List<String>();
  229. params.forEach((key, value) {
  230. list.add(key.toString() + value.toString());
  231. });
  232. params["sign"] = signWithArray(list);
  233. return params;
  234. }
  235. /// 获取请求缓存成功的数据
  236. static Future<void> _onCallBackCacheData(
  237. OnCache onCache, String cacheKey) async {
  238. // 读取缓存
  239. Map<String, dynamic> cacheMap =
  240. await SharedPreferencesUtil.getNetCacheResult(cacheKey);
  241. if (!EmptyUtil.isEmpty(cacheMap) &&
  242. cacheMap.containsKey(GlobalConfig.HTTP_RESPONSE_KEY_CODE) &&
  243. (cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  244. GlobalConfig.RESPONSE_SUCCESS_CODE ||
  245. cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  246. '${GlobalConfig.RESPONSE_SUCCESS_CODE}') && !EmptyUtil.isEmpty(cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) {
  247. onCache(cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA]);
  248. return;
  249. }
  250. return;
  251. }
  252. /// 缓存请求成功的数据
  253. static void _setCallBackCacheData(String cacheKey, String value) async {
  254. SharedPreferencesUtil.setNetCacheResult(cacheKey, value);
  255. }
  256. /// 根据请求参数,获取缓存的数据
  257. static Future<dynamic> getRequestCachedData(String url, {Map<String, dynamic> params}) async{
  258. Map<String, dynamic> cacheMap = await SharedPreferencesUtil.getNetCacheResult(getRequestParamsCachedKey(params, url));
  259. if (!EmptyUtil.isEmpty(cacheMap) &&
  260. cacheMap.containsKey(GlobalConfig.HTTP_RESPONSE_KEY_CODE) &&
  261. (cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  262. GlobalConfig.RESPONSE_SUCCESS_CODE ||
  263. cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  264. '${GlobalConfig.RESPONSE_SUCCESS_CODE}') && !EmptyUtil.isEmpty(cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) {
  265. return cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA];
  266. }
  267. return null;
  268. }
  269. /// 判断后台返回是否成功
  270. static bool isSuccess(Map<String, dynamic> data){
  271. try {
  272. if (!EmptyUtil.isEmpty(data) &&
  273. (data[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  274. GlobalConfig.RESPONSE_SUCCESS_CODE ||
  275. data[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  276. '${GlobalConfig.RESPONSE_SUCCESS_CODE}')) {
  277. return true;
  278. }
  279. }catch(e){
  280. return false;
  281. }
  282. return false;
  283. }
  284. /// 根据请求参数,获取缓存的Key
  285. static String getRequestParamsCachedKey(
  286. Map<String, dynamic> map, String path) {
  287. if (EmptyUtil.isEmpty(map)) {
  288. return EncodeUtil.generateMd5(path);
  289. }
  290. return EncodeUtil.generateMd5(path + map.toString());
  291. }
  292. /// 签名
  293. static String signWithArray(List<String> params) {
  294. // 字母升序
  295. params.sort();
  296. String result = "";
  297. // result += secret_key;
  298. params.forEach((param) {
  299. result += param;
  300. });
  301. // result += secret_key;
  302. return EncodeUtil.generateMd5(result);
  303. }
  304. /*
  305. * error统一处理
  306. */
  307. static void _formatError(DioError e) {
  308. if (e.type == DioErrorType.CONNECT_TIMEOUT) {
  309. Logger.error('连接超时: ${e.toString()}');
  310. } else if (e.type == DioErrorType.SEND_TIMEOUT) {
  311. Logger.error('请求超时: ${e.toString()}');
  312. } else if (e.type == DioErrorType.RECEIVE_TIMEOUT) {
  313. Logger.error('响应超时: ${e.toString()}');
  314. } else if (e.type == DioErrorType.RESPONSE) {
  315. Logger.error('出现异常: ${e.toString()}');
  316. } else if (e.type == DioErrorType.CANCEL) {
  317. Logger.error('请求取消: ${e.toString()}');
  318. } else {
  319. Logger.error('未知错误: ${e.toString()}');
  320. }
  321. }
  322. }
  323. /**
  324. * @description: 网络请求拦截器
  325. * @param {type}
  326. * @return:
  327. */
  328. class _NetInterceptors extends InterceptorsWrapper {
  329. @override
  330. Future onRequest(RequestOptions options) {
  331. Logger.net(options?.uri?.toString(), data: options.data.toString());
  332. // TODO 加密?
  333. return super.onRequest(options);
  334. }
  335. @override
  336. Future onResponse(Response response) {
  337. Logger.endNet(response?.request?.uri?.toString(),
  338. data: response?.data?.toString() ?? '');
  339. // TODO 解密?
  340. return super.onResponse(response);
  341. }
  342. @override
  343. Future onError(DioError err) {
  344. // Logger.error(err);
  345. return super.onError(err);
  346. }
  347. }