How to handle socket exception when net is not there? - asynchronous

I want to display an error screen if net is not there. I am not using connectivity package because I don't want continuous check. I just want to handle exception while calling backend api and display the screen. I am unable to catch the exception.
I found this issue and this question about socket exceptions but none seem to help me.
This is how I call my backend api -
callBackendApi() async {
try {
http.Response response = await Future.value(/*api call here*/)
.timeout(Duration(seconds: 90), onTimeout: () {
print('TIME OUT HAPPENED');
});
} catch (exception) {
Fluttertoast.showToast(msg: 'Check internet connection.');
print('Error occurred' + exception.toString());
}
}

I use dio like this:
try {
var formData = FormData.from(Map<String, dynamic>.from(data));
var response = await dio.post(
uri,
data: formData,
);
jsonResponse = json.decode(response.data);
} on DioError catch (e) {
if (DioErrorType.RECEIVE_TIMEOUT == e.type ||
DioErrorType.CONNECT_TIMEOUT == e.type) {
throw CommunicationTimeoutException(
"Server is not reachable. Please verify your internet connection and try again");
} else if (DioErrorType.RESPONSE == e.type) {
// 4xx 5xx response
// throw exception...
} else if (DioErrorType.DEFAULT == e.type) {
if (e.message.contains('SocketException')) {
throw CommunicationTimeoutException('blabla');
}
} else {
throw CommunicationException("Problem connecting to the server. Please try again.");
}
}

My solution is to import 'dart.io' in order to catch SocketException from try block:
import 'package:http/http.dart' as http;
import 'dart:io';
try{
//Handle you network call code block in here
}on SocketException catch(_){
//To handle Socket Exception in case network connection is not available during initiating your network call
}

