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 PUT
s 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.