今天在学习《SpringMVC3.0实战指南》的时候,了解到SpringMVC可以通过请求方法的限定来模拟请求方式,以下是我对该方式的一些解读:
很多人在刚接触JavaWeb的理论的时候,只知道浏览器在发送表单请求的时候只有2中方式:POST跟GET。可是我们了解到在HTTP协议中不光定义了POST跟GET两种请求方法,标准方法集合中还包括PUT、DELETE、HEAD跟POTIONS。在这些方法的含义连同行为许诺都一起被定义在HTTP规范之中,并且在实际开发中我们大量使用了SpringMVC+Restful的开发风格,对于解决浏览器的请求方式限制,SpringMVC采用了请求方式限定来模拟请求方式,从而达到可以让我们使用其他的请求方式(当然这些请求方式的转换都是在SpringMVC框架内部实现转换)来实现表单的提交,那接下来我们就一起来学习一下SpringMVC是如何实现模拟请求方式的。
首先我们要介绍一个SpringMVC的拦截器:HiddenHttpMethodFilter,这个拦截器的主要作用就是将我们的Post请求转换成我们想要转换请求方式,那他是如何工作的呢?
接下来我们先来看一下它是如何去配置的?
在我们的jsp页面做如下编写:
</head> <body> <form action="/rest/page/test" method="post"> <input type="hidden" name="_method" value="put" /> <input type="submit" value="提交"> </form> </body>
在jsp页面中我们需要设置一个隐藏的input标签,给他设置一个name为"_method",value为我们需要模拟的请求方式
有人会问这个隐藏的input标签的name值我们可以随意配置吗?答案是:不可以。为什么不可以呢?先卖个关子,等讲完web.xml配置之后我会告诉大家为什么要固定是"_method"。
在我们Web项目的web.xml中我们需要做如下的配置:
1 <!-- 将POST请求转换为DELETE或者PUT:请求必须是_method --> 2 <filter> 3 <filter-name>HiddenHttpMethodFilter</filter-name> 4 <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> 5 </filter> 6 <filter-mapping> 7 <filter-name>HiddenHttpMethodFilter</filter-name> 8 <url-pattern>/*</url-pattern> 9 </filter-mapping>
这里有个注意事项需要给大家去说明一下:
HiddenHttpMethodFilter拦截器是在SpringMVC的核心控制器DispatcherServlet之前执行的,所以HiddenHttpMethodFilter必须配置在核心控制器之前。
那接下来我们来看一下HiddenHttpMethodFilter的源码是怎么编写的。
我在源码中已经添加了注意,大家可以直接观看源码就可以了解到HiddenHttpMethodFilter是如何实现模拟请求转换的。
1 public class HiddenHttpMethodFilter extends OncePerRequestFilter { 2 3 /** Default method parameter: {@code _method} */ 4 /**在这里我们可以看到默认的方法名称就是"_method",如果你在input标签中指定其他值,input标签中的value值将无法被获取到 5 */ 6 public static final String DEFAULT_METHOD_PARAM = "_method"; 7 8 private String methodParam = DEFAULT_METHOD_PARAM; 9 10 11 /** 12 * Set the parameter name to look for HTTP methods. 13 * @see #DEFAULT_METHOD_PARAM 14 */ 15 public void setMethodParam(String methodParam) { 16 Assert.hasText(methodParam, "'methodParam' must not be empty"); 17 //这里将"_method"传给this.methodParam 18 this.methodParam = methodParam; 19 } 20 21 @Override 22 protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 23 throws ServletException, IOException { 24 //获取到"_method"对应的value值 25 String paramValue = request.getParameter(this.methodParam); 26 //如果是POST请求,就将POST请求包装成需要转换的请求 27 if ("POST".equals(request.getMethod()) && StringUtils.hasLength(paramValue)) { 28 //toUpperCase方法是将字符串转换成大写的英文字符串 29 String method = paramValue.toUpperCase(Locale.ENGLISH); 30 //通过调用包装方法将请求以及我们要转换的请求方式一起包装成新的请求 31 HttpServletRequest wrapper = new HttpMethodRequestWrapper(request, method); 32 filterChain.doFilter(wrapper, response); 33 } 34 else { 35 //如果不是,直接放行 36 filterChain.doFilter(request, response); 37 } 38 } 39 40 41 /** 42 * Simple {@link HttpServletRequest} wrapper that returns the supplied method for 43 * {@link HttpServletRequest#getMethod()}. 44 * 这个方法其实就是一个包装方法,它将我们的request以及请求方法重新包装,转换成我们需要的请求 45 */ 46 private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper { 47 48 private final String method; 49 50 public HttpMethodRequestWrapper(HttpServletRequest request, String method) { 51 super(request); 52 this.method = method; 53 } 54 //通过覆写了 getMethod 方法,后期再调用getMethod方法的时候获取到的就是我们转换之后的method 55 @Override 56 public String getMethod() { 57 return this.method; 58 } 59 } 60 61 } 62
本文仅代表个人的观点跟看法,如有不同的观点欢迎各位大佬留言指正,也希望能跟大家共同学习探讨,共同进步,谢谢大家。