Should I provide layer-specific exceptions in a SpringBoot backed REST Api? - spring

I have a SpringBoot based REST Api structured as follows :
Rest Controller -> Service -> Repository
and I'm wondering how exactly to handle exceptions "properly".
For instance, let's say someone calls a /myresources/{id} endpoint with a non-existant {id}. The call gets delegated to the service which in turns tries to get the MyResource from the Repository. It fails and returns null. The Service then throws a MyResourceNotFoundException.
Now I want a specific format for my REST errors so I have a #ControllerAdvice ResponseEntityExceptionHandler which handles the custom serialization of these exceptions (#ExceptionHandler(MyResourceNotFoundException.class)).
Fine.
But this is going to result in a lot of handling/translation for each different custom exception. So I thought I could generify this by adding HttpStatus codes and messages to a base abstract exception class which MyResourceNotFound and others would extend and override. Thus the ResponseEntityExceptionHandler would simply handle building my custom error DTO in a standard way.
But then I realised that I'm adding REST concepts to an exception thrown at the service level. These shouldn't be aware of such notions...
Maybe I should catch the MyResourceNotFoundException in the Controller and throw another layer-specific exception which contains the appropriate messages and HttpStatus etc. in order to handle this generically in the ResponseEntityExceptionHandler...
What are your thoughts on this ?

You can generalize the exception as XYZMicroserviceException.
class XYZGenericException extends Exception{
String message;
custom error details....
XYZgenericException(errorMessage, custom error Details..){
this.message=errorMessage;
.......
}
}
and you can surround the suspected call which would lead to exception with try block and raise the generic exception in catch block that can be handled in global exception handler.
try{
xyz.abcMethod() // may give some exception
}
catch(Exception e){
throw new XYZGenericException(.........)
}
In the exception handler class with #restcontrolleradvice you can annotate the methods with the type of specific exception class to be handled.

Related

Is it possible to throw checked exception in Spring AOP?

