1- import 'package:get/get.dart' ;
1+ import 'dart:convert' ;
2+ import 'dart:typed_data' ;
23import 'package:dio/dio.dart' ;
4+ export 'package:dio/dio.dart' ;
5+ import 'package:crypto/crypto.dart' ;
6+ import 'package:encrypt/encrypt.dart' ;
7+ import 'package:get/get.dart' hide Response;
38import 'package:get_storage/get_storage.dart' ;
49import 'package:dompet/configure/fluttertoast.dart' ;
10+ import 'package:dompet/extension/bool.dart' ;
511import 'package:dompet/logger/printer.dart' ;
612import 'package:dompet/logger/logger.dart' ;
713
8- export 'package:dio/dio.dart' ;
9-
1014final fetch = DioManager .fetch;
1115
1216class DioManager {
17+ static String encrypt (String data, [String ? key]) {
18+ final code = key.bv ? key! : 'XDompetTH90JK373' ;
19+ final encrypter = Encrypter (AES (Key .fromUtf8 (code)));
20+ final encrypted = encrypter.encrypt (data, iv: IV (utf8.encode (code)));
21+ return encrypted.base64;
22+ }
23+
24+ static String decrypt (String data, [String ? key]) {
25+ final code = key.bv ? key! : 'XDompetTH90JK373' ;
26+ final encrypter = Encrypter (AES (Key .fromUtf8 (code)));
27+ final decrypted = encrypter.decrypt64 (data, iv: IV (utf8.encode (code)));
28+ return decrypted;
29+ }
30+
31+ static String encode (dynamic data, [String ? type]) {
32+ if (type == null && data is Uint8List ) {
33+ return base64Encode (data);
34+ }
35+
36+ if (type == 'base64Encode' ) {
37+ return base64Encode (data);
38+ }
39+
40+ return jsonEncode (data);
41+ }
42+
43+ static dynamic decode (dynamic data, [String ? type]) {
44+ if (type == 'base64Decode' ) {
45+ return base64Decode (data);
46+ }
47+
48+ return jsonDecode (data);
49+ }
50+
51+ static dynamic recorder (DioException err, [Response ? res]) {
52+ err.requestOptions.extra.remove ('code' );
53+ err.requestOptions.extra.remove ('cache' );
54+ err.requestOptions.extra.remove ('retry' );
55+ err.requestOptions.extra.remove ('logger' );
56+
57+ logger.error (
58+ {
59+ 'dio' : 'network request failed' ,
60+ 'path' : err.requestOptions.uri,
61+ 'method' : err.requestOptions.method,
62+ 'headers' : err.requestOptions.headers,
63+ 'bodyData' : err.requestOptions.data,
64+ 'queryExtra' : err.requestOptions.extra,
65+ 'queryParams' : err.requestOptions.queryParameters,
66+ 'responseData' : err.response? .data,
67+ 'responseExtra' : err.response? .extra,
68+ 'responseError' : err.message ?? err.error,
69+ 'responseUsage' : res != null ? 'Cached' : 'Unknown' ,
70+ },
71+ err.error,
72+ err.stackTrace,
73+ );
74+ }
75+
76+ static Future <Response ?> query (DioException err) async {
77+ try {
78+ final uri = err.requestOptions.uri.toString ();
79+ final code = err.requestOptions.extra['code' ] ?? uri;
80+ final cache = err.requestOptions.extra['cache' ] ?? false ;
81+ final unique = md5.convert (utf8.encode (code ?? uri)).toString ();
82+ final source = cache ? GetStorage ().read ('dio_cache_$unique ' ) : null ;
83+ final storage = cache ? jsonDecode (decrypt (source)) : null ;
84+
85+ if (cache == true && storage != null ) {
86+ final options = storage['requestOptions' ];
87+
88+ final headers = storage['headers' ].map ((k, v) {
89+ return MapEntry ('$k ' , List <String >.from (v));
90+ });
91+
92+ final response = Response (
93+ requestOptions: RequestOptions (
94+ path: options['path' ],
95+ extra: Map .from (options['extra' ]),
96+ method: options['method' ] ?? 'GET' ,
97+ queryParameters: Map .from (options['queryParameters' ]),
98+ responseType: ResponseType .values[options['responseType' ]],
99+ contentType: options['contentType' ],
100+ ),
101+ data: DioManager .decode (storage['data' ], storage['decode' ]),
102+ extra: Map .from (storage['extra' ])..addAll ({'cached' : 'local' }),
103+ headers: Headers .fromMap (Map <String , List <String >>.from (headers)),
104+ statusMessage: storage['statusMessage' ],
105+ statusCode: storage['statusCode' ],
106+ );
107+
108+ return response;
109+ }
110+ } catch (e) {
111+ /* e */
112+ }
113+
114+ return null ;
115+ }
116+
117+ static Future <Response > cache (Response res) async {
118+ if (res.data is ResponseBody ) {
119+ return res;
120+ }
121+
122+ try {
123+ final uri = res.requestOptions.uri.toString ();
124+ final code = res.requestOptions.extra['code' ] ?? uri;
125+ final cache = res.requestOptions.extra['cache' ] ?? false ;
126+ final unique = md5.convert (utf8.encode (code ?? uri)).toString ();
127+
128+ res.requestOptions.extra.remove ('logger' );
129+ res.requestOptions.extra.remove ('retry' );
130+ res.requestOptions.extra.remove ('cache' );
131+ res.requestOptions.extra.remove ('code' );
132+
133+ if (cache == true && unique.isNotEmpty) {
134+ final encode = jsonEncode ({
135+ "requestOptions" : {
136+ "path" : res.requestOptions.path,
137+ "extra" : res.requestOptions.extra,
138+ "method" : res.requestOptions.method,
139+ "contentType" : res.requestOptions.contentType,
140+ "responseType" : res.requestOptions.responseType.index,
141+ "queryParameters" : res.requestOptions.queryParameters,
142+ },
143+ "data" : DioManager .encode (res.data),
144+ "decode" : res.data is Uint8List ? 'base64Decode' : null ,
145+ "statusMessage" : res.statusMessage,
146+ "statusCode" : res.statusCode,
147+ "headers" : res.headers.map,
148+ "extra" : res.extra,
149+ });
150+
151+ await GetStorage ().write ('dio_cache_$unique ' , encrypt (encode));
152+ }
153+ } catch (e) {
154+ /* e */
155+ }
156+
157+ return res;
158+ }
159+
13160 static Dio create (BaseOptions option) {
14161 final dio = Dio (
15162 BaseOptions (
@@ -34,49 +181,76 @@ class DioManager {
34181
35182 dio.interceptors.add (
36183 InterceptorsWrapper (
37- onRequest: (options, handler) {
184+ onRequest: (options, handler) async {
38185 options.headers['token' ] ?? = GetStorage ().read ('token' );
39- options.extra['request' ] ?? = dateAndTime ();
186+ options.extra['requested' ] ?? = dateAndTime ();
187+ options.extra['responsed' ] = null ;
188+ options.extra['cache' ] ?? = false ;
189+ options.extra['retry' ] ?? = - 1 ;
40190 return handler.next (options);
41191 },
42- onResponse: (response, handler) {
43- response.requestOptions.extra['response' ] ?? = dateAndTime ();
44- response.requestOptions.extra['request' ] ?? = dateAndTime ();
45- return handler.next (response);
192+ onResponse: (response, handler) async {
193+ response.requestOptions.extra['requested' ] ?? = dateAndTime ();
194+ response.requestOptions.extra['responsed' ] ?? = dateAndTime ();
195+
196+ if (response.statusCode != 200 || response.data is ResponseBody ) {
197+ response.requestOptions.extra['cache' ] = false ;
198+ }
199+
200+ if (response.requestOptions.extra['cache' ] == true ) {
201+ await DioManager .cache (response);
202+ }
203+
204+ response.requestOptions.extra.remove ('logger' );
205+ response.requestOptions.extra.remove ('retry' );
206+ response.requestOptions.extra.remove ('cache' );
207+ response.requestOptions.extra.remove ('code' );
208+
209+ return handler.resolve (response);
46210 },
47- onError: (error, handler) {
211+ ),
212+ );
213+
214+ dio.interceptors.add (
215+ InterceptorsWrapper (
216+ onError: (error, handler) async {
217+ String ? message;
218+ Response ? response;
219+
48220 switch (error.type) {
49221 case DioExceptionType .sendTimeout:
50222 case DioExceptionType .receiveTimeout:
223+ case DioExceptionType .connectionError:
51224 case DioExceptionType .connectionTimeout:
52- if (error.requestOptions.extra['retry' ] < 1 ) {
53- Toaster . error (message : 'System_Network_Timeout' .tr );
225+ if (error.requestOptions.extra['retry' ] >= 1 ) {
226+ return handler. next (error );
54227 }
55228
229+ message = 'System_Network_Timeout' ;
56230 break ;
57231
58232 default :
59- Toaster .error (message: 'System_Abnormality' .tr);
233+ message = 'System_Abnormality' ;
234+ break ;
235+ }
236+
237+ error.requestOptions.extra['requested' ] ?? = dateAndTime ();
238+ error.requestOptions.extra['responsed' ] ?? = dateAndTime ();
239+
240+ if (error.requestOptions.extra['cache' ] == true ) {
241+ response = await DioManager .query (error);
60242 }
61243
62244 if (error.requestOptions.extra['logger' ] == true ) {
63- error.requestOptions.extra['response' ] ?? = dateAndTime ();
64- error.requestOptions.extra['request' ] ?? = dateAndTime ();
65-
66- final message = {
67- 'dio' : 'network request failed' ,
68- 'path' : '${error .requestOptions .uri }' ,
69- 'method' : error.requestOptions.method,
70- 'headers' : '${error .requestOptions .headers }' ,
71- 'bodyData' : '${error .requestOptions .data ?? '' }' ,
72- 'queryParams' : '${error .requestOptions .queryParameters }' ,
73- 'requestTime' : '${error .requestOptions .extra ['request' ]}' ,
74- 'responseTime' : '${error .requestOptions .extra ['response' ]}' ,
75- 'responseData' : '${error .response ?.data ?? '' }' ,
76- 'responseError' : '${error .message ?? error .error ?? '' }' ,
77- };
78-
79- logger.error (message, error.error, error.stackTrace);
245+ recorder (error, response);
246+ }
247+
248+ if (response is ! Response ) {
249+ Toaster .error (message: message.tr);
250+ }
251+
252+ if (response is Response ) {
253+ return handler.resolve (response);
80254 }
81255
82256 return handler.next (error);
@@ -92,10 +266,9 @@ class DioManager {
92266 switch (error.type) {
93267 case DioExceptionType .sendTimeout:
94268 case DioExceptionType .receiveTimeout:
269+ case DioExceptionType .connectionError:
95270 case DioExceptionType .connectionTimeout:
96271 error.requestOptions.extra['retry' ] -= 1 ;
97- error.requestOptions.extra['request' ] = null ;
98- error.requestOptions.extra['response' ] = null ;
99272 await Future .delayed (Duration (milliseconds: 100 ));
100273 return dio.fetch (error.requestOptions).then (handler.resolve);
101274
@@ -121,7 +294,7 @@ class DioManager {
121294 sendTimeout: const Duration (milliseconds: 3000 ),
122295 connectTimeout: const Duration (milliseconds: 3000 ),
123296 receiveTimeout: const Duration (milliseconds: 3000 ),
124- extra: {'logger' : true , 'retry' : 0 },
297+ extra: {'logger' : true , 'cache' : false , ' retry': - 1 },
125298 ),
126299 );
127300}
0 commit comments