405 JSP error with Put Method

I had the same issue and solved it in the end by adding

<%@ page isErrorPage="true" %>

at the beginning of my JSP page.
With apache-jsp-8.0.33, org/apache/jasper/compiler/Generator.java skips the creation of this check for pages that have this flag set, allowing the JSP to answer to any method.


The problem is that when you return a view name from your controller method, the Spring DispatcherServlet will do a forward to the given view, preserving the original PUT method.

On attempting to handle this forward, Tomcat will refuse it, with the justification that a PUT to a JSP could be construed to mean "replace this JSP file on the server with the content of this request."

Really you want your controller to handle your PUT requests and then to subsequently forward to your JSPs as GET. Fortunately Servlet 3.0 provides a means to filter purely on the FORWARD dispatcher.

Create a filter:

public class GetMethodConvertingFilter implements Filter {

    @Override
    public void init(FilterConfig config) throws ServletException {
        // do nothing
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {

        chain.doFilter(wrapRequest((HttpServletRequest) request), response);
    }

    @Override
    public void destroy() {
        // do nothing
    }

    private static HttpServletRequestWrapper wrapRequest(HttpServletRequest request) {
        return new HttpServletRequestWrapper(request) {
            @Override
            public String getMethod() {
                return "GET";
            }
        };
    }
}

And wire it into your web.xml thusly:

<filter>
    <filter-name>getMethodConvertingFilter</filter-name>
    <filter-class>my.GetMethodConvertingFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>getMethodConvertingFilter</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>

This will convert requests to GET on forward only, leaving requests through other dispatchers unchanged, so PUTs will get intercepted by your controllers as normal.

My (possibly incorrect) understanding is that Tomcat 8.0.9 introduced a fix where this is effectively done automatically for the ERROR dispatcher - see the answer in your linked question. But you're not using the container's error handling mechanism to render your error page, you're using Spring MVC to manually forward to the view, hence why you need to do this. Personally I encountered this issue under Jetty 9.2.7 where no such fix is in place, and I delegate error handling to the container, so I have <dispatcher>ERROR</dispatcher> configured in my filter mapping as well.

This all seems a bit arcane but is the only way I've discovered to successfully jump through this particular RESTful-Spring-JSP-web-application hoop.

Tags:

Spring Mvc