Hello I'm trying to throw custom Exceptions from my #Aspect, but no matter what exception I throw, my Aspect is throwing UndeclaredThrowableException,
My custom exception is
public class AuthorizationException extends RuntimeException {
public AuthorizationException(String msg) {
super(msg);
}
}
And my Aspect is
#Around("#annotation(MyCustomAnnotation)")
public Object validateAspect(ProceedingJoinPoint pjp throws Throwable {
// for testing purpose directly throwing
throw new AuthorizationException("My custom error);
}
And I'm supposed to get AuthorizationException but I'm getting UndeclaredThrowableException
I have tried directly throwing RuntimeError from the validateAspect method and changed throws Throwable to throws AuthorizationException but still same. Tried to catch it with Global Exception handler as well but the custom string message is always null.
Your aspect is throwing a checked exception which is not declared in the intercepted method or one of its callers along the call stack. Unfortunately, you did not even post a stack trace, which is not very helpful. An MCVE is also missing, so I can only answer the question as generically as you were asking it.
Your claim that throwing a runtime exception makes an UndeclaredThrowableException appear is simply false, unless part of the application, e.g. another aspect or interceptor applied via dynamic proxies, catches that runtime exception and throws a checked exception instead. If you disagree, please prove your claim by editing your question and adding an MCVE or linking to one on GitHub.
Please read this tutorial in order to better understand your own situation:
https://www.baeldung.com/java-undeclaredthrowableexception

Spring controller exception handling locally and globally

I would like some advice on how to achieve the following. I'm not providing code, as my problem is theoretical, but upon request I can. So this is the situation:
I have multiple controllers, each can throw XYException
I have a #ControllerAdvice class, in which I have an #ExceptionHandler watching for XYExceptions. When it happens, it prints out "XY".
In one (and only one) controller, when XYException is thrown, I want to do some additional task (let's say, do something that only that controller can do), and then I want the exception to be "passed on" to the global handler mentioned above.
I know I can catch the exception, do the desired task in catch block, and then re-throw the exception so the global handler can catch it, but what if I have 23 methods in the controller potentially throwing XYExceptions, and I do not want to put try-catch blocks in all 23 methods.
What is the clean Spring way of achieving this?
You could use AOP to achieve that. You'd write a method that intercepts the method inside that controller and when they throw an exception, you're aop method would run and you can do your stuff and then the exception would go in your handler class.
To achieve that you should have a class and anotate it with #Aspect and #Component
then have a method anotated with #AfterThrowing and setting the pointcut which will intercept when the given method throws an exception.
Look into Aspect Oriented Programming and Aspectj for more info.
The easy way to handle this case in ControllerAdvice is checking the stacktrace where the exception originated.
#ExceptionHandler(Exception.class)
public String handleExc(HttpServletRequest req, HttpServletResponse res, Exception e) {
if ( /*Have all null and safe check */ e.getStackTrace()[0].contains("MyController")) {
// Do your exception handling
}
}

Is there a way to intercept Undertow RequestTooBigException in spring-ws

I have spring boot application that provides some rest api and soap interface using spring-ws.
I'm restricting all post requests to less than 50MB using
server.undertow.max-http-post-size=50MB.
The restriction works but not the error handling.
Tried using a SoapFaultMappingExceptionResolver and mapping RequestTooBigException as an exception inside that. That didn't work because I think this is blocked by undertow and that's before the soapExceptionResolver is triggered.
Tried using a #ExceptionHandler but same result.
Since it's a soap request the accepted type is not json and the default spring exception handler tries to return json with the details of the exception, thus resulting in the following in the logs:
2019-09-17 09:24:11.715 WARN 12564 --- [XNIO-1 task-23] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation]
2019-09-17 09:24:11.950 ERROR 12564 --- [XNIO-1 task-75] io.undertow.servlet.request : UT015012: Failed to generate error page /error for original exception: io.undertow.server.RequestTooBigException: UT000020: Connection terminated as request was larger than 52428800. Generating error page resulted in a 406.
Is there a way to either intercept that exception for the WS services, or to make the spring default error handler to return the exception as XML (just for the ws)?
Tried using a #ExceptionHandler but same result.
I suppose that Spring considers that exception as a implementation detail of Undertow during the request processing that it performs and as a consequence it doesn't want to allow it leak above.
In Spring, you have exception handing and exception resolving.
Exception handlers are not always well designed to define a custom exceptions handling whether Spring implementation resolves them for you and apply a specific processing for. There you look like in this case.
But all is not lost. A clean and possible approach (not tested) is to implement your own exception resolver to prevent Spring to map that Undertow exception into a HttpMediaTypeNotAcceptableException.
Since you use spring-ws, you should implement org.springframework.ws.server.EndpointExceptionResolver or more simply AbstractEndpointExceptionResolver or SimpleSoapExceptionResolver.
Implement resolveExceptionInternal() to perform the logic in case of that exception is thrown :
public class MyEndpointExceptionResolver extends AbstractEndpointExceptionResolver {
public MyEndpointExceptionResolver(){
setOrder(0);
}
#Override
protected abstract boolean resolveExceptionInternal(MessageContext messageContext, Object endpoint, Exception ex){
if (ex instanceof RequestTooBigException){
// create the response message with the exception
WebServiceMessage message = ...
// set it in the MessageContext
messageContext.setResponse(message);
return true; // resolved
}
return false; // leave the default resolver doing its job
}
}
Note that you may have multiple active exception revolvers for a same layer/concern. So you have to set the precedence of this one over the default one.
setOrder(0) does that.

Purpose of throwing exception by Spring controller function

I have come across few Spring contoller's function, which are throwing IOException.
#RequestMapping(method = ***)
#ResponseBody
public List<Offering> getOfferingDetailsList(HttpServletResponse response, #PathVariable("productIds") String productIdString, HttpServletRequest request) throws IOException
I doubt about use of such exception throwing, when no one above is catching and handling it. Is it fine to set response status like "response.setStatus(HttpStatus.SC_NOT_FOUND)" in place of throwing such exception ? What is the standard way of handling exception in controller ?
it is always good to have common exception handling functionality , so that we can make our controller code free from exception handling , by externalize to common exception handling functionality, i have come across this interesting topic for this purpose
http://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
and also use a global exception handler that will do that for all the exceptions of all the controller methods. That will centralize the exception handling, prevent massive code duplication, and avoid cluttering your controller methods.
Look at the #ControllerAdvice and #ExceptionHandler annotations.
A fairly standard way of handling exceptions in Spring Controllers is to use #ExceptionHandler.
Check out this post for more details

Spring #ExceptionHandler handling multiple kinds of exceptions

I can't figure out how to handle more than one kind of exception by #ExceptionHandler.
I need to programmatically deal with these exceptions, for this I'd need a shared reference. Is this done via this reference "Exception ex" ? I don't think so, cause the exception is not caught like this, how would I do it then ?
I can't put all the exception references as arguments to the handler method, it wouldn't make sense, it can't be programmatically dealt with. I need a shared reference so that I could use "instanceof" on it or just send it somewhere else as a general "Exception"
#ExceptionHandler({DescriptionCstOrderException.class, SpecializationCstOrderException.class, NoUploadFileException.class,
DeadLineCstOrderException.class, DocumentCstOrderException.class, CommentCstOrderException.class})
public String handleFormException(Exception ex, ActionRequest actionRequest) {
logger.error(ex.getMessage());
SessionErrors.add(actionRequest, ex.getClass().getName());
return "mainOrderForm";
}
Additional question: what if I wanted to handle org.springframework.web.multipart.MaxUploadSizeExceededException, that is not thrown from any method of the handler? Because #ExceptionHandler catches only exceptions that are thrown from one of the handler methods.
The exceptionHandler method could be placed into some extended parent controller but if one uses only defaultAnnotationHandlerMapping... ?
Appreciate any help, I'm going crazy, this is very frustrating....
The #ExceptionHandler value can be set to an array of Exception types.
The implementation of using exception array as mentioned in Spring documentation will be like:
#ExceptionHandler({
NotFoundException.class,
MissingServletRequestParameterException.class
})
The #ExceptionHandler value can be set to an array of Exception types. If an exception is thrown matches one of the types in the list, then the method annotated with the matching #ExceptionHandler will be invoked. If the annotation value is not set then the exception types listed as method arguments are used. See the documentation for details.
Your question is rather confusing but your exception handler method will only handle one exception at a time. It will not catch multiple exceptions and then pass both of them into your handleFormException() method. If you need to handle these exception types differently then you should create an exception handler method for each one, specify an argument of that specific Exception type to your method, and then do the appropriate handling. For example:
#ExceptionHandler(DescriptionCstOrderException.class)
public String handleDescriptionCstOrderException(DescriptionCstOrderException exception, ActionRequest actionRequest) {...}
#ExceptionHandler(SpecializationCstOrderException.class)
public String handleSpecializationCstOrderException(SpecializationCstOrderException exception, ActionRequest actionRequest) {...}
// and so on...
Please refer to the Spring documentation for further information:
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-ann-exceptionhandler

Resources