Reputation: 20966
I went through Spring exception handling documentation and I didn't grasp the idea how to deal with ajax calls unhandled exeptions.
What would be convinient method to handle both page request unhandled exceptions and ajax calls unhandled exceptions in one application?
This could be a problem since global exception handler also catch ajax calls and return 'dedicated error page' with lots of stuff and thus prevent to deliver slim error reply for the ajax error callback.
Upvotes: 2
Views: 3740
Reputation: 20966
I came on to the solution do split apart ajax calls and ordinal page request in global exception handler using request headers. There are also different error response for ajax invalid user input type of exceptions and internall server errors.
...
public class Application extends SpringBootServletInitializer {
@Bean(name = "simpleMappingExceptionResolver")
public SimpleMappingExceptionResolver createSimpleMappingExceptionResolver() {
SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
r.setDefaultErrorView("forward:/errorController");
return r;
}
@Controller
public class ErrorController {
public static final Logger LOG = Logger.getLogger(ErrorController.class);
@RequestMapping(value = "/errorController")
public ModelAndView handleError(HttpServletRequest request,
@RequestAttribute("exception") Throwable th) {
ModelAndView mv = null;
if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
if (isBusinessException(th)) {
mv = new ModelAndView("appAjaxBadRequest");
mv.setStatus(BAD_REQUEST);
} else {
LOG.error("Internal server error while processing AJAX call.", th);
mv = new ModelAndView("appAjaxInternalServerError");
mv.setStatus(INTERNAL_SERVER_ERROR);
}
mv.addObject("message", getUserFriendlyErrorMessage(th).replaceAll("\r?\n", "<br/>"));
} else {
LOG.error("Cannot process http request.", th);
mv = new ModelAndView("appErrorPage");
mv.addObject("exeption", th);
}
return mv;
}
}
Upvotes: 1
Reputation: 17075
There are three ways of handling exceptions in rest controllers:
Annotate your exceptions with @ResponseStatus and proper HTTP result code which should be returned when given exception is thrown.
Eg. If PersonNotFoundExcepition is thrown, return http 404 to the client (not found)
@ResponseStatus(HttpStatus.NOT_FOUND)
public class PersonNotFoundException { … }
Another way is to have a method annotated with @ExceptionHandler in your controller. In the @ExceptionHandler annotation's value you define which exceptions should be caught. Additionally, you can add @ResponseStatus annotation on the same method to define which HTTP result code should be returned to the client.
@ResponseStatus(HttpStatus.NOT_FOUND)
@ExceptionHandler({PersonNotFoundException.class})
public void handlePersonNotFound() {
...
}
Preferred method: Implementing ResponseEntityExceptionHandler interface as a @ControllerAdvice. This way you can apply exception handling logic to all controllers with centralized exception handling. You can read more in a tutorial here.
@ControllerAdvice
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
...
@Override
protected ResponseEntity<Object> handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
String unsupported = "Unsupported content type: " + ex.getContentType();
String supported = "Supported content types: " + MediaType.toString(ex.getSupportedMediaTypes());
ErrorMessage errorMessage = new ErrorMessage(unsupported, supported);
return new ResponseEntity(errorMessage, headers, status);
}
...
}
Note that you should not return generic 500 - Internal server error
on all types of exceptions. Generally you want to have 400s range of results for client errors - wrong request. And 500s range of result codes to server side errors. Furthermore, it is better to return more specific codes depending on what happened instead of just 400 or 500.
Upvotes: 2