今天在學習《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
本文僅代表個人的觀點跟看法,如有不同的觀點歡迎各位大佬留言指正,也希望能跟大家共同學習探討,共同進步,謝謝大家。