Well i don't know if my answer will be solving your question but days ago i had a problem little bit likely yours but in my case was using firebase realtime database. I was asking to myself how can i protect my app from network fails like no internet connection available? Well i am not using connectivity package too so i solve this problem with an approach that you already has been try using a timeout for network operations. I will share two snipets with differents approaches that i had implemented to handle this kind of problem adding some comments trying explain the differences between them.
Approach 1 - Setting timeout outside from network request method
Well the snipet below is a simple firebase database request where _viewsRef is a DatabaseReference and the once method do the request and returns me a Future with or without data.
// get users visualization from realtime database and returns a future
static Future<DataSnapshot> getUserVisualizations({#required String uid}) async {
return _viewsRef.child(uid).limitToLast(50).once();
}
In my BLoC component class i am calling the method below and setting a timeout to the future that is returned.
myBlocComponentMethod(){
//.. some work and finally the call
FirebaseUserViewsHelper.getUserVisualizations(uid: _currentUid)
.then(
(dataSnapshot){
if (dataSnapshot.value == null) {
// do some things to handle no data
}
else {
/// handle your data here
});
}
} // setting timeout here is an important point
).timeout( Duration(seconds: Constants.NETWORK_TIMEOUT_SECONDS),
onTimeout: (){
// method to handle a timeout exception and tell to view layer that
// network operation fails
// if we do not implement onTimeout callback the framework will throw a TimeoutException
} );
}
Well what is the point here? In this case if the timeout expires and future is not completed yet onTimeout callback is called and there i can tell to the view layer that network operation fails and show to the user some widget about it. But even with timeout expired the request to firebase database stays happening again and again, it's like the async event of request the database stays on dart event queue. I think this behavior is bad for performance aspects but if you're building your UI using a StreamBuilder with a little logic and code your requested data will be available right when you internet connection is back and with BLoC pattern the UI can respond easily to this event and we don't need provide a refresh button by example to user make the request again. I don't know if this is the right approach to implement this behavior but it works.
Approach 2 - Setting timeout inside from network request method
Below another firebase database request method
static Future<DataSnapshot> readUserNode( {#required String uid} ) async
=> USERS_REFERENCE.child(uid).once()
.timeout( Duration(seconds: Constants.NETWORK_TIMEOUT_SECONDS ) );
//note: Without timeout callback this line will throw a TimeoutException if the time expires
The usage in another BLoc component:
myBlocComponentMethod2(){
for( String uid in iterable ){
FirebaseUserHelper.readUserNode(uid: uid)
.then( (userSnapshot){
if (userSnapshot.value == null){
// do your stuffs
}
else {
// more stuffs to do
}
}).catchError( (error){
// when timeout expired we will catch the TimeoutException HERE and handling telling
// the UI what we need
} );
}
}
The big difference here that i get was in the behavior. In this second case since i put the timeout inside the request method when the timeout expires the request event do not run anymore, it's like that request event is removed from dart event queue. This can be good from performance perspective but now we need provide a refresh button in UI for user do the data again to get data from internet again.
I don't know if this workaround will solve your problem because you tell about SocketException what is not the case that i has described and i don't know what api you are using to make your requests. Anyway i hope that the concepts described in this post helps you implement a solution in your problem.

Related

Catch Socket Exception [duplicate]

This is probably a noob question, but how do I make my response throw an exception if the user does not have an internet connection or if it takes too long to fetch the data?
Future<TransactionModel> getDetailedTransaction(String crypto) async {
//TODO Make it return an error if there is no internet or takes too long!
http.Response response = await http.get(crypto);
return parsedJson(response);
}
You should surround it with try catch block, like so:
import 'package:http/http.dart' as http;
int timeout = 5;
try {
http.Response response = await http.get('someUrl').
timeout(Duration(seconds: timeout));
if (response.statusCode == 200) {
// do something
} else {
// handle it
}
} on TimeoutException catch (e) {
print('Timeout Error: $e');
} on SocketException catch (e) {
print('Socket Error: $e');
} on Error catch (e) {
print('General Error: $e');
}
Socket exception will be raised immediately if the phone is aware that there is no connectivity (like both WiFi and Data connection are turned off).
Timeout exception will be raised after the given timeout, like if the server takes too long to reply or users connection is very poor etc.
Also don't forget to handle the situation if the response code isn't = 200.
You don't need to use http to check the connectivity yourself, simply use connectivity library
You can use this plugin https://pub.dev/packages/data_connection_checker
So you can check prior if you have the connection, if not give a alert to the user that no internet connection. And if you have the internet connection then just proceed to your fetching part.
I will just link some resources below where it has been explained perfectly:
https://www.youtube.com/watch?v=u_Xyqo6lhFE
This is all things will be done prior to making an http call, but what if while making an http call the internet goes off then you can use the try catch block which #uros has mentioned.
Let me know if it works.
This is my approach to check internet connection to check internet connection throughout full app
i create a common class called "connectivity" & use it everywhere in app to check connectivity.i use connectivity package by flutter.
My connectivity class
Future<bool> check() async {
var connectivityResult = await (Connectivity().checkConnectivity());
if (connectivityResult == ConnectivityResult.mobile) {
return true;
} else if (connectivityResult == ConnectivityResult.wifi) {
return true;
}
return false;
}
then i use this class like this:
#override
void initState() {
super.initState();
checkInternetConnection().then((internet) {
if (internet != null && internet) {
// Internet Present Case
// do your task;
} else {
// No-Internet Case
showAlertDialog(context);
}
});
}

Xamarin.Forms get new Token when session is over

I have this scenario: Xamarin.Forms App connected with Web Api 2. I make all requests and get the data i want. Now when the session token expires, i need to refresh the token but don't logout the user. The user don't need to know when token is refreshed. How to organize this, add in every request if statement when i send it and check if token expires.
This is one of my requests:
public async Task<User> GetProfileSetup()
{
try
{
if (CrossConnectivity.Current.IsConnected)
{
string token = DependencyService.Get<ISharedFunctions>().GetAccessToken();
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
var response = await client.GetAsync(#"api/Profile/GetProfilSetup");
if (response.IsSuccessStatusCode)
{
string jsonMessage;
using (Stream responseStream = await response.Content.ReadAsStreamAsync())
{
jsonMessage = new StreamReader(responseStream).ReadToEnd();
}
User user = JsonConvert.DeserializeObject<User>(jsonMessage);
return user;
}
else
{
var m = response.Content.ToString();
return null;
}
}
else
{
return null;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
string error = ex.Message;
return null;
}
}
P.S I have Methods for GetToken and RefreshToken in my Api and they are working, just how to organize Refreshing ?
It really depends on what libraries are you using on your project.
But let's say you're using plain c# to handled your HTTP calls.
[OPTION 1] Polly
I can recommend you looking at Polly
It's a great library with a lot of features. You can use the Retry policy to handled expired tokens:
var _unauthorizedPolicy = Policy
.Handle<Exception>(ex => ex.StatusCode == HttpStatusCode.Unauthorized) // check here for your exception to be the right one
.RetryAsync(3, async (exception, retryCount, context) =>
{
try
{
var token = await _authService.RefreshToken();
// save the new token or whatever you need to store it
}
catch (Exception ex)
{
// RefreshToken failed, you should probably sign out the user
SignOut();
}
});
What this does is that Polly will try to execute your normal HTTP call and in case it fails and the cause is specified in Handle, then a retry mechanism is fired that will try to refresh the token and then retry your request. In the end, in case the token cannot be refreshed, you sign out the user. Of course, all this can be customized, check Polly's documentation is pretty well written.
Please note that inside Handle<T> you must put the right exception. I just used Exception as a placeholder since I'm not sure what Exception is thrown in your case.
Then you would call your method with this policy:
var result = await _unauthorizedPolicy.ExecuteAsync(() => GetProfileSetup())
And you can reuse that policy for any call, no need to create it every time.
[OPTION 2] DelegatingHandler
I will like here another StackOverflow answer:
How to Refresh a token using IHttpClientFactory
Basically you can intercept every HTTP call made via a HttpClient and refresh/add a token to your requests.
Note that that answer does not obligate you to use IHttpClientFactory, it also works for a simple HttpClient.
Also a little bit off-topic. You might want to look up for libraries to handle htt calls such as Retrofit. It will really reduce the amount of boilerplate code.

Token refresh concurrency issue in Dart with ASP.NET Core WebAPI

I wrote a simple application in Flutter using Dart. I use JWT tokens to authenticate user. Primary token is valid only 60 seconds.
When user send a request with expired token the webapi returns 401.
Then in my Dart code I check if statuscode of response is 401
If yes, then I send a request to RefreshToken endpoint and send request one more time (this request which returned 401 earlier).
If user does many actions too fast, expired token is renewed many times.
I'd like to avoid this.
In perfect soultion when token is being refreshing, other requests should wait.
I faced similar problem and tried to solve it using below approach.
I use flutter-redux to manage the state on client side.
Get jwt token after login
Decode the jwt token on client side as responded from server.
It contains a timeout - time of expiration.
Create a redux middleware on client side lets say _createRefreshTokenMiddleware.
Every request from client should go through this middle-ware before sending to server.
In this middle-ware, with every requests to server, check the token timeout, if token is expired, hold those request, send request to server to refresh token, wait until received new token, use this new token to send those request to server.
All other requests for which token will expire will wait on a common promise, lets say refreshTokenPromise to get refreshToken get resolved first. In this way you don't have to send multiple refreshToken requests.
If the token is still valid, let the requests to go through.
Refer below example -
Your middleware :
Middleware<AppState> _createRefreshTokenMiddleware() {
return (Store store, action, NextDispatcher next) async {
AppState appState = store.state;
AuthState auth = appState.auth;
if (isTokenExpired(auth)) {
if (auth.refreshTokenPromise == null) {
refreshToken(store).then((res) => next(action));
} else {
auth.refreshTokenPromise.then((res) => next(action));
}
}
next(action);
};
}
All the requests for which token is expired will wait on refreshTokenPromise to get resolved and as soon as that is resolved all of the pending requests will have new updated token set in request header (e.g).
Checking for token expiration :
bool isTokenExpired(AuthState auth) {
int bufferSeconds = 10;
if(auth != null && auth.authTokens != null && auth.authTokens.tokenExpiryTime != null) {
var currentTime = DateTime.now();
Duration durationRemaining = auth.authTokens.tokenExpiryTime.difference(currentTime);
return (durationRemaining.inSeconds - bufferSeconds) <= 0 ? true : false;
}
return false;
}
You send the request to refresh token 10 seconds before it is actually expired.
AuthState Model:
#immutable
class AuthState {
// properties
final bool isAuthenticated;
final bool isAuthenticating;
final AuthTokens authTokens;
final String error;
final Future<dynamic> refreshTokenPromise;
// constructor with default
AuthState({
this.isAuthenticated = false,
this.isAuthenticating = false,
this.authTokens,
this.error,
this.refreshTokenPromise,
});
}
Your auth-state model can be like above.
AuthToken:
#immutable
class AuthTokens {
// properties
final String accessToken;
final String refreshToken;
final DateTime tokenExpiryTime;
// constructor with default
AuthTokens({
this.accessToken,
this.refreshToken,
this.tokenExpiryTime,
});
}
Although I have given redux based solution here but same strategy can be applied anywhere else as well. I hope it helps.
As you correctly pointed out, the problem is that the authorization server receives too many token refresh request. Each particular user should only send one refresh request and rely on the results of that request.
Flutter's async package has a handy class called AsyncMemoizer for cases like this.
From the API reference:
A class for running an asynchronous function exactly once and caching its result.
An AsyncMemoizer is used when some function may be run multiple times
in order to get its result, but it only actually needs to be run once
for its effect. To memoize the result of an async function, you can
create a memoizer outside the function (for example as an instance
field if you want to memoize the result of a method), and then wrap
the function's body in a call to runOnce.
Assuming that the component of your app that handles all token requests is a singleton, you can cache the tokenrequests like that:
class TokenDataSource {
AsyncMemoizer<TokenResponse> tokenRequestMemoizer = AsyncMemoizer();
...
#override
Future<Tokens> verifyAndRefreshTokens() async {
var tokenResponse = await tokenRequestMemoizer.runOnce(() {
// run your token request code
});
// once the request is done, reset the memoizer so that future clients don't receive the cached tokens
tokenRequestMemoizer = AsyncMemoizer();
// return results
}
}
This will make all clients of TokenDataSource wait for the same token request instead of launching a new one.

IgniteMesssaging works in sync mode

It seems that Ignite(2.0) Messaging's send function works in sync mode, it will be blocked be the listener. And below is my testing code.
ignite.message().localListen("TEST", (nodeId, Msg) -> {
try {
Thread.sleep(500);
} catch (Exception ex) {
}
return true;
});
for (int i = 0; i < 100; i++) {
ignite.message().send("TEST", "Hello World");
}
It cost about 50 seconds to send 100 messages, and it is almost equals the sleep time 500 ms * 100. seems the send function in sync mode not in async mode.
Does anybody know how to change the send function in async mode?
Thanks in advance.
Seems async listener invocation was missed while adding new API, but you still have two options:
Use deprecated withAsync(), unless sendAsync() will be added.
Pass your own Executor in predicate, if you always return true, for example.
I've just opened a ticket for that IGNITE-5570
It seems that you're testing within one node. In this case there is no message sent and listener is invoked synchronously. Network communication is asynchronous in Ignite, so if you do the testing on two nodes, you should not see such behavior.

SignalR Long Running Process

I have setup a SignalR hub which has the following method:
public void SomeFunction(int SomeID)
{
try
{
Thread.Sleep(600000);
Clients.Caller.sendComplete("Complete");
}
catch (Exception ex)
{
// Exception Handling
}
finally
{
// Some Actions
}
m_Logger.Trace("*****Trying To Exit*****");
}
The issue I am having is that SignalR initiates and defaults to Server Sent Events and then hangs. Even though the function/method exits minutes later (10 minutes) the method is initiated again ( > 3 minutes) even when the sendComplete and hub.stop() methods are initiated/called on the client prior. Should the user stay on the page the initial "/send?" request stays open indefinitely. Any assistance is greatly appreciated.
To avoid blocking the method for so long, you could use a Taskand call the client method asynchronously.
public void SomeFunction(Int32 id)
{
var connectionId = this.Context.ConnectionId;
Task.Delay(600000).ContinueWith(t =>
{
var message = String.Format("The operation has completed. The ID was: {0}.", id);
var context = GlobalHost.ConnectionManager.GetHubContext<SomeHub>();
context.Clients.Client(connectionId).SendComplete(message);
});
}
Hubs are created when request arrives and destroyed after response is sent down the wire, so in the continuation task, you need to create a new context for yourself to be able to work with a client by their connection identifier, since the original hub instance will no longer be around to provide you with the Clients method.
Also note that you can leverage the nicer syntax that uses async and await keywords for describing asynchronous program flow. See examples at The ASP.NET Site's SignalR Hubs API Guide.

Resources