Search code examples
httpjsftomcat7servlet-3.0prettyfaces

error-page configuration in web.xml transforms the HTTP code returned in case of 404


I work on a JSF application powered by Tomcat 7.0.35. I would like to create a custom error page and have therefore played with the <error-page> tag in the web.xml configuration file.

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

It seems to work in the sense that in case of a 404, the page returned has the correct HTTP body. However the HTTP return code is 200. The expected 404 was correctly received when <error-page> was not configured.

The same soft-404 problem happens if <error-code> is not specified.

<error-page>
    <location>/error</location>
</error-page>

I am looking for a way to configure this error page without losing the error code.

Two more pieces of information that might be useful :

  • The JSF project stage is Production
  • Pretty URLs are being handled by Pretty Faces 3.3.3 with the following

    @Named(value = "error")
    @RequestScoped
    @URLMappings(mappings = {
        @URLMapping(id = "error", pattern = "/error", viewId = "/faces/error.xhtml")})
    public class ErrorController implements Serializable {
    

Solution

  • Sometimes containers do not seem to behave very nicely when you have an Error page handled by a servlet filter. In this case it looks like you probably have mapped "/error" with PrettyFaces, and are forwarding that internally to another resource. (PrettyFaces works with Servlet forwards)

    I believe what occurs here, is the container sees that new Forward, forgets that it was actually showing an error page, and sends a 200 status because the Forwarded request was successful.

    What you will need to do to resolve this, is add an <action>#{bean.send404}</action> that sets the 404 status in your error page prettyfaces mapping.

    <url-mapping>
       <pattern value="/error" />
       <view-id="/faces/error.xhtml" />
       <action>#{bean.send404}</action>
    </url-mapping>
    

    Then your bean method will need to get a hold of the HttpServletResponse, which ou can usually get from the FacesContext:

    HttpServletResponse response = (HttpServletResponse)FacesContext.getCurrentInstance()
             .getExternalContext().getResponse();
    response.sendError(HttpServletResponse.SC_NOT_FOUND);
    

    That should do the trick for you.