0

I'm working on a Spring Boot project. I'm checking out the code with class named ExceptionHandlerController provided at this post and tried it in my project. It works, but for errors with status codes 400 or 500 series I need to include the status code. When I try a simple invalid url in the browser address field, the view page, error.jsp, does render but the status code is not being accessed based on the model information. I've included the HttpServletResponse parameter to obtain the status code, but the code and message doesn't show on a 404 error. The defaultErrorHanddler method doesn't even fire on a 404 error. How do we force the method to fire on 400 and 500 errors?

ExceptionHandlerController:

@ControllerAdvice
public class ExceptionHandlerController
{
   public static final String DEFAULT_ERROR_VIEW = "error";

   private static final HashMap<Integer, String> statusCodes = new HashMap<Integer, String>();

   static {
       statusCodes.put(HttpServletResponse.SC_OK, "OK");
       statusCodes.put(HttpServletResponse.SC_CREATED, "Created");
       statusCodes.put(HttpServletResponse.SC_ACCEPTED, "Accepted");
       statusCodes.put(HttpServletResponse.SC_NON_AUTHORITATIVE_INFORMATION, "Non-authoritative Information");
       statusCodes.put(HttpServletResponse.SC_NO_CONTENT, "No Content");
       statusCodes.put(HttpServletResponse.SC_RESET_CONTENT, "Reset Content");
       statusCodes.put(HttpServletResponse.SC_PARTIAL_CONTENT, "Partial Content");         
       statusCodes.put(HttpServletResponse.SC_BAD_REQUEST, "Bad Request");
       statusCodes.put(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
       statusCodes.put(HttpServletResponse.SC_PAYMENT_REQUIRED, "Payment Required");
       statusCodes.put(HttpServletResponse.SC_FORBIDDEN, "Forbidden");
       statusCodes.put(HttpServletResponse.SC_NOT_FOUND, "Not Found");
       statusCodes.put(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "Method Not Allowed");
       statusCodes.put(HttpServletResponse.SC_NOT_ACCEPTABLE, "Not Acceptable");
       statusCodes.put(HttpServletResponse.SC_PROXY_AUTHENTICATION_REQUIRED, "Proxy Authentication Required");
       statusCodes.put(HttpServletResponse.SC_REQUEST_TIMEOUT, "Request Timeout");
       statusCodes.put(HttpServletResponse.SC_CONFLICT, "Conflict");
       statusCodes.put(HttpServletResponse.SC_GONE, "Gone");       
       statusCodes.put(HttpServletResponse.SC_LENGTH_REQUIRED, "Length Required");
       statusCodes.put(HttpServletResponse.SC_PRECONDITION_FAILED, "Precondition Failed");
       statusCodes.put(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE, "Request entity Too Large");
       statusCodes.put(HttpServletResponse.SC_REQUEST_URI_TOO_LONG, "Request-URI Too Long");
       statusCodes.put(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type");
       statusCodes.put(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE, "Requested Range Not Satisfiable");
       statusCodes.put(HttpServletResponse.SC_EXPECTATION_FAILED, "Expectation Failed");
       statusCodes.put(HttpServletResponse.SC_PRECONDITION_FAILED, "Precondition Required");           
       statusCodes.put(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Internal Server Error");
       statusCodes.put(HttpServletResponse.SC_NOT_IMPLEMENTED, "Not Implemented");
       statusCodes.put(HttpServletResponse.SC_BAD_GATEWAY, "Bad Gateway");
       statusCodes.put(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "Service Unavailable");
       statusCodes.put(HttpServletResponse.SC_GATEWAY_TIMEOUT, "Gateway Timeout");
       statusCodes.put(HttpServletResponse.SC_HTTP_VERSION_NOT_SUPPORTED, "HTTP Version Not Supported");

   }  


   @ExceptionHandler(value={Exception.class, RuntimeException.class})
   public ModelAndView defaultErrorHanddler(HttpServletRequest request, Exception e, HttpServletResponse response)
   {
       ModelAndView mav = new ModelAndView(DEFAULT_ERROR_VIEW);
       int scode = response.getStatus();
       System.out.println("scode: " + scode);
       mav.addObject("statuscode", String.valueOf(scode));
       mav.addObject("message", statusCodes.get(scode));
       mav.addObject("datetime", new Date());
       mav.addObject("exception", e);
       mav.addObject("url", request.getRequestURL());
       return mav;
   }
}

error.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML>
<html>
<head>
<title></title>
...
</head>
<body>
<div class="container">
<h1>Error Details</h1> 
    <p>Status Code: ${statuscode}</p>
    <p>Message: ${message}</p>
    <p>TimeStamp: ${datetime}</p>
    <p>Exception: ${exception}</p>
    <p>URL: ${url}</p>
    <p><a href="/">Home Page</a></p>    
</div>
</body>
</html>
Alan
  • 822
  • 1
  • 16
  • 39

2 Answers2

2

Alternatively you can annotate your method with @ResponseStatus, for example:

@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(value = {Exception.class, RuntimeException.class})
public ModelAndView defaultErrorHandler(HttpServletRequest request, Exception e) {
        ModelAndView mav = new ModelAndView(DEFAULT_ERROR_VIEW);

    mav.addObject("datetime", new Date());
    mav.addObject("exception", e);
    mav.addObject("url", request.getRequestURL());
    return mav;
}
Szymon Stepniak
  • 40,216
  • 10
  • 104
  • 131
  • Adding the @ResponseStatus(HttpStatus.BAD_REQUEST) annotation doesn't seem to help in this situation. – Alan Jul 18 '17 at 16:02
1

Add HttpServletResponse as a parameter to defaultErrorHandler method and call response.setStatusCode.

Edit:

For 404 to be treated as an exception, if using Spring Boot, set spring.mvc.throwExceptionIfNoHandlerFound = true. If not using Spring Boot, see this thread.

Abhijit Sarkar
  • 21,927
  • 20
  • 110
  • 219
  • Please see code changes above. I've included the HttpServletResponse as suggested and have added a HashMap to all the status codes supported by HttpServletResponse with appropriate message for each. But on an invalid URL 404 error, the error.jsp page still doesn't display the status code and message. Please advise. – Alan Jul 18 '17 at 15:50
  • Nothing is displayed, because when there is a 404 error the method defaultErrorHanddler doesn't even fire. I know this because I have a println statement inside the method which doesn't print to the console. – Alan Jul 18 '17 at 18:33
  • I found another post that discusses this problem: https://stackoverflow.com/questions/28902374/spring-boot-rest-service-exception-handling/30193013#30193013 It suggests the same thing you do. I added to the application.properties file spring.mvc.throw-exception-if-no-handler-found=true. That in and of itself did nothing. Then I added spring.resources.add-mappings=false. This time, at least I got something, however the status code displayed was 200, not 404. Firebug in Firefox correctly displays 404. Looks like we're on the right track but not quite there yet. Any other suggestions? – Alan Jul 18 '17 at 23:09
  • In addition, when setting both properties, the exception displayed was; org.springframework.web.servlet.NoHandlerFoundException No Handler fund for GET localhost:8080/whatever Hope this helps. – Alan Jul 18 '17 at 23:15