Spring ExceptionHandler but for normal beans - java

I have been able to successfully use #ExceptionHandler annonated methodsorg.springframework.web.bind.annotation.ExceptionHandler in Controller Classes in my Spring projects to handle exceptions thrown by spring #RestController
Working example:
#Validated
#RestController
#RequestMapping(value = UrlsProperties.API_PATH, produces = MediaType.APPLICATION_JSON, consumes = { MediaType.APPLICATION_JSON })
#Api(value = "MyController", description = "MyController processing and forwarding controller")
public class MyController {
private static Logger log = LogManager.getLogger(MyController.class);
...
#JsonFormat
#ExceptionHandler(MissingServletRequestParameterException.class)
public ResponseMessage handleMissingParams(MissingServletRequestParameterException ex) {
String name = ex.getParameterName();
log.error(name + " parameter is missing");
return new ResponseMessage(400, ex.getMessage());
}
}
I am trying to achieve the same way of exception handling but for a normal bean, [ not a controller ]. Simply adding an #ExceptionHanlder annotated method did not seem to catch the exceptions thrown by that bean's methods.
My question is how to handle exceptions thrown by a bean by writing a method inside this bean?

#ExceptionHandler annotation is not for general exception handling. It's used in controllers to convert an exception into a proper HTTP response. It won't work for normal beans, because only controllers return a response.
If any code (doesn't need to be in a bean) throws an exception and you don't handle it, it would eventually propagate up to your controller's exception handler and it would be converted to a response. That would be poor design though, as you should handle exceptions as early as you can.
What you can do is create exceptions that are meant to be propagated to your exception handlers. Your code catches an exception, then re-throws it wrapped into your own exception (such as IllegalRequestException). The handler then returns an error code and details to the caller.

Related

Stacktrace of custom exception is not printed in Spring Boot 2.3

Error stacktrace is not printed in console for the custom exception that is annotated with #ResponseStatus
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public class InternalErrorException extends RuntimeException {
public InternalErrorException(String message) {
super(message);
}
public InternalErrorException(String message, Throwable throwable) {
super(message, throwable);
}
}
Throwing exception like throw new InternalErrorException("error", e), never get the stacktrace printed in the console unlesss I remove the annotation #ResponseStatus
How could I get it printed while keeping the annotation #ResponseStatus?
See Annotation Type ResponseStatus API doc.
Warning: when using this annotation on an exception class, or when setting the reason attribute of this annotation, the HttpServletResponse.sendError method will be used.
With HttpServletResponse.sendError, the response is considered complete and should not be written to any further. Furthermore, the Servlet container will typically write an HTML error page therefore making the use of a reason unsuitable for REST APIs. For such cases it is preferable to use a ResponseEntity as a return type and avoid the use of #ResponseStatus altogether.
HttpServletResponse.sendError does not throw your error and I guess it is never logged because of that.
Maybe you want to implement exception handler for that exception to get it logged.
Related question

Handle exceptions in a rest controller in Spring Boot

I create REST web-service with Spring Boot.
I would like to know what is a better way to handle exceptions in a controller. I have seen other questions and didn’t found an answer.
My controller:
#GetMapping
public ResponseEntity<?> saveMyUser(){
MyUser myUser = new MyUser(“Anna”);
//throws SQLException
MyUserDetails userDetails = userService.saveMyUser(myUser);
//if successful
return ResponseBody.ok(userDetails);
}
saveMyUser() method of UserService:
public MyUserDetails saveUser(MyUser) throws SQLException {...}
So at this point I have at least 2 simple options:
Add exception to method signature.
Here I may rely on Spring Boot to pass all information about exception and status code to a client. However do not know if it is a reliable approach.
Surround with try/catch and pass all information about exceptions manually.
What is a better simple way?
You can create an additional class with #ControllerAdivce annotation and later you will be able to write custom response logic for each exception e.g:
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler({SQLException.class})
public ResponseEntity<Object> sqlError(Exception ex) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Some SQL exception occured");
}
}
Also, you can extend ResponseEntityExceptionHandler and override the default behavior for mapping from exceptions to HTTP response.
Also, take a look at this, it holds very usefull information for your case.

How could we use #ExceptionHandler with spring web flux?

