如果项目是运行在 Tomcat 8 及以上,会发现发出的 PUT 请求和 DELETE 请求可以被控制其接收到,但是返回页面时(forward)会报HTTP 405 的错误提示:"消息 JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS"。
解决方案:
使用 Tomcat 8 以下版本。
使用
@RestController
或者@Controller + @ResponseBody
标签,但是这样就无法跳转页面了。避免使用 forward 方式跳转页面,改为 重定向
redirect
方式跳转到另一个控制器方法,再由这个控制器方法跳转页面。@RequestMapping(value = "/rest", method = RequestMethod.PUT)
public String put() {
// 接收表单中的各种信息
System.out.println("PUT --- 更新数据");
return "redirect:/success";
} @RequestMapping(value = "/success")
public String success() {
return "success";
}
给 Tomcat 添加启动参数,使Tomcat允许写操作
<init-param>
<param-name>readonly</param-name>
<param-value>false</param-value>
</init-param>
创建一个新的 Filter 来过滤 FORWARD
// HiddenHttpMethodFilter.java
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException { HttpServletRequest requestToUse = request; if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) {
String paramValue = request.getParameter(this.methodParam);
if (StringUtils.hasLength(paramValue)) {
requestToUse = new HttpMethodRequestWrapper(request, paramValue);
}
} filterChain.doFilter(requestToUse, response);
}
HiddenHttpMethodFilter 中的
doFilterInternal
方法是用来过滤 form 表单中 name 为 _method的请求。可以发现,它把请求作为参数传进HttpMethodRequestWrapper
中并且返回了一个新的请求,放行的也是新的请求。所以我们可以重写 HttpMethodRequestWrapper 中的getMethod()
方法,让它支持 forward 方式的跳转。// 重写 getMethod()
package com.pudding.conf; import java.io.IOException; import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse; import org.springframework.web.filter.HiddenHttpMethodFilter; public class MyHttpMethodFilter extends HiddenHttpMethodFilter { @Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException { HttpServletRequest requestToUse = request; String method = requestToUse.getMethod();
if (method.equalsIgnoreCase("delete") || method.equalsIgnoreCase("put")) {
method = "POST";
} requestToUse = new HttpMethodRequestWrapper(request, method); filterChain.doFilter(requestToUse, response);
} private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper { private final String method; public HttpMethodRequestWrapper(HttpServletRequest request, String method) {
super(request);
this.method = method;
} public String getMethod() {
return this.method;
}
}
}
在 web.xml 中配置自己的过滤器:
<filter>
<filter-name>myFilter</filter-name>
<filter-class>com.pudding.conf.MyHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
在 forward 需要跳转的页面头加上
isErrorPage="true"
:<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" isErrorPage="true"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>success</h1>
</body>
</html>