Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@ExceptionHandler + @ResponseStatus

I am handling my controlled exceptions using the following code:

  @ExceptionHandler(MyException.class)
       @ResponseStatus(HttpStatus.NOT_FOUND)
       public ModelAndView handleMyException(MyException e) {
          ModelAndView mav = new ModelAndView(ERROR_PAGE);
          (...)
          return mav;
       }

That is, I want to both use custom views for different errors AND use response status code for the HTTP response.

At the same time, for pure 404 I have the following config in web.xml

<error-page>
        <error-code>404</error-code>
        <location>/404</location>
    </error-page>

    <error-page>
        <error-code>400</error-code>
        <location>/400</location>
    </error-page>

Which takes to a 404 specific view.

The problem is that when a NOT_FOUND is thrown from my @ExceptionHandled method, it is not showing my custom view, debugging shows that execution actually goes through the handleMyException method, but after it's done it also goes through the method that maps the /404 in web.xml, and that is the view that gets shown.

Also if I throw a different Response Code, I get the default behavior on Exceptions, instead of my custom view.

like image 736
Gabriel Sanmartin Avatar asked Sep 13 '26 13:09

Gabriel Sanmartin


1 Answers

I can't reproduce your problem with Tomcat 6 ans Spring 2.3.4. That is correct, because accroding to Servlet specification 2.5, the deployment descriptor defines a list of error page descriptions. The syntax allows the configuration of resources to be returned by the container either when a servlet or filter calls sendError on the response for specific status codes (...)

I tracked where Spring sets response code basing on @ResponseStatus(HttpStatus.NOT_FOUND) It is here:

public class ServletInvocableHandlerMethod (...)     
private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
        if (this.responseStatus == null) {
            return;
        }

        if (StringUtils.hasText(this.responseReason)) {
            webRequest.getResponse().sendError(this.responseStatus.value(), this.responseReason);
        }
        else {
            webRequest.getResponse().setStatus(this.responseStatus.value());
        }

        // to be picked up by the RedirectView
        webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, this.responseStatus);
    }

In my case if error handler method is annotated

 @ResponseStatus(HttpStatus.NOT_FOUND)

the following branch is selected:

else {
        webRequest.getResponse().setStatus(this.responseStatus.value());
    }

Because HttpServletResponse.setStatus is called and NOT HttpServletResponse.sendError, web container ignores error page defined in <error-code>404</error-code>

I hope my explanation will be useful to track the problem yourself. I suspect somewhere HttpServletResponse.sendError is called and it triggers web container to return default error page

like image 152
michaldo Avatar answered Sep 16 '26 07:09

michaldo



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!