Jonny Borges
Committed by GitHub

Merge pull request #2448 from wheeOs/wheeOs-response-interception

response interception
1 import 'dart:async'; 1 import 'dart:async';
2 import 'dart:convert'; 2 import 'dart:convert';
  3 +import 'dart:io';
3 4
4 import '../src/certificates/certificates.dart'; 5 import '../src/certificates/certificates.dart';
5 import '../src/exceptions/exceptions.dart'; 6 import '../src/exceptions/exceptions.dart';
@@ -15,6 +16,8 @@ typedef Decoder<T> = T Function(dynamic data); @@ -15,6 +16,8 @@ typedef Decoder<T> = T Function(dynamic data);
15 16
16 typedef Progress = Function(double percent); 17 typedef Progress = Function(double percent);
17 18
  19 +typedef ResponseInterceptor<T> = Future<Response<T>?> Function(Request<T> request, Type targetType, HttpClientResponse response);
  20 +
18 class GetHttpClient { 21 class GetHttpClient {
19 String userAgent; 22 String userAgent;
20 String? baseUrl; 23 String? baseUrl;
@@ -29,6 +32,7 @@ class GetHttpClient { @@ -29,6 +32,7 @@ class GetHttpClient {
29 bool sendContentLength; 32 bool sendContentLength;
30 33
31 Decoder? defaultDecoder; 34 Decoder? defaultDecoder;
  35 + ResponseInterceptor? defaultResponseInterceptor;
32 36
33 Duration timeout; 37 Duration timeout;
34 38
@@ -99,6 +103,7 @@ class GetHttpClient { @@ -99,6 +103,7 @@ class GetHttpClient {
99 String method, 103 String method,
100 Map<String, dynamic>? query, 104 Map<String, dynamic>? query,
101 Decoder<T>? decoder, 105 Decoder<T>? decoder,
  106 + ResponseInterceptor<T>? responseInterceptor,
102 Progress? uploadProgress, 107 Progress? uploadProgress,
103 ) async { 108 ) async {
104 List<int>? bodyBytes; 109 List<int>? bodyBytes;
@@ -111,7 +116,7 @@ class GetHttpClient { @@ -111,7 +116,7 @@ class GetHttpClient {
111 116
112 if (body is FormData) { 117 if (body is FormData) {
113 bodyBytes = await body.toBytes(); 118 bodyBytes = await body.toBytes();
114 - _setContentLenght(headers, bodyBytes.length); 119 + headers['content-length'] = bodyBytes.length.toString();
115 headers['content-type'] = 120 headers['content-type'] =
116 'multipart/form-data; boundary=${body.boundary}'; 121 'multipart/form-data; boundary=${body.boundary}';
117 } else if (contentType != null && 122 } else if (contentType != null &&
@@ -159,6 +164,7 @@ class GetHttpClient { @@ -159,6 +164,7 @@ class GetHttpClient {
159 followRedirects: followRedirects, 164 followRedirects: followRedirects,
160 maxRedirects: maxRedirects, 165 maxRedirects: maxRedirects,
161 decoder: decoder, 166 decoder: decoder,
  167 + responseInterceptor: responseInterceptor
162 ); 168 );
163 } 169 }
164 170
@@ -267,6 +273,7 @@ class GetHttpClient { @@ -267,6 +273,7 @@ class GetHttpClient {
267 String? contentType, 273 String? contentType,
268 Map<String, dynamic>? query, 274 Map<String, dynamic>? query,
269 Decoder<T>? decoder, 275 Decoder<T>? decoder,
  276 + ResponseInterceptor<T>? responseInterceptor,
270 ) { 277 ) {
271 final headers = <String, String>{}; 278 final headers = <String, String>{};
272 _setSimpleHeaders(headers, contentType); 279 _setSimpleHeaders(headers, contentType);
@@ -277,12 +284,21 @@ class GetHttpClient { @@ -277,12 +284,21 @@ class GetHttpClient {
277 url: uri, 284 url: uri,
278 headers: headers, 285 headers: headers,
279 decoder: decoder ?? (defaultDecoder as Decoder<T>?), 286 decoder: decoder ?? (defaultDecoder as Decoder<T>?),
  287 + responseInterceptor: _responseInterceptor(responseInterceptor),
280 contentLength: 0, 288 contentLength: 0,
281 followRedirects: followRedirects, 289 followRedirects: followRedirects,
282 maxRedirects: maxRedirects, 290 maxRedirects: maxRedirects,
283 )); 291 ));
284 } 292 }
285 293
  294 + ResponseInterceptor<T>? _responseInterceptor<T>(ResponseInterceptor<T>? actual) {
  295 + if(actual != null) return actual;
  296 + final defaultInterceptor = defaultResponseInterceptor;
  297 + return defaultInterceptor != null
  298 + ? (request, targetType, response) async => await defaultInterceptor(request, targetType, response) as Response<T>?
  299 + : null;
  300 + }
  301 +
286 Future<Request<T>> _request<T>( 302 Future<Request<T>> _request<T>(
287 String? url, 303 String? url,
288 String method, { 304 String method, {
@@ -290,6 +306,7 @@ class GetHttpClient { @@ -290,6 +306,7 @@ class GetHttpClient {
290 required dynamic body, 306 required dynamic body,
291 required Map<String, dynamic>? query, 307 required Map<String, dynamic>? query,
292 Decoder<T>? decoder, 308 Decoder<T>? decoder,
  309 + ResponseInterceptor<T>? responseInterceptor,
293 required Progress? uploadProgress, 310 required Progress? uploadProgress,
294 }) { 311 }) {
295 return _requestWithBody<T>( 312 return _requestWithBody<T>(
@@ -299,6 +316,7 @@ class GetHttpClient { @@ -299,6 +316,7 @@ class GetHttpClient {
299 method, 316 method,
300 query, 317 query,
301 decoder ?? (defaultDecoder as Decoder<T>?), 318 decoder ?? (defaultDecoder as Decoder<T>?),
  319 + _responseInterceptor(responseInterceptor),
302 uploadProgress, 320 uploadProgress,
303 ); 321 );
304 } 322 }
@@ -308,6 +326,7 @@ class GetHttpClient { @@ -308,6 +326,7 @@ class GetHttpClient {
308 String? contentType, 326 String? contentType,
309 Map<String, dynamic>? query, 327 Map<String, dynamic>? query,
310 Decoder<T>? decoder, 328 Decoder<T>? decoder,
  329 + ResponseInterceptor<T>? responseInterceptor,
311 ) { 330 ) {
312 final headers = <String, String>{}; 331 final headers = <String, String>{};
313 _setSimpleHeaders(headers, contentType); 332 _setSimpleHeaders(headers, contentType);
@@ -318,8 +337,22 @@ class GetHttpClient { @@ -318,8 +337,22 @@ class GetHttpClient {
318 url: uri, 337 url: uri,
319 headers: headers, 338 headers: headers,
320 decoder: decoder ?? (defaultDecoder as Decoder<T>?), 339 decoder: decoder ?? (defaultDecoder as Decoder<T>?),
  340 + responseInterceptor: _responseInterceptor(responseInterceptor),
321 ); 341 );
322 } 342 }
  343 + Future<Response<T>> send<T>(Request<T> request) async {
  344 + try {
  345 + var response = await _performRequest<T>(() => Future.value(request));
  346 + return response;
  347 + } on Exception catch (e) {
  348 + if (!errorSafety) {
  349 + throw GetHttpException(e.toString());
  350 + }
  351 + return Future.value(Response<T>(
  352 + statusText: 'Can not connect to server. Reason: $e',
  353 + ));
  354 + }
  355 + }
323 356
324 Future<Response<T>> patch<T>( 357 Future<Response<T>> patch<T>(
325 String url, { 358 String url, {
@@ -328,6 +361,7 @@ class GetHttpClient { @@ -328,6 +361,7 @@ class GetHttpClient {
328 Map<String, String>? headers, 361 Map<String, String>? headers,
329 Map<String, dynamic>? query, 362 Map<String, dynamic>? query,
330 Decoder<T>? decoder, 363 Decoder<T>? decoder,
  364 + ResponseInterceptor<T>? responseInterceptor,
331 Progress? uploadProgress, 365 Progress? uploadProgress,
332 // List<MultipartFile> files, 366 // List<MultipartFile> files,
333 }) async { 367 }) async {
@@ -340,6 +374,7 @@ class GetHttpClient { @@ -340,6 +374,7 @@ class GetHttpClient {
340 body: body, 374 body: body,
341 query: query, 375 query: query,
342 decoder: decoder, 376 decoder: decoder,
  377 + responseInterceptor: responseInterceptor,
343 uploadProgress: uploadProgress, 378 uploadProgress: uploadProgress,
344 ), 379 ),
345 headers: headers, 380 headers: headers,
@@ -362,6 +397,7 @@ class GetHttpClient { @@ -362,6 +397,7 @@ class GetHttpClient {
362 Map<String, String>? headers, 397 Map<String, String>? headers,
363 Map<String, dynamic>? query, 398 Map<String, dynamic>? query,
364 Decoder<T>? decoder, 399 Decoder<T>? decoder,
  400 + ResponseInterceptor<T>? responseInterceptor,
365 Progress? uploadProgress, 401 Progress? uploadProgress,
366 // List<MultipartFile> files, 402 // List<MultipartFile> files,
367 }) async { 403 }) async {
@@ -374,6 +410,7 @@ class GetHttpClient { @@ -374,6 +410,7 @@ class GetHttpClient {
374 body: body, 410 body: body,
375 query: query, 411 query: query,
376 decoder: decoder, 412 decoder: decoder,
  413 + responseInterceptor: responseInterceptor,
377 uploadProgress: uploadProgress, 414 uploadProgress: uploadProgress,
378 ), 415 ),
379 headers: headers, 416 headers: headers,
@@ -397,6 +434,7 @@ class GetHttpClient { @@ -397,6 +434,7 @@ class GetHttpClient {
397 Map<String, String>? headers, 434 Map<String, String>? headers,
398 Map<String, dynamic>? query, 435 Map<String, dynamic>? query,
399 Decoder<T>? decoder, 436 Decoder<T>? decoder,
  437 + ResponseInterceptor<T>? responseInterceptor,
400 Progress? uploadProgress, 438 Progress? uploadProgress,
401 }) async { 439 }) async {
402 try { 440 try {
@@ -408,6 +446,7 @@ class GetHttpClient { @@ -408,6 +446,7 @@ class GetHttpClient {
408 query: query, 446 query: query,
409 body: body, 447 body: body,
410 decoder: decoder, 448 decoder: decoder,
  449 + responseInterceptor: responseInterceptor,
411 uploadProgress: uploadProgress, 450 uploadProgress: uploadProgress,
412 ), 451 ),
413 headers: headers, 452 headers: headers,
@@ -430,6 +469,7 @@ class GetHttpClient { @@ -430,6 +469,7 @@ class GetHttpClient {
430 Map<String, String>? headers, 469 Map<String, String>? headers,
431 Map<String, dynamic>? query, 470 Map<String, dynamic>? query,
432 Decoder<T>? decoder, 471 Decoder<T>? decoder,
  472 + ResponseInterceptor<T>? responseInterceptor,
433 Progress? uploadProgress, 473 Progress? uploadProgress,
434 }) async { 474 }) async {
435 try { 475 try {
@@ -441,6 +481,7 @@ class GetHttpClient { @@ -441,6 +481,7 @@ class GetHttpClient {
441 query: query, 481 query: query,
442 body: body, 482 body: body,
443 decoder: decoder, 483 decoder: decoder,
  484 + responseInterceptor: responseInterceptor,
444 uploadProgress: uploadProgress, 485 uploadProgress: uploadProgress,
445 ), 486 ),
446 headers: headers, 487 headers: headers,
@@ -462,10 +503,11 @@ class GetHttpClient { @@ -462,10 +503,11 @@ class GetHttpClient {
462 String? contentType, 503 String? contentType,
463 Map<String, dynamic>? query, 504 Map<String, dynamic>? query,
464 Decoder<T>? decoder, 505 Decoder<T>? decoder,
  506 + ResponseInterceptor<T>? responseInterceptor,
465 }) async { 507 }) async {
466 try { 508 try {
467 var response = await _performRequest<T>( 509 var response = await _performRequest<T>(
468 - () => _get<T>(url, contentType, query, decoder), 510 + () => _get<T>(url, contentType, query, decoder, responseInterceptor),
469 headers: headers, 511 headers: headers,
470 ); 512 );
471 return response; 513 return response;
@@ -546,10 +588,11 @@ class GetHttpClient { @@ -546,10 +588,11 @@ class GetHttpClient {
546 String? contentType, 588 String? contentType,
547 Map<String, dynamic>? query, 589 Map<String, dynamic>? query,
548 Decoder<T>? decoder, 590 Decoder<T>? decoder,
  591 + ResponseInterceptor<T>? responseInterceptor
549 }) async { 592 }) async {
550 try { 593 try {
551 var response = await _performRequest<T>( 594 var response = await _performRequest<T>(
552 - () async => _delete<T>(url, contentType, query, decoder), 595 + () async => _delete<T>(url, contentType, query, decoder, responseInterceptor),
553 headers: headers, 596 headers: headers,
554 ); 597 );
555 return response; 598 return response;
@@ -8,6 +8,7 @@ import '../../response/response.dart'; @@ -8,6 +8,7 @@ import '../../response/response.dart';
8 import '../interface/request_base.dart'; 8 import '../interface/request_base.dart';
9 import '../utils/body_decoder.dart'; 9 import '../utils/body_decoder.dart';
10 10
  11 +
11 /// A `dart:io` implementation of `HttpRequestBase`. 12 /// A `dart:io` implementation of `HttpRequestBase`.
12 class HttpRequestImpl extends HttpRequestBase { 13 class HttpRequestImpl extends HttpRequestBase {
13 io.HttpClient? _httpClient; 14 io.HttpClient? _httpClient;
@@ -57,6 +58,10 @@ class HttpRequestImpl extends HttpRequestBase { @@ -57,6 +58,10 @@ class HttpRequestImpl extends HttpRequestBase {
57 }); 58 });
58 59
59 final bodyBytes = (response); 60 final bodyBytes = (response);
  61 +
  62 + final interceptionResponse = await request.responseInterceptor?.call(request, T, response);
  63 + if(interceptionResponse != null) return interceptionResponse;
  64 +
60 final stringBody = await bodyBytesToString(bodyBytes, headers); 65 final stringBody = await bodyBytesToString(bodyBytes, headers);
61 66
62 final body = bodyDecoded<T>( 67 final body = bodyDecoded<T>(
@@ -13,6 +13,8 @@ class Request<T> { @@ -13,6 +13,8 @@ class Request<T> {
13 final Uri url; 13 final Uri url;
14 14
15 final Decoder<T>? decoder; 15 final Decoder<T>? decoder;
  16 +
  17 + final ResponseInterceptor<T>? responseInterceptor;
16 18
17 /// The Http Method from this [Request] 19 /// The Http Method from this [Request]
18 /// ex: `GET`,`POST`,`PUT`,`DELETE` 20 /// ex: `GET`,`POST`,`PUT`,`DELETE`
@@ -44,6 +46,7 @@ class Request<T> { @@ -44,6 +46,7 @@ class Request<T> {
44 required this.files, 46 required this.files,
45 required this.persistentConnection, 47 required this.persistentConnection,
46 required this.decoder, 48 required this.decoder,
  49 + this.responseInterceptor,
47 }); 50 });
48 51
49 factory Request({ 52 factory Request({
@@ -57,6 +60,7 @@ class Request<T> { @@ -57,6 +60,7 @@ class Request<T> {
57 FormData? files, 60 FormData? files,
58 bool persistentConnection = true, 61 bool persistentConnection = true,
59 Decoder<T>? decoder, 62 Decoder<T>? decoder,
  63 + ResponseInterceptor<T>? responseInterceptor,
60 }) { 64 }) {
61 if (followRedirects) { 65 if (followRedirects) {
62 assert(maxRedirects > 0); 66 assert(maxRedirects > 0);
@@ -72,6 +76,7 @@ class Request<T> { @@ -72,6 +76,7 @@ class Request<T> {
72 files: files, 76 files: files,
73 persistentConnection: persistentConnection, 77 persistentConnection: persistentConnection,
74 decoder: decoder, 78 decoder: decoder,
  79 + responseInterceptor: responseInterceptor
75 ); 80 );
76 } 81 }
77 82
@@ -87,6 +92,7 @@ class Request<T> { @@ -87,6 +92,7 @@ class Request<T> {
87 bool? persistentConnection, 92 bool? persistentConnection,
88 Decoder<T>? decoder, 93 Decoder<T>? decoder,
89 bool appendHeader = true, 94 bool appendHeader = true,
  95 + ResponseInterceptor<T>? responseInterceptor,
90 }) { 96 }) {
91 // If appendHeader is set to true, we will merge origin headers with that 97 // If appendHeader is set to true, we will merge origin headers with that
92 if (appendHeader && headers != null) { 98 if (appendHeader && headers != null) {
@@ -104,6 +110,7 @@ class Request<T> { @@ -104,6 +110,7 @@ class Request<T> {
104 files: files ?? this.files, 110 files: files ?? this.files,
105 persistentConnection: persistentConnection ?? this.persistentConnection, 111 persistentConnection: persistentConnection ?? this.persistentConnection,
106 decoder: decoder ?? this.decoder, 112 decoder: decoder ?? this.decoder,
  113 + responseInterceptor: responseInterceptor ?? this.responseInterceptor
107 ); 114 );
108 } 115 }
109 } 116 }