基础库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

net_util.dart 16 KiB

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