(1)RESTful架构:
RESTful架构,就是目前最流行的一种互联网软件架构,它结构清晰、符合标准、易于理解、扩展方便;so正得到越来越多的网站采用; RESTful(Representational State Transfer),其实是一个开发理念,是对http的很好的诠释;
(2)REST风格的URL请求:
请求路径 | 请求方法 | 作用 |
---|---|---|
-/user/1 | HTTP GET | 得到id为1的user |
-/user/1 | HTTP DELETE | 删除id为1的user |
-/user/1 | HTTP PUT | 更新id为1的user |
-/user | HTTP POST | 新增user |
(3)浏览器只支持GET和POST请求:
由于浏览器表单只支持 GET 和 POST 请求,为了实现 DELETE 和 PUT 请求,Spring 为我们提供了一个过滤器org.springframework.web.filter.HiddenHttpMethodFilter
,可以为我们将 GET 和 POST 请求通过过滤器转化成 DELETE 和 PUT 请求:
在web.xml文件中配置过滤器:
<!-- 配置 org.springframework.web.filter.HiddenHttpMethodFilter 过滤器 -->
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<!-- 拦截所有请求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
(4)在表单中添加隐藏域:
由于浏览器表单无法发送 DELETE 和 PUT 请求,所以为了让 HiddenHttpMethodFilter 识别请求的方法,需要在表单中添加一个隐藏域,name = _method
, value = DELETE/POST /PUT
,修改后 index.jsp 页面代码如下:
<form action="user" method="post"> // 提交方法必须为post
<input type="hidden" name="_method" value="PUT"/> // 隐藏域
<input type="submit" value="Test Rest PUT"/>
</form>
(5)HTTP 405的错误提示:
如果web项目是运行在Tomcat 8及以上,会发现被过滤成DELETE和PUT请求,到达控制器时能顺利执行,但是返回时(forward)会报HTTP 405的错误提示:消息 JSP 只允许 GET、POST 或 HEAD。Jasper 还允许 OPTIONS
;
(6)解决方法:
1、使用Tomcat7;
2、在Controller当中添加 @ResponseBody 或者 @RestController注解,但是最后执行的结果 , 是在页面当中输出方法 return 的内容;
3、将请求转发(forward)改为请求重定向(redirect):重定向到一个Handler,由Handler转发到页面:
@RequestMapping(value = "/testMethod/{id}" , method = RequestMethod.DELETE)
public String testMethodDELETE(@PathVariable(value = "id") Integer id) {
System.out.println("testMethod: DELETE: "+ id);
return "redirect:/springmvc/success"; //重定向到一个没有指定 method的 Handler方法
}
@RequestMapping(value = "/success")
public String successGenecal() {
return "success"; //由该方法 转发到success.jsp页面
}
4、创建一个Filter来包装HttpRequest中的getMethod()方法:
- 客户端发送请求至服务器,这时如果发送的是POST请求且带有以_method为名的参数,会被
Spring的HiddenHttpMethodFilter
给拦截; HiddenHttpMethodFilter
内有一个静态内部类通过继承HttpServletRequestWrapper
类并重写getMethod()
方法,将该方法返回值设为_method隐藏域的值;HiddenHttpMethodFilter
在包装好Request后,将请求发往服务器的控制器中对应的方法处理器,这时的请求变成了图中的WrapperRequest by SpringFilter
;- 服务器处理完请求后,产生了一个forward请求,产生相应的请求处理信息发往客户端,注意这时的request的getMethod()方法仍然是
HiddenHttpMethodFilter
包装过的; - 我们需要在服务器的响应请求到达客户端前进行拦截,这也是最关键的一步,通过自定义过滤器
MyHttpMethodFilter
进一步包装请求,将getMethod()方法返回值改成POST或GET即可;
(用HttpRequestWrapper将原始请求包装成一个假的请求并告诉它返回POST来delete和put请求所以Jsp认为这是一个POST请求)
public class MyHttpMethodFilter extends HiddenHttpMethodFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String method = httpServletRequest.getMethod();
if (method.equalsIgnoreCase("delete") || method.equalsIgnoreCase("put")) { method = "POST"; }
httpServletRequest = new HttpMethodRequestWrapper(request, method);
filterChain.doFilter(httpServletRequest, 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,注意dispatcher结点值必须为FORWARD:
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>