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

net_util.dart 10 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年前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  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.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'},
  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}) async {
  78. if (params == null) {
  79. params = {};
  80. }
  81. Map<String, dynamic> sign = await signParams(params);
  82. Response response;
  83. try {
  84. Dio dio = await NetUtil.getInstance().dio;
  85. response = await dio.post(path, data: sign);
  86. } on DioError catch (e) {
  87. _formatError(e);
  88. // Logger.error(e);
  89. }
  90. if (response == null) {
  91. return null;
  92. }
  93. if (response.statusCode != 200) {
  94. // 请求错误
  95. const bool inProduction = const bool.fromEnvironment("dart.vm.product");
  96. if (!inProduction) {
  97. Fluttertoast.showToast(msg: response.statusMessage);
  98. }
  99. // log.e(response.statusMessage);
  100. return null;
  101. }
  102. var result = response.data;
  103. if (result[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  104. GlobalConfig.RESPONSE_SUCCESS_CODE ||
  105. result[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  106. '${GlobalConfig.RESPONSE_SUCCESS_CODE}') {
  107. Logger.error(result[GlobalConfig.HTTP_RESPONSE_KEY_MSG]);
  108. Fluttertoast.showToast(
  109. msg: result[GlobalConfig.HTTP_RESPONSE_KEY_MSG],
  110. toastLength: Toast.LENGTH_SHORT,
  111. gravity: ToastGravity.BOTTOM,
  112. );
  113. }
  114. return result;
  115. }
  116. /// 异步请求
  117. static void request(String path,
  118. {NetMethod method = NetMethod.GET,
  119. Map<String, dynamic> params,
  120. Map<String, File> multiFiles,
  121. OnSuccess onSuccess,
  122. OnError onError,
  123. OnCache onCache}) async {
  124. if (params == null) {
  125. params = {};
  126. }
  127. // 根据请求参数,获取缓存的Key
  128. String cacheKey = _getRequestParamsCachedKey(params, path);
  129. // // 读取缓存回调
  130. await _onCallBackCacheData(onCache, cacheKey);
  131. // 参数签名
  132. Map<String, dynamic> sign = await signParams(params);
  133. Response response;
  134. try {
  135. Dio dio = await NetUtil.getInstance().dio;
  136. dio.request(path,
  137. data: sign, options: Options(method: enumToString(method)));
  138. } on DioError catch (e) {
  139. _formatError(e);
  140. }
  141. if (response == null) {
  142. return null;
  143. }
  144. var result = jsonDecode(response.data);
  145. //TODO 加密?
  146. if (result[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  147. GlobalConfig.RESPONSE_SUCCESS_CODE ||
  148. result[GlobalConfig.HTTP_RESPONSE_KEY_CODE] ==
  149. '${GlobalConfig.RESPONSE_SUCCESS_CODE}') {
  150. if (onSuccess != null) {
  151. onSuccess(result[GlobalConfig.HTTP_RESPONSE_KEY_DATA]);
  152. // 缓存返回的数据
  153. _setCallBackCacheData(cacheKey, response.data);
  154. }
  155. return;
  156. }
  157. Logger.error('error: ' + result[GlobalConfig.HTTP_RESPONSE_KEY_MSG]);
  158. Fluttertoast.showToast(
  159. msg: result[GlobalConfig.HTTP_RESPONSE_KEY_MSG],
  160. toastLength: Toast.LENGTH_SHORT,
  161. gravity: ToastGravity.BOTTOM,
  162. );
  163. if (onError != null) {
  164. onError(result[GlobalConfig.HTTP_RESPONSE_KEY_MSG] ?? '未知错误');
  165. }
  166. return;
  167. }
  168. /// 统一添加必要的参数
  169. static Future<Map<String, dynamic>> signParams(
  170. Map<String, dynamic> params) async {
  171. // 应用信息
  172. PackageInfo packageInfo = await PackageInfo.fromPlatform();
  173. // 原生传的信息
  174. Map setting = await NativeUtil.getSetting();
  175. if (Platform.isIOS) {
  176. IosDeviceInfo iosInfo = await DeviceInfoPlugin().iosInfo;
  177. // 设备
  178. params["platform"] = "ios";
  179. // 设备系统版本
  180. params["system_version"] = iosInfo?.systemVersion;
  181. // 设备型号 如:iPhone 11pro
  182. params['device_model'] = iosInfo?.name;
  183. // 设备型号,如:MLMF2LL/A
  184. // params['device_number'] = iosInfo?.model;
  185. // 设备ID
  186. params['device_id'] = iosInfo?.identifierForVendor;
  187. } else if (Platform.isAndroid) {
  188. AndroidDeviceInfo androidInfo = await DeviceInfoPlugin().androidInfo;
  189. // 设备
  190. params["platform"] = "android";
  191. // 设备系统版本
  192. params["system_version"] = "Android ${androidInfo?.version?.release}";
  193. // 设备型号 如:iPhone 11pro
  194. params['device_model'] = androidInfo?.model;
  195. // 设备型号,如:MLMF2LL/A
  196. // params['device_number'] = androidInfo?.id;
  197. // 设备Id
  198. params['device_id'] = androidInfo?.androidId;
  199. }
  200. // 应用版本号
  201. params["app_version"] = packageInfo.version;
  202. // 分辨率
  203. params["solution"] =
  204. "${window.physicalSize.width.floor()}*${window.physicalSize.height.floor()}";
  205. // 站长ID
  206. String masterId = setting['master_id'];
  207. if (null != null &&
  208. masterId != '' &&
  209. (!params.containsKey('master_id') || params['master_id'] == '')) {
  210. params['master_id'] = masterId;
  211. }
  212. // secret_key
  213. params['secret_key'] = setting['secret_key'];
  214. // token
  215. String token = setting['token'];
  216. if (token != null &&
  217. token != '' &&
  218. (!params.containsKey('token') || params['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.RESPONSE_SUCCESS_CODE) &&
  246. (cacheMap[GlobalConfig.RESPONSE_SUCCESS_CODE] ==
  247. GlobalConfig.RESPONSE_SUCCESS_CODE ||
  248. cacheMap[GlobalConfig.RESPONSE_SUCCESS_CODE] ==
  249. '${GlobalConfig.RESPONSE_SUCCESS_CODE}') &&
  250. null != cacheMap[GlobalConfig.HTTP_RESPONSE_KEY_DATA]) {
  251. onCache(cacheMap['data']);
  252. return;
  253. }
  254. return;
  255. }
  256. /// 缓存请求成功的数据
  257. static void _setCallBackCacheData(String cacheKey, String value) async {
  258. SharedPreferencesUtil.setNetCacheResult(cacheKey, value);
  259. }
  260. /// 根据请求参数,获取缓存的Key
  261. static String _getRequestParamsCachedKey(
  262. Map<String, dynamic> map, String path) {
  263. if (EmptyUtil.isEmpty(map)) {
  264. return EncodeUtil.generateMd5(path);
  265. }
  266. return EncodeUtil.generateMd5(path + map.toString());
  267. }
  268. /// 签名
  269. static String signWithArray(List<String> params) {
  270. // 字母升序
  271. params.sort();
  272. String result = "";
  273. // result += secret_key;
  274. params.forEach((param) {
  275. result += param;
  276. });
  277. // result += secret_key;
  278. return EncodeUtil.generateMd5(result);
  279. }
  280. /*
  281. * error统一处理
  282. */
  283. static void _formatError(DioError e) {
  284. if (e.type == DioErrorType.CONNECT_TIMEOUT) {
  285. Logger.error('连接超时: ${e.toString()}');
  286. } else if (e.type == DioErrorType.SEND_TIMEOUT) {
  287. Logger.error('请求超时: ${e.toString()}');
  288. } else if (e.type == DioErrorType.RECEIVE_TIMEOUT) {
  289. Logger.error('响应超时: ${e.toString()}');
  290. } else if (e.type == DioErrorType.RESPONSE) {
  291. Logger.error('出现异常: ${e.toString()}');
  292. } else if (e.type == DioErrorType.CANCEL) {
  293. Logger.error('请求取消: ${e.toString()}');
  294. } else {
  295. Logger.error('未知错误: ${e.toString()}');
  296. }
  297. }
  298. }
  299. /**
  300. * @description: 网络请求拦截器
  301. * @param {type}
  302. * @return:
  303. */
  304. class _NetInterceptors extends InterceptorsWrapper {
  305. @override
  306. Future onRequest(RequestOptions options) {
  307. Logger.net(options?.path, data: options.data.toString());
  308. // TODO 加密?
  309. return super.onRequest(options);
  310. }
  311. @override
  312. Future onResponse(Response response) {
  313. Logger.endNet(response?.statusCode?.toString(),
  314. data: response?.data?.toString() ?? '');
  315. // TODO 解密?
  316. return super.onResponse(response);
  317. }
  318. @override
  319. Future onError(DioError err) {
  320. // Logger.error(err);
  321. return super.onError(err);
  322. }
  323. }