I'm developing a public API for people to use in their applications. I'm currently trying to figure out the best way to handle exceptions. For example, the following code snippet throws four different kinds of exceptions:
Signature s = Signature.getInstance("SHA1withRSA"); //NoSuchAlgorithmException
s.initSign(keyChain.getPrivateKey()); //InvalidKeyException
s.update(plainText.getBytes("UTF-8")); //UnsupportedEncodingException
byte[] signature = s.sign(); //SignatureException
String signedData = base64Encoder.encode(signature);
My question is, how do I handle these in the best possible way?
One way I came up with was catching exceptions and throwing a custom exception:
public void signRequest(Request request) throws APISignatureException {
try {
...
Signature s = Signature.getInstance("SHA1withRSA"); //NoSuchAlgorithmException
s.initSign(keyChain.getPrivateKey()); //InvalidKeyException
s.update(plainText.getBytes("UTF-8")); //UnsupportedEncodingException
byte[] signature = s.sign(); //SignatureException
String signedData = base64Encoder.encode(signature);
...
}
catch (Exception e) {
throw new APISignatureException("Failed to create signature", e);
}
}
Is this a good way of handling exceptions for an open API?
It's nearly a reasonable way! I'd say that if you replaced your catch
with a list of specific exceptions, rather than a blanket Exception
, it would be completely defensible.
It would also be quite reasonable to let all four types propagate upwards, though. It depends on what you want. If the user wants to have immediate access to the reason for failure, you might leave the types unaltered and uncaught. If you want this layer of abstraction, where the user just gets a "something went wrong with the signature" type, but can still drill down into the details, then the structure you've got is ideal.
The point is that you're not hiding anything: the original exception is still buried in the new one, and available to the caller.
Catching general Exceptions is usually a bad idea.
You can do this instead (Java 7):
public void signRequest(Request request) throws APISignatureException {
try {
Signature s = Signature.getInstance("SHA1withRSA"); //NoSuchAlgorithmException
s.initSign(keyChain.getPrivateKey()); //InvalidKeyException
s.update(plainText.getBytes("UTF-8")); //UnsupportedEncodingException
byte[] signature = s.sign(); //SignatureException
String signedData = base64Encoder.encode(signature);
}
catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException | SignatureException e) {
throw new APISignatureException("Failed to create signature", e);
}
}
But ask yourself what the client is going to do with this as you're forcing him to still catch your Exception.
If the client shouldn't be concerned because in general this won't go wrong, you can throw an unchecked Exception:
public void signRequest(Request request) {
try {
Signature s = Signature.getInstance("SHA1withRSA"); //NoSuchAlgorithmException
s.initSign(keyChain.getPrivateKey()); //InvalidKeyException
s.update(plainText.getBytes("UTF-8")); //UnsupportedEncodingException
byte[] signature = s.sign(); //SignatureException
String signedData = base64Encoder.encode(signature);
}
catch (NoSuchAlgorithmException | InvalidKeyException | UnsupportedEncodingException | SignatureException e) {
throw new RuntimeException("Failed to create signature", e); // This doesn't need to be explicitly caught
}
}
What I mean is, if the signing goes wrong, there's probably no need for the user to continue his application as if nothing happened by catching your own Exception. He need to change some configuration and run his application again. The RuntimeException
will just propagate until a more general catcher is catching it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With