send static method
Future<UHttpClientResponse>
send({
- required String method,
- required String endpoint,
- required dynamic onSuccess(),
- required dynamic onError(),
- required dynamic onException(),
- Map<
String, String> ? headers, - Map<
String, dynamic> ? queryParams, - dynamic body,
- URequestBodyType bodyType = URequestBodyType.json,
- String? noNetworkMessage,
- String? unexpectedErrorMessage,
- bool offline = false,
- Duration? cacheDuration,
- int retryAmount = 3,
- Duration timeout = const Duration(seconds: 30),
- void onProgress(
- int percent
- bool isRetryAfterRefresh = false,
Implementation
static Future<UHttpClientResponse> send({
required String method,
required String endpoint,
required Function(Response) onSuccess,
required Function(Response) onError,
required Function(String) onException,
Map<String, String>? headers,
Map<String, dynamic>? queryParams,
dynamic body,
URequestBodyType bodyType = URequestBodyType.json,
String? noNetworkMessage,
String? unexpectedErrorMessage,
bool offline = false,
Duration? cacheDuration,
int retryAmount = 3,
Duration timeout = const Duration(seconds: 30),
void Function(int percent)? onProgress,
bool isRetryAfterRefresh = false,
}) async {
int lastPercent = -1;
void report(num percent) {
if (onProgress == null) return;
final int p = percent.clamp(0, 100).toInt();
if (p <= lastPercent) return;
lastPercent = p;
onProgress(p);
}
final bool hasNetworkConnection = await UNetwork.hasAnyConnection();
if (!hasNetworkConnection && offline == false) {
final String message = noNetworkMessage ?? U.s.connectionToNetworkWasNotPossible;
onException(message);
return UHttpClientResponse(exception: message);
}
final Uri uri = _buildUri(endpoint, queryParams);
final bool cacheEnabled = offline || cacheDuration != null;
final String cacheKey = cacheEnabled ? _cacheKey(method, uri, body) : "";
if (offline) {
final String? cachedData = await _readCache(cacheKey);
if (cachedData != null) {
final Response response = Response(cachedData, 200, request: Request(method, uri));
onSuccess(response);
return UHttpClientResponse(response: cachedData);
}
}
final Response response;
try {
final Request request = Request(method, uri);
if (headers != null) request.headers.addAll(headers);
if (body != null) {
if (bodyType == URequestBodyType.json) {
if (body is Map) {
request.body = jsonEncode(removeNullEntries(body));
request.headers["Content-Type"] = "application/json";
request.headers["Locale"] = UApp.locale();
request.headers["Timezone"] = UTimezone.getLocalTimezone();
} else if (body is String) {
request.body = body;
} else if (body is List<int>) {
request.bodyBytes = body;
}
} else if (bodyType == URequestBodyType.formData && body is Map<String, dynamic>) {
final Map<String, String> formFields = <String, String>{};
body.forEach((String key, dynamic value) {
if (value != null) {
formFields[key] = value.toString();
}
});
request.bodyFields = formFields;
request.headers["Content-Type"] = "application/x-www-form-urlencoded";
}
}
final List<int> uploadBytes = request.bodyBytes;
final bool hasUpload = uploadBytes.length >= 64 * 1024;
final int uploadWeight = hasUpload ? 50 : 0;
final int downloadBand = 100 - uploadWeight;
final BaseRequest outgoing = !hasUpload || onProgress == null
? request
: (_UProgressRequest(method, uri, uploadBytes, (int uploadPercent) => report((uploadPercent / 100 * uploadWeight).round()))
..headers.addAll(request.headers)
..contentLength = uploadBytes.length);
final StreamedResponse streamed = await _client.send(outgoing).timeout(timeout);
report(uploadWeight);
final int? totalBytes = streamed.contentLength;
final BytesBuilder receivedBuilder = BytesBuilder(copy: false);
int receivedCount = 0;
Timer? estimateTicker;
if (onProgress != null && (totalBytes == null || totalBytes <= 0)) {
const int tauMs = 7000;
final Stopwatch sw = Stopwatch()..start();
estimateTicker = Timer.periodic(const Duration(milliseconds: 200), (Timer _) {
final double frac = sw.elapsedMilliseconds / (sw.elapsedMilliseconds + tauMs);
report(uploadWeight + (frac * downloadBand).round().clamp(0, downloadBand - 1));
});
}
try {
await for (final List<int> chunk in streamed.stream) {
receivedBuilder.add(chunk);
receivedCount += chunk.length;
if (totalBytes != null && totalBytes > 0) report(uploadWeight + (receivedCount / totalBytes * downloadBand).round());
}
} finally {
estimateTicker?.cancel();
}
report(100);
response = Response.bytes(
receivedBuilder.takeBytes(),
streamed.statusCode,
request: streamed.request,
headers: streamed.headers,
reasonPhrase: streamed.reasonPhrase,
);
} catch (e, stack) {
developer.log(e.toString(), stackTrace: stack);
if (retryAmount > 0) {
final int attempt = retryAmount < 1 ? 1 : (4 - retryAmount).clamp(1, 4);
await Future<void>.delayed(Duration(milliseconds: 300 * attempt));
return send(
method: method,
endpoint: endpoint,
onSuccess: onSuccess,
onError: onError,
onException: onException,
headers: headers,
offline: offline,
cacheDuration: cacheDuration,
body: body,
bodyType: bodyType,
noNetworkMessage: noNetworkMessage,
queryParams: queryParams,
retryAmount: retryAmount - 1,
unexpectedErrorMessage: unexpectedErrorMessage,
timeout: timeout,
onProgress: onProgress,
isRetryAfterRefresh: isRetryAfterRefresh,
);
} else {
final String message = unexpectedErrorMessage ?? U.s.unexpectedErrorPleaseTryAgain;
onException(message);
return UHttpClientResponse(exception: message);
}
}
if (kDebugMode) response.prettyLog(params: jsonEncode(body));
try {
if (response.statusCode >= 200 && response.statusCode <= 299) {
if (cacheEnabled) await _writeCache(cacheKey, response.body, cacheDuration);
onSuccess(response);
return UHttpClientResponse(response: response.body);
} else if (response.statusCode == Usc.expiredToken.number && !isRetryAfterRefresh && !endpoint.contains("/auth/")) {
final bool refreshed = await _refreshToken();
if (refreshed) {
final dynamic retryBody = body is Map ? (Map<String, dynamic>.from(body)..["token"] = ULocalStorage.getToken()) : body;
return await send(
method: method,
endpoint: endpoint,
onSuccess: onSuccess,
onError: onError,
onException: onException,
headers: headers,
queryParams: queryParams,
body: retryBody,
bodyType: bodyType,
noNetworkMessage: noNetworkMessage,
unexpectedErrorMessage: unexpectedErrorMessage,
offline: offline,
cacheDuration: cacheDuration,
retryAmount: retryAmount,
timeout: timeout,
onProgress: onProgress,
isRetryAfterRefresh: true,
);
}
await onAuthFailed?.call();
onError(response);
return UHttpClientResponse(error: response.body);
} else {
onError(response);
return UHttpClientResponse(error: response.body);
}
} catch (e, stack) {
developer.log(e.toString(), stackTrace: stack);
final String message = unexpectedErrorMessage ?? U.s.unexpectedErrorPleaseTryAgain;
onException(message);
return UHttpClientResponse(exception: message);
}
}