I'm using Dio to handle APIs functions.
Here's my code:
Future<List<ItemModel>> getItems() async {
try {
Response response = await dio.get("$_apiUrl$_itemEndPoint",
options: Options(headers: {
"Accept": 'application/json',
}));
List<ItemModel> _items = List<ItemModel>();
response.data['data']?.forEach((c) {
_items.add(ItemModel.fromMap(c));
});
return _items;
} catch (e) {
throw (e);
}
}
How can I catch an error based on timeout of send and receive?
Manage timeout exception using dio :
ApiRepositary.dart
class ApiRepositary {
Dio dio;
ApiRepositary() {
if (dio == null) {
BaseOptions options = new BaseOptions(
baseUrl: "your base url",
receiveDataWhenStatusError: true,
connectTimeout: 60*1000, // 60 seconds
receiveTimeout: 60*1000 // 60 seconds
);
dio = new Dio(options);
}
}
Future<LoginResponse> getLoginDetails(var loginRequestData) async {
try {
Response response = await dio.post("/authenticate", data: loginRequestData);
final LoginResponse loginResponse = LoginResponse.fromJson(response.data);
return loginResponse;
}on DioError catch (ex) {
if(ex.type == DioErrorType.CONNECT_TIMEOUT){
throw Exception("Connection Timeout Exception");
}
throw Exception(ex.message);
}
}
}
Example of handle exception :
void checkLogin(){
LoginRequest loginRequest = new LoginRequest(
email: "abcd#gmail.com",password: "passs#123");
var requestBody =jsonEncode(loginRequest);
debugPrint("Request Data : $requestBody");
_apiRepositary.getLoginDetails(requestBody).then((response){
debugPrint("Login Success $response");
//manage your response here
},
onError: (exception){
//Handle exception message
if(exception.message != null ){
debugPrint(exception.message); // Here you get : "Connection Timeout Exception"
}
},
);
}
You to define DIO option first:
BaseOptions options = new BaseOptions(
baseUrl: "http://example.org",
connectTimeout: 5000,
receiveTimeout: 3000,
);
then:
Dio dio = new Dio(options);
var jsonNews = await dio.get(
'http://example.org/v2/everything?q=bitcoin&from=2020-01-24&sortBy=publishedAt&apiKey=7f3c604b6e2245c88se50lzx02dc9cac1e2');
Source :
https://pub.dev/packages/dio
Here's what I'm guessing by looking at one of their test files and their error class:
try {
await Dio().get("https://does.not.exist");
} on DioError catch (e) {
if (e.type == DioErrorType.connectTimeout) {
// ...
}
if (e.type == DioErrorType.receiveTimeout) {
// ...
}
}
you can use the try-catch for Timeout exception and the handle it on you basis
Related
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;
}
I tried refreshing my tokens when my calling api goes to error 401 (token expires) but when it calls dio interceptor is not triggered.
api.dart is definition of my dio
Dio _createHttpClient() {
final api = Dio(
new BaseOptions(
baseUrl: environments.api,
contentType: Headers.jsonContentType,
responseType: ResponseType.json,
),
);
api
..interceptors.clear()
..interceptors.add(new ErrorDialogInterceptor())
..interceptors.add(new AuthTokenInterceptor(api));
return api;
}
final api = _createHttpClient();
profil_provider.dart is the call of my api
Future<ProfilePicture?> getPictureProfile(String id) async {
String url = '/v1/users/$id/profile-picture';
try {
final response = await api.get(
url,
options: Options(
responseType: ResponseType.bytes,
headers: {
ErrorDialogInterceptor.skipHeader: true,
},
),
);
Uint8List avatar = Uint8List.fromList(response.data);
return ProfilePicture(image: avatar);
} catch (e) {
print('e');
return null;
}
}
and it go to the catch error
auth_interceptor.dart is for manage my error and request of my api
class AuthTokenInterceptor extends Interceptor {
static const skipHeader = 'skipAuthToken';
Dio api;
AuthTokenInterceptor(this.api);
#override
onRequest(RequestOptions options, RequestInterceptorHandler handler) async {
final context = applicationKey.currentContext;
print("test");
final repository = context?.read<AuthRepository>();
if (repository == null) {
return;
}
final accessToken = await repository.getAccessToken();
print("access: $accessToken");
if (accessToken != null) {
print(accessToken);
options.headers['Authorization'] = 'Bearer $accessToken';
}
return super.onRequest(options, handler);
}
#override
onError(DioError err, ErrorInterceptorHandler handler) async {
final context = applicationKey.currentContext;
if (context == null) {
return;
}
final response = err.response?.data;
if (response == null) {
return super.onError(err, handler);
}
final repository = context.read<AuthRepository>();
if (err.response?.statusCode == 401)
if (err.response?.statusCode == 401 &&
await repository.getRefreshToken() != null) {
api.interceptors.clear();
return _handlerRefreshToken(context, repository, err, handler);
}
return super.onError(err, handler);
}
and it not go to the auth interceptor with my debug print, I don't know why it not go there with the error 401 from the catch error
you can do.
// returns the http request
Future<Response<ProfilePicture>> getPictureProfile(String id) {
String url = '/v1/users/$id/profile-picture';
return api.get(url)
}
//...
// and use a try/catch outside the method
try{
Response<ProfilePicture> respose = await getPictureProfile(555)
print(respose.data)
}catch(e){
print(e)
}
Is there any example that I can refer to about Common class/method for flutter API calls(GET,POST,...) in flutter? I have handled all the API requests in a common method in react native, I'm not sure how to implement it in flutter.
you have to call getRequest using url parameter
Future<Response> getRequest(String url) async {
Response response;
try {
response = await _dio.get(url,
options: Options(headers: {
HttpHeaders.authorizationHeader:
'Bearer $accessToken'
}));
print('response $response');
} on DioError catch (e) {
print(e.message);
throw Exception(e.message);
}
return response;
}
here is the post method
Future<Response> posRequestImage(String url, data) async {
try {
response = await _dio.post(
url,
data: formData,
options: Options(headers: {
HttpHeaders.authorizationHeader:
'Bearer $accessToken'
}),
);
if (response.statusCode == 200) {
return response;
}
print('post response $response');
} on DioError catch (e) {
print(e.message);
throw Exception(e.response?.statusMessage);
}
return response;
}
You can create a class to handle it. For example, this is my class to handle all service for user model
import 'package:http/http.dart' as http;
class UserService {
var baseUrl = URL.devAddress;
Future<User> getUser() async {
final response = await http.get(
Uri.parse(baseUrl + "user/1")
);
if (response.statusCode == 200) {
final data = json.decode(response.body);
return data
} else {
throw Exception("Failed");
}
}
}
Future<void> getUser(String username) async {
Uri uri = Uri.parse('https://example.com');
try {
Map<String, dynamic> params = new HashMap();
params['username'] = username;
final response = await client.post(uri,
body: jsonEncode(params),
);
print("response ${response.body}");
} on FetchDataException {
throw FetchDataException("No Internet connection");
}
}
i have a problem with hate. I'm trying to login using dio, the login method works perfectly, but when I put invalid credentials dio gives me this error:
DioError
Error in execution
I created a boolean function that would return true or false if the statuscode was 200 it would return true and if not it would return false, but when logging in with the right credentials everything is ok, everything happens as it should, but when logging in with invalid credentials this error above causes it. I'm using shared preferences to store the tolken in the app, and the logic would be simple, if it was 200 I would log into the app, otherwise it would show me a snackbar I made in another file, this is my code:
loginFinal() async {
if (formKey.currentState!.validate()) {
bool loginIsOk = await loginConect();
if (loginIsOk) {
Get.offAllNamed("/home");
await Future.delayed(const Duration(seconds: 1));
message(MessageModel.info(
title: "Sucesso",
message: "Seja bem vindo(a) influenciador(a)",
));
} else {
loaderRx(false); //LOADER
message(MessageModel.error(
title: "Erro",
message: "Erro ao realizar login",
));
}
}
}
//LOGICA DE ENTRAR NO APP
Future<bool> loginConect() async {
final dio = Dio();
String baseUrl = "https://soller-api-staging.herokuapp.com";
loaderRx(true); //LOADER
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
final response = await dio.post(
baseUrl + "/auth",
data: jsonEncode(
{
"login": emailController.text,
"senha": passWordController.text,
},
),
options: Options(
headers: {'Content-Type': 'application/json; charset=UTF-8'},
method: "post",
),
);
if (response.statusCode == 200) {
await sharedPreferences.setString(
"token",
"string: ${response.data["string"]}",
);
print("Resposta: ${response.data["string"]}");
loaderRx(false);
return true;
} else {
print("RESPOSTA: ${response.data}");
return false;
}
}
}
Dio always throw an exception if the status code in the header is not 200,
you will need to catch the exception using try catch.
In the catch method, you can check if the type of the error is DioError and then handle that exception,
Here is a code snippet of a login process that I use in my code to handle this behavior.
Future<SignInApiResponse> signInUser(String _email,String _password) async {
try {
final dio = Dio(ApiConstants.headers());
final Response response = await dio.post(
ApiConstants.baseUrl + ApiConstants.signInUrl,
data: {"email": _email,
"password": _password,
},
);
if (response.statusCode == 200) {
return SignInApiResponse.fromJson(response.data);
} else {
return SignInApiResponse(message: response.toString());
}
} catch (e) {
if (e is DioError) {
if (e.response?.data == null) {
return SignInApiResponse(message: Messages.loginFailed);
}
return SignInApiResponse.fromJson(e.response?.data);
} else {
return SignInApiResponse(message: e.toString());
}
}
}
hopefully, this will help
if not you can always use http package that does not throw an exception in similer case
This method is to post an order to a server and it's in a Provider class :
Future<void> addOrder(OrderRequest orderRequest) async {
final prefs = await SharedPreferences.getInstance();
String accessToken = prefs.getString(Constants.prefsUserAccessTokenKey);
String url = Urls.addOrderUrl;
try {
var bodyParams = json.encode({
"Branch": {"Id": orderRequest.branchId},
"DeliveryAddress":
orderRequest.addressId == 0 ? {} : {"Id": orderRequest.addressId},
"InBranch": orderRequest.inBranch,
"TableNumber": orderRequest.tableNumber.toString(),
"OrderItems": orderRequest.items,
"PromoCode": orderRequest.promoCodeId == 0
? {}
: {"Id": orderRequest.promoCodeId}
});
print("Url: " + url);
print("Token: " + accessToken);
print("Params: " + bodyParams);
final response = await retry(
() => http
.post(url,
headers: {
"content-type": "application/json",
"Accept": "application/json",
"Authorization": "Bearer " + accessToken
},
body: json.encode(bodyParams))
.timeout(Duration(seconds: 5)),
retryIf: (e) => e is SocketException || e is TimeoutException);
final responseData = json.decode(response.body);
print(responseData);
if (response.statusCode == 200) {
} else if (response.statusCode == 401) {
throw AuthException("401", responseData['Message']);
} else {
throw HttpException(responseData['Message']);
}
} catch (error) {
print(error);
throw error;
}
}
and in my screen class i create a method to upload my data to the server which i use it when i press a button which handle the post request :
Future<void> _addOrder() async {
OrderRequest request = OrderRequest();
request.addressId = _selectedAddress.id;
request.branchId = int.parse(_selectedBranchId);
request.inBranch = _selectedAddress.id == 0;
request.items = _cartItemsList;
request.promoCodeId = _promoCodeId;
request.tableNumber = _tableNumber;
try {
setState(() {
_isLoading = true;
});
await Provider.of<OrderProvider>(context).addOrder(request);
Provider.of<CartProvider>(context).emptyCart();
_showDialog("Order Sent", "Your order is sent to restaurant.");
} on HttpException catch (error) {
_showDialog("Error adding order", error.message);
} on SocketException catch (_) {
_showDialog("Error adding order",
"Please check your internet connection and try again");
} on TimeoutException catch (_) {
_showDialog("Error adding order",
"Please check your internet connection and try again");
} on AuthException catch (_) {
_refreshToken();
} catch (error) {
print(error);
_showDialog("Error adding address", "Something went wrong");
}
}
but when i press a Order button t to send a post request to a server i got this error:
I/flutter (12421): {Message: Error:Object reference not set to an instance of an object.}
I/flutter (12421): HttpException: Error:Object reference not set to an instance of an object.
this is the model class that i use
class OrderRequest{
int branchId;
int addressId;
bool inBranch;
int promoCodeId;
int tableNumber;
List<CartItem> items;
OrderRequest(
{
this.branchId,
this.addressId,
this.inBranch,
this.promoCodeId,
this.tableNumber,
this.items});
}