Question

I have Java microservice that exposes multiple APIs. Under each API there are many exceptions could be thrown. The last method that catches them all has big catch block ...

catch(exc1 e1 | exc2 e2 | exc3 e3 ..... | exc10 e10){ ...}

They are all of the same level. Now I thought of combining them all under one exception. So, exc1 to exc10 extend Api1Exception. So I could jus catch it like this:

catch(Api1Exceptione){ ... }

Cleaner, and easier to control. However. I realized other APIs share some of Api1Exception exceptions. Let's say API2, is throwing something like:

catch(exc13 e13 | exc2 e2 | exc16 e16 ..... | exc19 e19){ ...}

I do not want to catch the entire Api1Exception, because some of them are not thrown in that API2 logic. So, I had to create another exception Api2Exception and I had create another copy of say exc2 to be a child of Api2Exception. This to me looks like redundant code, and I thought there should be better way to handle this. Any idea?


UPDATE

Following the answer, this is what I did:

  • created CustomException that extends Exception, with field errorCode
  • Every custom exception I create, extends CustomException and pass the errorCode along with the message
  • I catch it like this:

    catch(CustomException ce){    
        handleCustomException(ce)    
    }
    
  • handling method:

    if(ce.getErrorCode().eqauls(SOME_ERROR_CODE){    
            //do something    
    }    
    else if (ce.getErrorCode().eqauls(SOME_ERROR_CODE){    
            //do something else    
    }    
    //.......    
    else{    
         throw new Exception ....    
    }    
    

P.S.: Not sure why my question got downvoted. Whoever did it, it would be great if you provide some feedback.

Was it helpful?

Solution

The standard approach is to create one class ApiException that is thrown by all public APIs of the library, and wrap the underlying exception into it:

try {
}
catch (Exception e) {
    throw new ApiException(e);
}

If a user's catch block needs special handling for some of the underlying exceptions, it can unwrap it (with .getCause()) and if it's not what's needed, rethrow the original exception.

The idea is that if there are all sorts of possible underlying causes unrelated to code's logic, the code can't do anything intelligent about it, and this rather requires the intervention of the app's admin. So it just passes it upstream, possibly wrapping with its own type, with the idea that it will eventually be logged.

If the code is supposed to actually be able to resolve the problem, the function should return concrete exceptions that are in line with the nature of its work, and no more than a few of them. Failing to keep the number small in this case means you probably need to split the function into a few more specialized ones which the user will call in sequence.

Licensed under: CC-BY-SA with attribution
scroll top