In spring web we could use annotation #ExceptionHandler for handling server and client errors for controllers.
I've tried to use this annotation with web-flux controller and it still worked for me, but after some investigation I've found out here
The situation with Spring Web Reactive is more complicated. Because
the reactive streams are evaluted by a different thread than the one
that executes the controllers method, the exceptions won’t be
propagated to the controller thread automatically. This means that the
#ExceptionHandler method will work only for exceptions that are thrown
in the thread that handles the request directly. Exceptions thrown in
the stream will have to be propagated back to the thread if we want to
use the #ExceptionHandler feature. This seems like a bit of a let down
but at the time of writing this Spring 5 is still not released so
error handling might still get better.
So my question is how to propagate back exception to the thread. Is there a good example or article about using #ExceptionHandler and Spring web flux?
Updated:
From spring.io it looks like it's supported, but still lack general understanding
Thanks,
Now it is possible to use the #ExceptionHandler as well as #RestControllerAdvice or even #ControllerAdvice in Spring WebFlux.
Example:
Add the webflux dependency
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
Create your class ExceptionHandler
#RestControllerAdvice
public class ExceptionHandlers {
private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionHandlers.class);
#ExceptionHandler(Exception.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public String serverExceptionHandler(Exception ex) {
LOGGER.error(ex.getMessage(), ex);
return ex.getMessage();
}
}
Create a Controller
#GetMapping(value = "/error", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public Mono<String> exceptionReturn() {
return Mono.error(new RuntimeException("test error"));
}
Example extracted here:
https://ddcode.net/2019/06/21/spring-5-webflux-exception-handling/
You can use #ExceptionHandler annotated methods to handle errors that happen within the execution of a WebFlux handler (e.g., your controller method). With MVC you can indeed also handle errors happening during the mapping phase, but this is not the case with WebFlux.
Back to your exception propagation question, the article you're sharing is not accurate.
In reactive applications, the request processing can indeed hop from one thread to another at any time, so you can't rely on the "one thread per request" model anymore (think: ThreadLocal).
You don't have to think about exception propagation or how threads are managed, really. For example, the following samples should be equivalent:
#GetMapping("/test")
public Mono<User> showUser() {
throw new IllegalStateException("error message!");
}
#GetMapping("/test")
public Mono<User> showUser() {
return Mono.error(new IllegalStateException("error message!"));
}
Reactor will send those Exceptions as error signals as expected in the Reactive Streams contract (see the "error handling" documentation section for more on that).
not an exact answer to the original question, but a quick way to map your exceptions to http response status is to throw org.springframework.web.server.ResponseStatusException / or create your own subclasses...
Full control over http response status + spring will add a response body with the option to add a reason.
{
"timestamp": 1529138182607,
"path": "/api/notes/f7b.491bc-5c86-4fe6-9ad7-111",
"status": 400,
"error": "Bad Request",
"message": "For input string: \"f7b.491bc\""
}
The following global error handler did the trick for me:
import org.springframework.web.server.ResponseStatusException;
#Slf4j
#RestControllerAdvice
#ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public class MyCustomReactiveErrorHandling {
#ExceptionHandler(MyCustomNotFoundException.class)
public void handleMyCustomException(MyCustomNotFoundException ex) {
throw new ResponseStatusException(404, "Data not found!", ex);
}
}
Throwing my exceptions returns the correct http status code at the rest service.

Multiple exception handlers for ResponseBody methods and normal methods

I have a requirement where I want to handle how exceptions are returned. Depending on the type of the controller method if it is annotated with #ResponseBody a json string should be returned. Additionally if it is a String returning method a jsp error page should be returned.
However it seems that I am unable to define two global exception handler (with ControllerAdvice) both which handles Exception.class but one returns a ModelAndView and the other is annotated with #ResponseBody. I get an exception mentioning that it is too ambigious.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver#0': Invocation of init method failed; nested exception is java.lang.IllegalStateException: Ambiguous #ExceptionHandler method mapped for [class java.lang.Exception]
The below code is an example of an ideal situation where both scenarios are handled
The methods
#RequestMapping(value = "/{pathValue}/page", method = RequestMethod.GET)
public String getPage(#PathVariable(value="pathValue") String pathValue, ModelMap model) throws Exception {
#RequestMapping(value = "{pathValue}/jsonData", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody ModelMap getJson(#PathVariable(value="pathValue") String pathValue) throws Exception {
The Exception Handlers
#ExceptionHandler(Exception.class)
#ResponseBody ErrorInfo handleBadRequest(HttpServletRequest req, Exception ex) {
#ExceptionHandler(value = Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
in the above example the getPage method should be handled by handleBadRequest and getJson should be handled by defaultErrorHandler
Is there any way to configure two global exception handlers which both handle the same Exception class and returns a page or json based on the Controller method type.
Not out of the box I suppose, because AbstractHandlerMethodExceptionResolver cannot do what you want. If I'm not mistaken then interface has no argument that could indicate method which has thrown particular instance of exception.
I remember that creating wrapping implementation (for example aspect) which was configurable with particular exception type has helped me in the past. Such a implementation will wrap every controller's method's call and catch all exceptions just to wrap it in configurable exception type and rethrow. In your case it would be something like JsonSomethingException and ResourceSomethingException.
Another option might be opposite (more low-level) approach. Note that in exception handling method you receive HttpServletRequest instance, so it is possible to browse some data about request. In you case #RequestMapping bases on this data to determinate which method should be called - you can do the same in exception handler! The best (imho) apprach would be to distinguish json and html by Content-Type header of HTTP protocol. In such situation writing single global error handler, which returns appropriate ResponseEntity based on Content-Type will be quite easy. Especially with addition of some json parser (for example Jackson).
Good luck :).

REST resource exception interceptor

I am trying to determine if it is possible to setup an interceptor like solution on a REST resource such that if an exception is thrown I can log the exception and change the response returned. I basically don't want to wrap all my REST resources with try/catch blocks. If a REST resource was managed I would just use an #Interceptor on all of my calls but since it is not managed that seems to be out of the question.
You can use an implementation javax.ws.rs.ext.ExceptionMapper. Let's suppose that your code might throw a YourFancyException from the resources. Then you can use the following mapper:
#Provider
public class YourFancyExceptionMapper
implements ExceptionMapper <YourFancyException> {
#Override
public Response toResponse(YourFancyException exception) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(exception.getMessage()).build();
}
}
Don't forget to annotate the mapper with #Provider and to make your resources methods to throw YourFancyException.

Categories

Resources