How to catch an Error for timeout on http.client Flutter - flutter

I have a Future that calls a POST API using the http.client structure.
At the moment there is an issue with said API and my call is timing out before the full header is received, giving me a unhandled exception.
How is the best way to return this exception and show a snackbar of the issue returned?
Future<dynamic> get() async {
try {
var response = await client.post(
Uri.parse(Url),
headers: headers,
body: body,
);
}

here is the simple http call to catch an Error for timeout
return the error and catch this where from you handle the api call
import 'package:http/http.dart';
Future<dynamic> get() async {
try {
var response = await post(
Uri.parse(Url),
headers: headers,
body: body,
).timeout(Duration(seconds: 2), onTimeout: (){
/// here is the response if api call time out
/// you can show snackBar here or where you handle api call
return Response('Time out!', 500);
});
}catch(e){
print(e);
}
}
you can change your duration in the timeout method

Related

Function does not continue after catch

I have a try..catch within a dart function. When the await client.post throws an error, it does not continue after the catch, why?
#override
Future<http.Response> post(url, {Map<String, String?>? headers, body, Encoding? encoding, BuildContext? context}) async {
headers = await prepareHeaders(headers);
http.Response? response = null;
try {
response = await client.post(url, headers: headers as Map<String, String>?, body: body, encoding: encoding);
} catch (_) {
debugPrint('test'); // It comes here
}
// Does not come here
log(url: url, type: 'POST', body: body as String?, response: response!);
return await parse(response, context: context);
}
And it shouldnt. In the code below the catch, you are relying on the response object being set. If the post errors, that wont be the case, producing more errors. Move the log and the return call inside the try block.
Your code almost certainly is continuing after the catch block; it's just immediately throwing another exception. If client.post throws an exception, then response will not be set and will retain its initial value of null. However, after your catch block, you do:
log(url: url, type: 'POST', body: body as String?, response: response!);
which asserts that response is not null. That will throw a TypeError.
I don't know why you aren't observing the TypeError, but I'd suspect that you have a blanket catch block somewhere higher up in the call stack that is swallowing the exception (especially given your use of catch (_) in what code you have shown). This is why Effective Dart recommends:
Avoiding catch without on.
Not catching Errors.
The function will not execute after the catch block, the function will be terminated after the catch whenever any exception occurred then the catch block gets called. To solve this issue you can try this.
#override
Future<http.Response> post(url, {Map<String, String?>? headers, body,
Encoding? encoding, BuildContext? context}) async {
headers = await prepareHeaders(headers);
http.Response? response = null;
try {
response = await client.post(url, headers: headers as Map<String,
String>?, body: body, encoding: encoding);
log(url: url, type: 'POST', body: body as String?, response: response!);
return await parse(response, context: context);
} catch (_) {
debugPrint('test');
rethrow;
}
}

http put did not send any response in flutter

Hey I have this app where I can update status, and I use http.put method, but it takes forever to response. I got this error
error
And here is the code for http.put
Future<void> mapEventToState(
Emitter<ReportStatusState> emit, ReportStatusEvent event) async {
emit(ReportStatusLoading());
ReportStatusPut statusPut = event.statusPutBody;
// ReportStatusModel model =
// await apiAuthRepository.updateReportStatus(statusPut, event.id);
ReportStatusModel model = await updateReportStatus({'biaya': '0', 'status': 'SELESAI'}, event.id);
print(model);
if (!model.success) {
emit(ReportStatusFailure(model.message));
}
print(model.code);
emit(ReportStatusSuccess());
}}
Future<ReportStatusModel> updateReportStatus(
Map data, String id) async {
final SharedPreferencesManager sharedPreferencesManager =
locator<SharedPreferencesManager>();
String? token =
sharedPreferencesManager.getString(SharedPreferencesManager.keyAccessToken);
try {
final response = await http.put(
Uri.parse('https://api.komplekku.com/officer/api/report/v1/$id'),
body: json.encode(data),
headers: {'Authorization': 'Bearer $token'});
return ReportStatusModel.fromJson(json.decode(response.body));
} catch (e) {
throw Exception(e);
}
}
There is nothing wrong with the API, I already check using Postman and it worked perfectly fine, Anyone know what went wrong?

Why sending the following request ends up with uncaught exception?

