Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you make invalid urls ending in .jsp use the spring 404 page?

404 pages are working properly across my site for all url's unless it ends in .jsp. In that case I get the generic jboss 404 page instead of my styled Spring 404 page.

I have the following in my web.xml:

<error-page>
    <error-code>404</error-code>
    <location>/WEB-INF/error/404.jsp</location>
</error-page>

Edited with full web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <servlet>
        <servlet-name>mainServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/spring/servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>mainServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>
            /WEB-INF/spring/root-context.xml,
            /WEB-INF/spring-security.xml
        </param-value>
    </context-param>

    <welcome-file-list>
        <welcome-file></welcome-file>
    </welcome-file-list>

    <filter>
        <filter-name>springSecurityFilterChain</filter-name>
        <filter-class>
            org.springframework.web.filter.DelegatingFilterProxy
        </filter-class>
    </filter>

    <filter-mapping>
        <filter-name>springSecurityFilterChain</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <error-page>
        <error-code>400</error-code>
        <location>/WEB-INF/error/400.jsp</location>
    </error-page>

    <error-page>
        <error-code>403</error-code>
        <location>/WEB-INF/error/403.jsp</location>
    </error-page>

    <error-page>
        <error-code>404</error-code>
        <location>/WEB-INF/error/404.jsp</location>
    </error-page>

    <error-page>
        <error-code>405</error-code>
        <location>/WEB-INF/error/405.jsp</location>
    </error-page>

    <error-page>
        <exception-type>java.lang.Exception</exception-type>
        <location>/WEB-INF/error/error.jsp</location>
    </error-page>

    <context-param>
        <param-name>defaultHtmlEscape</param-name>
        <param-value>true</param-value>
    </context-param>
</web-app>
like image 877
oojoe Avatar asked Nov 22 '13 22:11

oojoe


1 Answers

The problem is probably that your web.xml is pointing directly to your jsp pages instead of directing to a spring controller that can then point to the appropriate view.

I'd be curious to also see your rootContext.xml to see how you have your view resolver set up. Cause maybe spring is getting confused... There's only a couple ways that the App Container (JBoss) would end up handling the error, and that is if either the request doesn't even match the Spring context servlet or if there is no error handler for the error type generated (is it possible that the attempt to handle "asdlfasdf.jsp" ends up causing another type of exception that is not getting logged and then is getting tossed up to JBoss and handled as a 404?).

But so you have something to compare to - Here's how I have mine set up:

I have a single error.jsp page in my views directory and set up the view resolver similar to the following:

<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
    <property name="prefix" value="/WEB-INF/views/"/>
    <property name="suffix" value=".jsp"/>
</bean>

Then, I have a base PageController class that I extend for all my other "user facing" controller classes. That class has a method that handles the "/error/404", "/error/403" style error page patterns:

@RequestMapping(value = "/error/{code}")
public String errorPage(Principal principal, Model model, @PathVariable("code") int code) {
    model.addAttribute("code", code);
    return "error";
}

Then, I set up the web.xml similar to yours, only redirecting to the spring controller endpoints along with the appropriate code:

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

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

Now, I don't know if this is the best way, but it works really well for me. The web container routes those error types to the /error/code page which is just calling a controller method and then the controller method produces the error.jsp page (by returning the view name "error").

Since it puts the error code in the model, I have a single jsp that handles multiple error types, I just put a conditional that renders a different user message depending on the error code. In other words, "Page Not Found :(" vs "You aren't authorized to view this page!"

And then I do this in my spring security config to make sure an unauthenticated user can see the error page (you've probably got this part already since you can see the jsp pages):

<http pattern="/error/**" security="none" />

I combine this with the annotation style exception handlers to do all my server exception handling (handling data requests with a json error response and html/view requests with html error pages) because you can just annotate a controller method to handle Exception or RuntimeException. I'd recommend you check out this method as well because it is very powerful and makes it dramatically easier to log and handle exceptions gracefully:

@ExceptionHandler(Exception.class)
public ModelAndView exceptionHandler(Exception e){
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.getModelMap().addAttribute("code", HttpStatus.INTERNAL_SERVER_ERROR);
    modelAndView.setViewName("error");

    logger.error("Error handling page request.", e);
    return modelAndView;
}

This is also a good way to make sure you have a chance to log exceptions that go through your controllers without having to put exception handling into every single one.

I made two base classes... one for my page controllers and one for my rest data controllers. The base page controller return the jsp view that renders a nice error page for the user. The base rest data controller returns a json message with the error information in the model and then the jsp will print the full stack or just a generic error depending on if the server is running in development or production mode.

like image 97
reblace Avatar answered Nov 15 '22 06:11

reblace