简体   繁体   中英

Java - Handle multiple exceptions in method

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.

Handling exception depends on what you want to do. Most of the time you can do nothing in current method and only able to communicate error to caller method, then you wrap and rethrow (or even declare exception in you own signature). But some time you can handle it. For example if you fail to find and open property file you can use default values.

This is my personal opinion

I would say the more exceptions you have towards the user, the more detailed errors he can develop towards. And since this is an open API meaning everybody can use it, I would go for granularity and provide with errors that can help the user in figuring out what is wrong.

If you provide one specific error on each exception the user may also provide feedback to you as a developer. He is trying your api and gets an error, he will provide you git that one exception you are providing him and then you have to figure out what is wrong.

Gief所有的信息

If you using Java 7 then you can catch multiple exceptions in the same catch clause,

 catch (APISignatureException|SignatureException e) {
    throw e;
 }

Otherwise you need to catch them separately

catch (APISignatureException e) {
    throw e;
}
catch (SignatureException e) {
    throw e;
}

You shouldn't use the super type which is Exception so you don't end up catching exceptions that you don't want to handle

Right now all of your exceptions are caught by catch (Exception e) { and they then all throw a APISignatureException .

What you really want to do is catch your specific exception, like this:

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 (APISignatureException e) {
     //Handle exception
}
catch (InvalidKeyException e) {
     //Handle exception
}

Also, if you catch specific exceptions there's no need to throw another exception.

You can also combine catching of multiple exceptions like this:

catch (APISignatureException|InvalidKeyException e) {
    // Handle exception

}

You can create custom exceptions by extending the Exception class:

class APISignatureException extends Exception {
  // empty constructor
  public APISignatureException () {}

  //constructor that takes a string message
  public APISignatureException (String message)
  {
      super(message);
  }

}

For more details take a look at Exception

You need to understand this please look into it.

Basic understanding is

try { 
   //Something that can throw an exception.
} catch (Exception e) {
  // To do whatever when the exception is caught.
} 

There is also an finally block which will always be execute even if there is an error. it is used like this

try { 
   //Something that can throw an exception.
} catch (Exception e) {
  // To do whatever when the exception is caught & the returned.
} finally {
  // This will always execute if there is an exception or no exception.
}

In a particular case of scanner you can have the following exceptions ( link ).

InputMismatchException - if the next token does not match the Integer regular expression, or is out of range
NoSuchElementException - if input is exhausted
IllegalStateException - if this scanner is closed

So you would need to catch exceptions like

try { 
   rows=scan.nextInt();
} catch (InputMismatchException e) {
  // When the InputMismatchException is caught.
  System.out.println("The next token does not match the Integer regular expression, or is out of range");
} catch (NoSuchElementException e) {
  // When the NoSuchElementException is caught.
  System.out.println("Input is exhausted");
} catch (IllegalStateException e) {
  // When the IllegalStateException is caught.
  System.out.println("Scanner is close");
} 

When I write an API, I catch lower level exceptions and throw a related API exception. In that way you are not hiding anything and you allow API users to catch one related exception.

If the API is large, you may want several API exceptions that are all subclasses of a common API exception. For example:

public class APIException { ...
}

public class APISignatureException extends APIException { ...
}

public class APISomeException extends APIException { ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM