Multiple exception handlers for ResponseBody methods and normal methods - java

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 :).

Related

ExceptionHandler doesn't catch HandlerInterceptor exception if endpoint path is unknown

I have a component that implements the HandlerInterceptor interface, and implements the preHandle method. In this method I retrieve a parameter from the request, and throw an IllegalArgumentException if that parameter is missing.
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String parameter = request.getHeader("parameter123");
if (StringUtils.isEmpty(parameter)) {
throw new IllegalArgumentException("parameter123 not specified");
}
[...]
return true;
}
In another class annotated with #ControllerAdvice, I have a ExceptionHandler that catches the IllegalArgumentExceptions and turns those into a formatted response with HTTP status code 400.
When this is executed by triggering a valid path of my API, everything works just fine. Problems arise when I try to call an invalid/unexisting path of my API. The HandlerInterceptor is called and the exception is thrown but my ExceptionHandler is not triggered and the result is a basic HTTP status code 500 exception. It seems to both override the basic HTTP status 404 mechanism, while also preventing the triggering of my ExceptionHandlers (even an ExceptionHandler on Exception.class doesn't ever get called).
Any explanations regarding this behaviour are welcome ! Thanks
Although this may be an old question, I want to provide an answer for anyone who may come across it in the future.
When you raise an exception in the preHandle method of a HandlerInterceptor, it may be wrapped in another exception called NestedServletException. This is a specific exception thrown by the Spring framework.
It's worth noting that NestedServletException is a runtime exception that occurs when a servlet or filter throws an exception. It encloses the original exception and provides additional information about the location where the exception occurred.

Throwing custom errors as Json when a parameter is invalid

I am working on an API and need to throw and exception that looks like this
"error": "sortBy parameter is invalid"
}
if the sort by parameter is not one of my predetermined values,
i have a few parameters to do this for
here is what my controller looks like
#GetMapping("/api/posts")
public ResponseEntity<List<Post>> getPostResponse(#RequestParam String tag, Optional<String> sortBy,
Optional<String> direction) throws InvalidSortBy {
RestTemplate postResponseTemplate = new RestTemplate();
URI postUri = UriComponentsBuilder.fromHttpUrl("urlHere")
.queryParam("tag", tag)
.queryParamIfPresent("sortBy", sortBy)
.queryParamIfPresent("direction", direction)
.build()
.toUri();
ResponseEntity<PostResponse> response = postResponseTemplate.getForEntity(postUri, PostResponse.class);
ResponseEntity<List<Post>> newResponse = responseService.createResponse(response, sortBy, direction);
return newResponse;
}
}
ive remove the url but it works for sorting the incoming data but i need to validate and throw correct errors, im just really not sure how to do it in the format required, as json, any help appreciated
First you need to handle your exception and resolve it based on error, I would suggest you raise error codes for known application exception and resolve them in your exception handler (either by using #ControllerAdvice or #RestControllerAdvice), once you have translated error code to respective message send them as json you can refer below thread for more details on following SO thread
How to throw an exception back in JSON in Spring Boot
#ExceptionHandler
#ExceptionHandler to tell Spring which of our methods should be
invoked for a given exception
#RestControllerAdvice
Using #RestControllerAdvice which contains #ControllerAdvice to
register the surrounding class as something each #Controller should be
aware of, and #ResponseBody to tell Spring to render that method's
response as JSON

Spring ExceptionHandler but for normal beans

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.

ExceptionHandler with ResponseBody: set ResponseStatus in method body

I have a method to handle a particular class of exceptions in a Spring MVC environment.
The metod (simplified) implementation follows
#ExceptionHandler(AjaxException.class)
#ResponseStatus(value=HttpStatus.BAD_REQUEST)
#ResponseBody
public Exception handleException(AjaxException ex) {
return ex;
}
This is works fine, but to return a different ResponseStatus I have to create a new handling method.
Is it possible to change the response status inside the method body instead of using the #ResponseStatus annotation without changing the return type?
If not, is it possible to achieve the same result changing the return type (maybe serializing the exception class by myself and returning it as a string)?
Add the HttpServletResponse to the method signature and simply call the setStatus method.
#ExceptionHandler(AjaxException.class)
#ResponseBody
public Exception handleException(AjaxException ex, HttpServletResponse response) {
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
return ex;
}
Something like that should work.
Easy done, reading a little more carefully the spring documentation.
It is possible to pass the HttpServletResponse as an object parameter. In such object is possible to set the return code. The syntax is as follows:
#ExceptionHandler(AjaxException.class)
#ResponseBody
public AjaxException handleException(AjaxException ex,HttpServletResponse response) {
//test code ahead, not part of the solution
//throw new NullPointerException();
//end of test code
response.setStatus(404);//example
return ex;
}
This will return the json serialization of the exception along with the specified http return code.
EDIT:
I deleted this answer yesterday because this solution didn't seem to work. The problem was a bit trickyer: when you manage an exception this way, if the method annotated with ExceptionHandler throws an exception itself then the thrown exception is ignored and the original exception is thrown instead.
My code was somehow like the solution I posted (it threw exception in the start of the method), so I couldn't see the json output, the standard spring exception handler was fired instead. To resolve I simply trycatched the exception-throwing line and everything was ok.

Java SpringMVC Error object on Exception

I have a function that is both an HTTP endpoint and a function that I call elsewhere in the Java program:
#RequestMapping(method = RequestMethod.GET)
public #ResponseBody MyObject getObject(final HttpServletRequest request, final HttpServletResponse response)
The problem is in error handling. Functionality I would like on error:
Function called from inside Java: throws exception
Function called from HTTP: returns error json object
Trying to figure out the best way to do it. Thought about just returning a Java.lang.Object, but that's a lot of casting and type checking.
Add a Spring exception handler when calling it from web and then have the handler return your JSON.
Better approach might be to refactor the functionality into a service method then you can have the HTTP call handle the exception in it's own way and internal calls handle it however you want.
Edit
The second approach would be slightly easier to test too.

Categories

Resources