I have the following Flutter & Dart code function, which sends a request to the server:
Future<void> autoAuth(BuildContext ctx) async {
final url = Uri.parse('${this._baseURL.toString()}/auto-auth');
try {
final deviceStorage = await SharedPreferences.getInstance();
if (deviceStorage.getString('refreshToken') == null) {
return this._setUser(null);
}
final response = await http.post(url, headers: {
'Authorization': 'Bearer ${deviceStorage.getString('refreshToken')!}',
}).timeout(const Duration(seconds: 3));
final Map<String, dynamic> responseBody = json.decode(response.body);
if (responseBody['success']) {
this._refreshAccessToken(ctx, deviceStorage);
return this._setUser(new User(
id: responseBody['data']['id'],
isSubscribed: responseBody['data']['isSubscribed'],
playlistId: responseBody['data']['playlistId'],
));
}
this._setUser(null);
} on SocketException {
this._setUser(null);
throw Error();
} on TimeoutException {
this._setUser(null);
throw Error();
} catch (_) {
this._setUser(null);
}
}
Note, that url is wrong intentionally, so the request will timeout.
But, for this, I coded: .timeout(...) on the future request. So, basically, after 3 secnods it should caught by on TimeoutException exception catch.
It does so. However, after something like 1 minute (probably some default timeout of http request in dart), I get an uncaught exception because the request has timed-out. Where Am I wrong?
This is because you are using it in the wrong way. The .timeout code you use, is generic timeout for any future. Thus, you catch the error of the future timeout, but you don't catch the error being generated from the http request timeout.
To use it correctly, first add the following import: import 'package:http/io_client.dart' as http;
Then change the code to:
final ioClient = HttpClient();
ioClient.connectionTimeout = const Duration(seconds: 3);
final client = http.IOClient(ioClient);
final response = await client.post(url, headers: {
'Authorization': 'Bearer ${deviceStorage.getString('refreshToken')!}',
});

Flutter http.post timeout [duplicate]

This method submits a simple HTTP request and calls a success or error callback just fine:
void _getSimpleReply( String command, callback, errorCallback ) async {
try {
HttpClientRequest request = await _myClient.get( _serverIPAddress, _serverPort, '/' );
HttpClientResponse response = await request.close();
response.transform( utf8.decoder ).listen( (onData) { callback( onData ); } );
} on SocketException catch( e ) {
errorCallback( e.toString() );
}
}
If the server isn't running, the Android-app more or less instantly calls the errorCallback.
On iOS, the errorCallback takes a very long period of time - more than 20 seconds - until any callback gets called.
May I set for HttpClient() a maximum number of seconds to wait for the server side to return a reply - if any?
There are two different ways to configure this behavior in Dart
Set a per request timeout
You can set a timeout on any Future using the Future.timeout method. This will short-circuit after the given duration has elapsed by throwing a TimeoutException.
try {
final request = await client.get(...);
final response = await request.close()
.timeout(const Duration(seconds: 2));
// rest of the code
...
} on TimeoutException catch (_) {
// A timeout occurred.
} on SocketException catch (_) {
// Other exception
}
Set a timeout on HttpClient
You can also set a timeout on the HttpClient itself using HttpClient.connectionTimeout. This will apply to all requests made by the same client, after the timeout was set. When a request exceeds this timeout, a SocketException is thrown.
final client = new HttpClient();
client.connectionTimeout = const Duration(seconds: 5);
You can use timeout
http.get(Uri.parse('url')).timeout(
const Duration(seconds: 1),
onTimeout: () {
// Time has run out, do what you wanted to do.
return http.Response('Error', 408); // Request Timeout response status code
},
);
The HttpClient.connectionTimeout didn't work for me. However, I knew that the Dio packet allows request cancellation. Then, I dig into the packet to find out how they achieve it and I adapted it to me. What I did was to create two futures:
A Future.delayed where I set the duration of the timeout.
The HTTP request.
Then, I passed the two futures to a Future.any which returns the result of the first future to complete and the results of all the other futures are discarded. Therefore, if the timeout future completes first, your connection times out and no response will arrive. You can check it out in the following code:
Future<Response> get(
String url, {
Duration timeout = Duration(seconds: 30),
}) async {
final request = Request('GET', Uri.parse(url))..followRedirects = false;
headers.forEach((key, value) {
request.headers[key] = value;
});
final Completer _completer = Completer();
/// Fake timeout by forcing the request future to complete if the duration
/// ends before the response arrives.
Future.delayed(timeout, () => _completer.complete());
final response = await Response.fromStream(await listenCancelForAsyncTask(
_completer,
Future(() {
return _getClient().send(request);
}),
));
}
Future<T> listenCancelForAsyncTask<T>(
Completer completer,
Future<T> future,
) {
/// Returns the first future of the futures list to complete. Therefore,
/// if the first future is the timeout, the http response will not arrive
/// and it is possible to handle the timeout.
return Future.any([
if (completer != null) completeFuture(completer),
future,
]);
}
Future<T> completeFuture<T>(Completer completer) async {
await completer.future;
throw TimeoutException('TimeoutError');
}
This is an example of how to extend the http.BaseClient class to support timeout and ignore the exception of the S.O. if the client's timeout is reached first.
you just need to override the "send" method...
the timeout should be passed as a parameter to the class constructor.
import 'dart:async';
import 'package:http/http.dart' as http;
// as dart does not support tuples i create an Either class
class _Either<L, R> {
final L? left;
final R? right;
_Either(this.left, this.right);
_Either.Left(L this.left) : right = null;
_Either.Right(R this.right) : left = null;
}
class TimeoutClient extends http.BaseClient {
final http.Client _httpClient;
final Duration timeout;
TimeoutClient(
{http.Client? httpClient, this.timeout = const Duration(seconds: 30)})
: _httpClient = httpClient ?? http.Client();
Future<http.StreamedResponse> send(http.BaseRequest request) async {
// wait for result between two Futures (the one that is reached first) in silent mode (no throw exception)
_Either<http.StreamedResponse, Exception> result = await Future.any([
Future.delayed(
timeout,
() => _Either.Right(
TimeoutException(
'Client connection timeout after ${timeout.inMilliseconds} ms.'),
)),
Future(() async {
try {
return _Either.Left(await _httpClient.send(request));
} on Exception catch (e) {
return _Either.Right(e);
}
})
]);
// this code is reached only for first Future response,
// the second Future is ignorated and does not reach this point
if (result.right != null) {
throw result.right!;
}
return result.left!;
}
}
Their is onError option which works fine if their is any exception like no internet.It has to return response(my case in below code) or null.
In response their are 2 options Body and Status code.
var response = await http.post(url, body: body, headers: _headers).onError(
(error, stackTrace) => http.Response(
jsonEncode({
'message':no internet please connect to internet first
}),
408));
You can also choose to override the settings for a HttpClient:
class DevHttpOverrides extends HttpOverrides {
#override
HttpClient createHttpClient(SecurityContext? context) {
return super.createHttpClient(context)
..connectionTimeout = Duration(seconds: 2);
}
}

