Search code examples
restspring-mvctomcatspring-bootexceptionhandler

No @ResponseBody returned from @ExceptionHandler in Spring boot app deployed in Tomcat


I have a Spring Boot web app that runs just fine from STS but shows different behavior when running in Tomcat from a WAR file.

I use Thymeleaf to handle all my web pages but I have a couple pages that are using jQuery to send async calls and make user experience more dynamic.

Anyway, I have a Controller method that calls a service method which may throw a RuntimeException which I handle this way :

@ExceptionHandler(MyRuntimeException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public @ResponseBody String handleMyRuntimeException(MyRuntimeException exception) {
    return "Oops an error happened : " + exception.getMessage();
}

In JS, I use the response body I return above to display a message on screen.

That works perfectly fine when running my app in STS but once I switch to deploy it in Tomcat the ErrorPageFilter gets invoked and in doFilter() it does:

if (status >= 400) {
    handleErrorStatus(request, response, status, wrapped.getMessage());
    response.flushBuffer();
}

In handleErrorStatus() it creates an error with the status and associated message but doesn't return my response.

I haven't figured out how to solve this and I'd really appreciate if anybody could help.

Thanks!


Solution

  • I went around this issue (I would think that is a Spring Boot issue) by doing the following.

    1. Separate Rest and Mvc controllers See my question here : Spring MVC: Get i18n message for reason in @RequestStatus on a @ExceptionHandler

    2. Inject Jackson converter and write response myself :

      @ControllerAdvice(annotations = RestController.class)
      @Priority(1)
      @ResponseBody
      public class RestControllerAdvice {
          @Autowired
          private MappingJackson2HttpMessageConverter jacksonMessageConverter;
      
          @ExceptionHandler(RuntimeException.class)
          @ResponseStatus(value = HttpStatus.BAD_REQUEST)
          public void handleRuntimeException(HttpServletRequest request, HttpServletResponse response, RuntimeException exception) {
              try {
                  jacksonMessageConverter.write(new MyRestResult(translateMessage(exception)), MediaType.APPLICATION_JSON, new ServletServerHttpResponse(response));
                  response.flushBuffer(); // Flush to commit the response
                  } catch (IOException e) {
                  e.printStackTrace();
              }
          }
      }