Why sending the following request ends up with uncaught exception? - flutter

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')!}',
});

Related

client.post login timeout not working in flutter

I need to use timeout if post request not working so, I write below code:
class APIService {
static var client = http.Client();
static Future<bool> login(LoginRequestModel model) async {
Map<String, String> requestHeaders = {
'Content-Type': 'application/json',
};
var url = Uri.http(Config.apiURL, Config.loginAPI);
try {
final response = await client
.post(
url,
headers: requestHeaders,
body: jsonEncode(model.toJson()),
)
.timeout(const Duration(seconds: 5));
print("response:");
print(response);
if (response.statusCode == 200) {
//SHARED
await SharedService.setLoginDetails(loginResponseJson(response.body));
return true;
} else {
return false;
}
} on TimeoutException catch (e) {
// handle timeout
return false;
}
}
But never end await client.post method waiting althouth I add timeout. How can I solve this ?
You can try this:
import 'package:http/http.dart' as http;
import 'package:http/io_client.dart' as http;
final body = { 'email': email, 'password': password };
final client = http.Client();
http.Response res;
try {
res = await client
.post(
url,
headers: requestHeaders,
body: jsonEncode(model.toJson()),
.catchError((e) {
// SocketException would show up here, potentially after the timeout.
})
.timeout(const Duration(seconds: 5));
} on TimeoutException catch (e) {
// Display an alert, no internet
} catch (err) {
print(err);
return null;
}

How to solve ssl certificate error with GetX and Get Connect in flutter

I am trying to use Getx service.
here is my api client class as am trying to get data from internet using getx
import 'package:flutter_application_shop/utilis/app_constance.dart';
import 'package:get/get.dart';
class ApiClient extends GetConnect implements GetxService {
late String token;
final String appBaseUrl;
late Map<String, String> _mainHeaders;
ApiClient({required this.appBaseUrl}) {
baseUrl = appBaseUrl;
timeout = const Duration(seconds: 30);
token = AppConstance.TOKEN;
_mainHeaders = {
'Content-type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer $token',
};
}
Future<Response> getData(String url) async {
try {
Response response = await get(url);
return response;
} catch (e) {
return Response(statusCode: 1, statusText: e.toString());
}
}
///end
}
and when I run debug, I get this error.
I/flutter ( 6967): HandshakeException: Handshake error in client (OS Error:
I/flutter ( 6967): CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate(handshake.cc:393))
How can I solve this?
This is because the request is coming from an untrusted source, in order to bypass the error, Add allowAutoSignedCert = true; to your request in the class that extends GetConnet.
Example:
import 'package:flutter_application_shop/utilis/app_constance.dart';
import 'package:get/get.dart';
class ApiClient extends GetConnect implements GetxService {
late String token;
final String appBaseUrl;
late Map<String, String> _mainHeaders;
ApiClient({required this.appBaseUrl}) {
baseUrl = appBaseUrl;
timeout = const Duration(seconds: 30);
token = AppConstance.TOKEN;
allowAutoSignedCert = true; // the solution
_mainHeaders = {
'Content-type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer $token',
};
}
Future<Response> getData(String url) async {
try {
Response response = await get(url);
return response;
} catch (e) {
return Response(statusCode: 1, statusText: e.toString());
}
}
}

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?

flutter dio(4.0.0) handling token expiration (handling 401)

I have declared a class to make api requests using flutter Dio as follows.
class DioUtil {
static Dio _instance;
static Dio getInstance() {
if (_instance == null) {
_instance = createDio();
}
return _instance;
}
static Dio createDio() {
var dio = Dio();
dio.interceptors.add(InterceptorsWrapper(onRequest: (options, handler) {
// Do something before request is sent
return handler.next(options); //continue
}, onResponse: (response, handler) {
// Do something with response data
return handler.next(response); // continue
}, onError: (DioError e, handler) async {
if (e.response != null) {
if (e.response.statusCode == 401) {
var dio = DioUtil.getInstance();
dio.interceptors.requestLock.lock();
dio.interceptors.responseLock.lock();
RequestOptions requestOptions = e.requestOptions;
await refreshToken();
Repository repository = Repository();
var accessToken = await repository.readData("accessToken");
final opts = new Options(
method: requestOptions.method
);
dio.options.headers["Authorization"] = "Bearer " + accessToken;
dio.interceptors.requestLock.unlock();
dio.interceptors.responseLock.unlock();
dio.request(requestOptions.path,
options: opts,
data: requestOptions.data,
queryParameters: requestOptions.queryParameters);
}//TODO: handle else clause
}
}));
return dio;
}
static refreshToken() async {
Response response;
Repository repository = Repository();
var dio = Dio();
final Uri apiUrl = Uri.parse(BASE_PATH + "auth/reIssueAccessToken");
var refreshToken = await repository.readData("refreshToken");
dio.options.headers["Authorization"] = "Bearer " + refreshToken;
response = await dio.postUri(apiUrl);
if (response.statusCode == 200) {
LoginResponse loginResponse =
LoginResponse.fromJson(jsonDecode(response.toString()));
repository.addValue('accessToken', loginResponse.data.accessToken);
repository.addValue('refreshToken', loginResponse.data.refreshToken);
} else {
print(response.toString());
}
}
}
and I use flutter bloc pattern and my bloc is as follows.
class OurClassBloc extends Bloc<OurClassEvent, OurClassState> {
OurClassBloc(OurClassState initialState) : super(initialState);
Repository repository = Repository();
#override
Stream<OurClassState> mapEventToState(
OurClassEvent event,
) async* {
if (event is GetClasses) {
yield* _getClassCategories(event);
}
}
Stream<OurClassState> _getClassCategories(GetClasses event) async* {
Response response;
var dio = DioUtil.getInstance();
final String apiUrl = (BASE_PATH + "classCategories");
var accessToken = await repository.readData("accessToken");
Map<String, dynamic> map = {"active": event.active};
dio.options.headers["Authorization"] = "Bearer " + accessToken;
dio.options.headers["Accept"] = "*/*";
try {
response = await dio.get(apiUrl, queryParameters: map);
if (response.statusCode == 200) {
OurClassResponse loginResponse =
OurClassResponse.fromJson(jsonDecode(response.toString()));
yield OurClassSuccess(loginResponse);
}
if (response.statusCode >= 400) {
yield OurClassFailed();
}
} catch (e) {
yield OurClassFailed();
}
}
}
When I make the requests with valid access token, I get 200 status code in bloc class and api works fine.when the token is expired, the dio class correctly gets the new token, make the same api call with new token successfully and inside the below callback I get the correct response also.
onResponse: (response, handler) {
return handler.next(response);
}
but response doesn't comes to bloc class. Though it returned the response by calling return handler.next(response);,it is not coming to response variable inside _getClassCategories method.I expect the correct response should come to the response variable in bloc class for both scenarios:
makes the api call with valid token.
makes the api call with expired token.
but only scenario 1 is working in my code and hope someone here can help me to fix this.
EDIT- this works fine with dio previous version(3.0.10) - code
dio.request(requestOptions.path,
options: opts,
data: requestOptions.data,
queryParameters: requestOptions.queryParameters);
This line creates a new request with no relation to the original one. If the request succeeds, there is no code listening for a response. If you want the original caller to receive anything, you will need to forward the response to the original handler:
try {
final response = await dio.request(requestOptions.path,
options: opts,
data: requestOptions.data,
queryParameters: requestOptions.queryParameters);
handler.resolve(response);
} on DioError catch (error) {
handler.next(error); // or handler.reject(error);
}
Also, be sure to forward the error to the handler in non-401 cases as well. Dio 4.0.0 interceptors don't automatically forward anything.

unhandled socket exception with no internet connection when i set timeout duration

I use simple method to get some data from internet 'http get request' :
`Future<UserModel> getUser(int userId) async {
UserModel user;
try {
final response = await http.get(
"$_baseUrl/users/$userId",
)
.timeout(Duration(seconds: 5))
;
user = userModelFromJson(response.body);
return user;
} on TimeoutException catch (e) {
print('$e in authentication service');
throw e;
} on SocketException catch (e) {
print('$e in authentication service');
throw e;
} catch (e) {
print('$e in authentication service');
throw e;
}
}`
but when i have no internet connection it shows me that error :
`Exception has occurred.
SocketException (SocketException: Failed host lookup:
'jsonplaceholder.typicode.com' (OS Error: No address associated with
hostname, errno = 7))`
whenever i remove the .timeout(Duration(seconds:5)) the code works perfectly ,
but the socket exception is caught after long time (15-20)seconds to show that there is no internet connection that's why i used timeout, i tried to use multiple packages (http middleware ,http helper ,retry) , i tried to use http.client and close it in finally block and the same error occurred and the app crashes
the image shows the error when the socket exception is thrown and unhandled
it catches the timeout exception as expected but then after another 10-15 seconds it throws an handled socket exception ,why it throws this socket exception and what can i do to avoid this?
If you want to implement a timeout with the http package, here is how it can be done:
import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:http/io_client.dart' as http;
Future<void> login(String email, String password) async {
final ioClient = HttpClient();
client.connectionTimeout = const Duration(seconds: 5);
final body = { 'email': email, 'password': password };
final client = http.IOClient(ioClient);
http.Response res;
try {
res = await client
.post(
'$url/login',
headers: {'Content-Type': 'application/json'},
body: jsonEncode(body));
} on SocketException catch (e) {
// Display an alert, no internet
} catch (err) {
print(err);
return null;
}
// Do something with the response...
}
You should consider using the HTTP package https://pub.dev/packages/http
as it helps cleanup your code an helps with error handling.
Here's an example of a GET request using the package :
await http.get(url).then((response) async {
// DO SOMETHING HERE
});
response.body is your data.
response.statusCode is your http status code (200, 404, 500, etc.)
https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
and here's a post request with data :
var data = {
"dataset1": {
"key1": "value",
"key2": "value",
},
};
await http.post(url,
body: jsonEncode(data),
headers: {'content-type': 'application/json'}).then((response) async {
// DO SOMETHING HERE
});