rxDart not calling onError

I am trying to make a simple request to backend using rxDart. But the problem I face is that when I get a http error such as 404, onError is not called, however, it is possible to extract it in onData.
I have a little experience with RxJava + retrofit and there it works as expected, when there is a response with error http status code onError is called and can be handled appropriately.
1. What am I doing wrong, or is it intended behavior?.
Object sendProfileData() {
Stream<Response> stream = onboardingRepository.createUser(User(name: 'name', surname: 'surname', lat: 1.0, lng: 2.0));
stream.listen((response) {
print(response.statusCode);
setAttributes();
}, onError: (e) {
print(e);
});
}
OnboardingRepository.dart:
class OnboardingRepository {
Observable<Response> createUser(User user) {
return Observable.fromFuture(TMApi.createUser(user));
}
}
TMApi.dart:
class TMApi {
static Future<http.Response> createUser(User user) async {
String url = '$baseUrl/create_user';
return await http.post(url, body: json.encode(user.toJson()));
}
}
What would be the best way to handle the event in the View? There should be an error displayed if error occurs, otherwise it should open a new screen. sendProfileData() method will return an Object, based on that I am going to perform actions in the view, but that doesn't sound like a very elegant solution...
Any suggestions on architecture are welcome :)
the http library in dart works a bit different than Retrofit.
The Future returned by http.post only throws an exception when there is an io error (socket error, no internet).
Server responses like 404 are reflected in the http.Response.
I created a simple convenience method that might help you:
void throwIfNoSuccess(http.Response response) {
if(response.statusCode < 200 || response.statusCode > 299) {
print('http error!');
print(response.body);
throw new HttpException(response);
}
}
class HttpException implements Exception {
HttpException(this.response);
http.Response response;
}
How to use:
import 'dart:convert';
import 'package:http/http.dart' as http;
Future<UserProfile> getUserProfile(String userId) async {
final url = 'https://example.com/api/users/$userId';
final response = await http.get(url);
throwIfNoSuccess(response);
final jsonBody = json.decode(response.body);
return UserProfile.fromJson(jsonBody);
}