基础库
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

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年前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. import 'dart:convert';
  2. import 'dart:io';
  3. import 'dart:ui';
  4. import 'package:device_info/device_info.dart';
  5. import 'package:dio/adapter.dart';
  6. import 'package:dio/dio.dart';
  7. import 'package:fluttertoast/fluttertoast.dart';
  8. import 'package:package_info/package_info.dart';
  9. import 'package:zhiying_comm/util/empty_util.dart';
  10. import 'package:zhiying_comm/zhiying_comm.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.112:8888');
  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. validateStatus: (_) {
  57. return true;
  58. }));
  59. _dio.interceptors.add(_NetInterceptors());
  60. // _dio.interceptors.add(LogInterceptor());
  61. const bool inProduction = const bool.fromEnvironment("dart.vm.product");
  62. if (proxyUrl != null && proxyUrl != '' && !inProduction) {
  63. _setProxy(proxyUrl);
  64. }
  65. }
  66. /// 设置代理
  67. void _setProxy(String proxyUrl) {
  68. (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
  69. (HttpClient client) {
  70. client.findProxy = (uri) {
  71. return "PROXY $proxyUrl";
  72. };
  73. client.badCertificateCallback =
  74. (X509Certificate cert, String host, int port) => true;
  75. };
  76. }
  77. /// 同步请求
  78. static Future<dynamic> post(String path,
  79. {Map<String, dynamic> params,
  80. NetMethod method = NetMethod.POST,
  81. bool cache = false}) async {
  82. if (params == null) {
  83. params = {};
  84. }
  85. // 根据请求参数,获取缓存的Key
  86. String cacheKey = getRequestParamsCachedKey(params, path);
  87. // 参数签名 TODO 加密?
  88. Map<String, dynamic> sign = await signParams(params);
  89. Response response;
  90. try {
  91. String token = await SharedPreferencesUtil.getStringValue(
  92. GlobalConfig.SHARED_KEY_TOKEN) ??
  93. '';
  94. Dio dio = await NetUtil.getInstance().dio;
  95. response = await dio.request(path,
  96. data: sign,
  97. options:
  98. Options(method: enumToString(method), headers: {'token': token}));
  99. } on DioError catch (e) {
  100. _formatError(e);
  101. }
  102. try {
  103. var result =
  104. response.data is Map ? response.data : jsonDecode(response.data);
  105. // TODO 解密?
  106. if (isSuccess(result)) {
  107. // 缓存成功的数据
  108. if (cache) {
  109. _setCallBackCacheData(cacheKey,
  110. 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. return result;
  124. }
  125. } catch (e) {
  126. return null;
  127. }
  128. }
  129. /// 异步请求
  130. static void request(String path,
  131. {NetMethod method = NetMethod.GET,
  132. Map<String, dynamic> params,
  133. OnSuccess onSuccess,
  134. OnError onError,
  135. OnCache onCache}) async {
  136. if (params == null) {
  137. params = {};
  138. }
  139. // 根据请求参数,获取缓存的Key
  140. String cacheKey = getRequestParamsCachedKey(params, path);
  141. // // 读取缓存回调
  142. if (onCache != null) {
  143. await _onCallBackCacheData(onCache, cacheKey);
  144. }
  145. try {
  146. Map result = await NetUtil.post(path, method: method, params: params);
  147. // TODO 解密?
  148. if (isSuccess(result)) {
  149. if (onSuccess != null) {
  150. onSuccess(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]);
  151. }
  152. return;
  153. }
  154. if (onError != null) {
  155. onError(!EmptyUtil.isEmpty(result)
  156. ? !EmptyUtil.isEmpty(result[GlobalConfig.HTTP_RESPONSE_KEY_MSG])
  157. ? result[GlobalConfig.HTTP_RESPONSE_KEY_MSG]
  158. : '未知错误'
  159. : '未知错误');
  160. }
  161. } catch (e) {
  162. Logger.error('error: ' + e.toString());
  163. if (onError != null) {
  164. onError(e?.toString() ?? '未知错误');
  165. }
  166. }
  167. return;
  168. }
  169. /// 统一添加必要的参数
  170. static Future<Map<String, dynamic>> signParams(
  171. Map<String, dynamic> params) async {
  172. // 应用信息
  173. PackageInfo packageInfo = await PackageInfo.fromPlatform();
  174. // 原生传的信息
  175. Map setting = await NativeUtil.getSetting();
  176. if (Platform.isIOS) {
  177. IosDeviceInfo iosInfo = await DeviceInfoPlugin().iosInfo;
  178. // 设备
  179. params["platform"] = "ios";
  180. // 设备系统版本
  181. params["system_version"] = iosInfo?.systemVersion;
  182. // 设备型号 如:iPhone 11pro
  183. params['device_model'] = iosInfo?.name;
  184. // 设备型号,如:MLMF2LL/A
  185. // params['device_number'] = iosInfo?.model;
  186. // 设备ID
  187. params['device_id'] = iosInfo?.identifierForVendor;
  188. } else if (Platform.isAndroid) {
  189. AndroidDeviceInfo androidInfo = await DeviceInfoPlugin().androidInfo;
  190. // 设备
  191. params["platform"] = "android";
  192. // 设备系统版本
  193. params["system_version"] = "Android ${androidInfo?.version?.release}";
  194. // 设备型号 如:iPhone 11pro
  195. params['device_model'] = androidInfo?.model;
  196. // 设备型号,如:MLMF2LL/A
  197. // params['device_number'] = androidInfo?.id;
  198. // 设备Id
  199. params['device_id'] = androidInfo?.androidId;
  200. }
  201. // 应用版本号
  202. params["app_version"] = packageInfo.version;
  203. // 分辨率
  204. params["solution"] =
  205. "${window.physicalSize.width.floor()}*${window.physicalSize.height.floor()}";
  206. // 站长ID
  207. String masterId = setting['master_id'];
  208. if (null != masterId &&
  209. masterId != '' &&
  210. (!params.containsKey('master_id') || params['master_id'] == '')) {
  211. params['master_id'] = masterId;
  212. }
  213. // secret_key
  214. params['secret_key'] = setting['secret_key'];
  215. // token 读取SP缓存中的用户token
  216. String token = await SharedPreferencesUtil.getStringValue(
  217. GlobalConfig.SHARED_KEY_TOKEN);
  218. if (!EmptyUtil.isEmpty(token)) {
  219. params['token'] = token;
  220. }
  221. // 当前时间戳:秒
  222. params["time"] = TimeUtil.getNowTime();
  223. // 过滤空字段,过滤首尾空格
  224. Map<String, dynamic> filters = Map<String, dynamic>();
  225. params.forEach((key, value) {
  226. if (key != '' && value != '') {
  227. filters[key] = (value is String) ? value.trim() : value;
  228. }
  229. });
  230. params = filters;
  231. List<String> list = List<String>();
  232. params.forEach((key, value) {
  233. list.add(key.toString() + value.toString());
  234. });
  235. params["sign"] = signWithArray(list);
  236. return params;
  237. }
  238. /// 获取请求缓存成功的数据
  239. static Future<void> _onCallBackCacheData(
  240. OnCache onCache, String cacheKey) async {
  241. // 读取缓存
  242. Map<String, dynamic> cacheMap =
  243. await SharedPreferencesUtil.getNetCacheResult(cacheKey);
  244. if (!EmptyUtil.isEmpty(cacheMap) &&
  245. cacheMap.containsKey(GlobalConfig.HTTP_RESPONSE_KEY_CODE) &&
  246. (cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  247. GlobalConfig.RESPONSE_SUCCESS_CODE ||
  248. cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  249. '${GlobalConfig.RESPONSE_SUCCESS_CODE}') &&
  250. !EmptyUtil.isEmpty(cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) {
  251. onCache(cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA]);
  252. return;
  253. }
  254. return;
  255. }
  256. /// 缓存请求成功的数据
  257. static void _setCallBackCacheData(String cacheKey, String value) async {
  258. SharedPreferencesUtil.setNetCacheResult(cacheKey, value);
  259. }
  260. /// 根据请求参数,获取缓存的数据
  261. static Future<dynamic> getRequestCachedData(String url,
  262. {Map<String, dynamic> params}) async {
  263. Map<String, dynamic> cacheMap =
  264. await SharedPreferencesUtil.getNetCacheResult(
  265. getRequestParamsCachedKey(params, url));
  266. if (!EmptyUtil.isEmpty(cacheMap) &&
  267. cacheMap.containsKey(GlobalConfig.HTTP_RESPONSE_KEY_CODE) &&
  268. (cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  269. GlobalConfig.RESPONSE_SUCCESS_CODE ||
  270. cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  271. '${GlobalConfig.RESPONSE_SUCCESS_CODE}') &&
  272. !EmptyUtil.isEmpty(cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA])) {
  273. return cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA];
  274. }
  275. return null;
  276. }
  277. /// 判断后台返回是否成功
  278. static bool isSuccess(Map<String, dynamic> data) {
  279. try {
  280. if (!EmptyUtil.isEmpty(data) &&
  281. (data[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  282. GlobalConfig.RESPONSE_SUCCESS_CODE ||
  283. data[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  284. '${GlobalConfig.RESPONSE_SUCCESS_CODE}')) {
  285. return true;
  286. }
  287. } catch (e) {
  288. return false;
  289. }
  290. return false;
  291. }
  292. /// 根据请求参数,获取缓存的Key
  293. static String getRequestParamsCachedKey(
  294. Map<String, dynamic> map, String path) {
  295. if (EmptyUtil.isEmpty(map)) {
  296. return EncodeUtil.generateMd5(path);
  297. }
  298. return EncodeUtil.generateMd5(path + map.toString());
  299. }
  300. // 七牛云文件上传
  301. static Future uploadFile(String url, File file,
  302. {String method = 'POST',
  303. Map<String, dynamic> params,
  304. OnSuccess onSuccess,
  305. OnError onError}) async {
  306. if (params == null) {
  307. params = {};
  308. }
  309. Dio dio = Dio(BaseOptions(
  310. method: "post",
  311. connectTimeout: 15000,
  312. receiveTimeout: 15000,
  313. contentType: Headers.jsonContentType,
  314. followRedirects: true,
  315. ));
  316. params['file'] = await MultipartFile.fromFile(file.path);
  317. FormData format = FormData.fromMap(params);
  318. return dio.request(url, data: format, options: Options(method: method));
  319. }
  320. /// 签名
  321. static String signWithArray(List<String> params) {
  322. // 字母升序
  323. params.sort();
  324. String result = "";
  325. // result += secret_key;
  326. params.forEach((param) {
  327. result += param;
  328. });
  329. // result += secret_key;
  330. return EncodeUtil.generateMd5(result);
  331. }
  332. /*
  333. * error统一处理
  334. */
  335. static void _formatError(DioError e) {
  336. if (e.type == DioErrorType.CONNECT_TIMEOUT) {
  337. Logger.error('连接超时: ${e.toString()}');
  338. } else if (e.type == DioErrorType.SEND_TIMEOUT) {
  339. Logger.error('请求超时: ${e.toString()}');
  340. } else if (e.type == DioErrorType.RECEIVE_TIMEOUT) {
  341. Logger.error('响应超时: ${e.toString()}');
  342. } else if (e.type == DioErrorType.RESPONSE) {
  343. Logger.error('出现异常: ${e.toString()}');
  344. } else if (e.type == DioErrorType.CANCEL) {
  345. Logger.error('请求取消: ${e.toString()}');
  346. } else {
  347. Logger.error('未知错误: ${e.toString()}');
  348. }
  349. }
  350. }
  351. /**
  352. * @description: 网络请求拦截器
  353. * @param {type}
  354. * @return:
  355. */
  356. class _NetInterceptors extends InterceptorsWrapper {
  357. @override
  358. Future onRequest(RequestOptions options) {
  359. Logger.net(options?.uri?.toString(), data: options.data.toString());
  360. // TODO 加密?
  361. return super.onRequest(options);
  362. }
  363. @override
  364. Future onResponse(Response response) {
  365. Logger.endNet(response?.request?.uri?.toString(),
  366. data: response?.data?.toString() ?? '');
  367. // TODO 解密?
  368. return super.onResponse(response);
  369. }
  370. @override
  371. Future onError(DioError err) {
  372. // Logger.error(err);
  373. return super.onError(err);
  374. }
  375. }