Zeffry Reynando
Zeffry Reynando

Reputation: 3899

Dart : Make reusable try catch block for error handling

I have simple function login request to server, in this function i have some error handling .

Example Source Code

try {
      final response = await http.post(
        '${appConfig.baseApiUrl}/${appConfig.userController}/loginUser',
        headers: appConfig.headersApi,
        body: {
          "username": username,
          "password": password,
        },
      );
      final Map<String, dynamic> responseJson = json.decode(response.body);
      if (responseJson["status"] == "ok") {
        List userList = responseJson['data'];
        List<UserModel> result = userList.map((e) => UserModel.fromJson(e)).toList();
        return result;
      } else {
        throw CustomError(responseJson['message']);
      }
    } on SocketException catch (_) {
      return Future.error(ConstText.NO_CONNECTION);
    } on TimeoutException catch (_) {
      return Future.error(ConstText.TIMEOUT_EXCEPTION);
    } on FormatException catch (_) {
      return Future.error(ConstText.FORMAT_EXCEPTION);
    } catch (e) {
      return Future.error(e.toString());
    }
  }

In above source code, I have 4 Handling error like SocketException,TimeoutException,FormatException and UnknownException. This function work fine, but if i create another function for request server i should repeat the error handling again. My question is , it's possible to make error handling reusable ? I want something like this.

Example Reusable Try Catch

requestServer(yourRequestServer) async{
  try{
    return yourRequestServer;
  }on SocketException catch (_) {
      return Future.error(ConstText.NO_CONNECTION);
    } on TimeoutException catch (_) {
      return Future.error(ConstText.TIMEOUT_EXCEPTION);
    } on FormatException catch (_) {
      return Future.error(ConstText.FORMAT_EXCEPTION);
    } catch (e) {
      return Future.error(e.toString());
    }
}

How To Use it

Future<String> testLogin(String username,String password)async{
requestServer({
final response = await http.post(
        '${appConfig.baseApiUrl}/${appConfig.userController}/loginUser',
        headers: appConfig.headersApi,
        body: {
          "username": username,
          "password": password,
        },
      );
      final Map<String, dynamic> responseJson = json.decode(response.body);
      if (responseJson["status"] == "ok") {
        List userList = responseJson['data'];
        List<UserModel> result = userList.map((e) => UserModel.fromJson(e)).toList();
        return result;
      } else {
        throw CustomError(responseJson['message']);
      }
});
}

Or if you have another suggestion , i really appreciate that. Thank's.

Upvotes: 1

Views: 1725

Answers (1)

lrn
lrn

Reputation: 71633

Reusing code is always a matter of figuring out what to keep an what to abstract away. In your case, you want to reuse the catch clauses. The thing you are abstracting over is the body of the try clause, which appears to contain some asynchronous code. Since you are abstracting over code, you'll need to pass in a function. That's the only way to make code into an argument value.

So you'll need something like:

Future<T> requestServer(FutureOr<T> computation()) {
  try {
    return await computation();
  } on SocketException catch (_) {
    throw ConstText.NO_CONNECTION;
  } on TimeoutException catch (_) {
    throw ConstText.TIMEOUT_EXCEPTION;
  } on FormatException catch (_) {
    throw ConstText.FORMAT_EXCEPTION;
  } catch (e) {
    throw e.toString();
  }
}

You can then use it as:

var result = await requestServer(() {
   final response = await http.post(...
   ...
     return result;
   ...
});

I changed the return Future.error(someString); to throw someString because it's the same thing, and the latter is much more readable. You are throwing strings here, not exception or error objects. That's a bold move, but as long as you are the one to catch them again, it's reasonable. It's not a good API for other people to have to catch.

Upvotes: 3

Related Questions