REST和SpringMVC映射請求數據 7.REST-優雅的url請求風格 7.1REST基本介紹 REST風格詳細介紹 REST:即 Representational State Transfer,表述性狀態傳遞。它結構清晰,同時可以隱藏行為。 通過一個url來直觀展示傳統風格與REST風格的區 ...
REST和SpringMVC映射請求數據
7.REST-優雅的url請求風格
7.1REST基本介紹
-
REST:即 Representational State Transfer,表述性狀態傳遞。它結構清晰,同時可以隱藏行為。
-
通過一個url來直觀展示傳統風格與REST風格的區別:
-
傳統風格:
當我們在瀏覽器上訪問一些資源時,可以看到有些網頁的url為http://localhost/students/selectById?id=1
(該地址表示查找id為1的students對象)http://localhost/students/saveStudent
(該地址表示保存students信息) -
REST風格:
http://localhosts/student/1
http://localhosts/student
通過這兩種風格的對比,我們可以看到REST風格的一部分優點:
(1)可以隱藏資源訪問行為(如隱藏了selectById等),這樣就無法通過地址得知對資源進行了哪種操作
(2)可以明顯的看到其書寫簡化了,不僅書寫簡化了,在開發時代碼也可以簡化。
-
-
HTTP 協議里,四個表示操作方式的動詞:GET,POST,PUT,DELETE。它們分別對應四種基本操作:GET用來獲取資源,POST用來新建資源,PUT用來更新資源,DELETE用來刪除資源。
-
傳統的url通過參數說明 crud的類型,而rest則是通過 get/post/put/delete 來說明 crud的類型
- REST的核心過濾器
- 當前的瀏覽器只支持 post/get 請求,因此為了得到 put/delete 的請求方式,需要使用 Spring 提供的
HiddenHttpMethodFilter
過濾器進行轉換。 - HiddenHttpMethodFilter :瀏覽器form表單隻支持GET 和 POST請求,而DELETE、PUT 等method並不支持,Spring 添加了一個過濾器,可以將這些請求轉換為標準的http方法,使得支持這四種請求方式。
- 需要特別註意:HiddenHttpMethodFilter 只能對 post請求方式進行轉換
- 這個過濾器需要在web.xml中配置
- 當前的瀏覽器只支持 post/get 請求,因此為了得到 put/delete 的請求方式,需要使用 Spring 提供的
7.2Rest風格的url-完成crud操作
7.2.1需求說明
7.2.2代碼實現
-
修改web.xml添加 HiddenHttpMethodFilter過濾器,它的作用是將post請求轉換為指定的delete或put請求
<!--配置HiddenHttpMethodFilter過濾器 1.它的作用是將以post方式提交的delete請求和put請求進行轉換 2.配置url-pattern為/*,表示所有請求都經過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>
-
在SpringDispatcherServlet-servlet.xml添加兩個常規的配置
<!--加入兩個常規的配置--> <!--支持SpringMVC的高級功能,比如:JSR303校驗,映射動態請求--> <mvc:annotation-driven></mvc:annotation-driven> <!--將SpringMVC不能處理的請求,交給tomcat處理,比如css,js--> <mvc:default-servlet-handler/>
註意:mvc:annotion添加的是尾碼為mvc的命名空間:
-
創建rest.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>rest</title> <script type="text/javascript" src="script/jquery-3.6.0.min.js"></script> <script type="text/javascript"> $(function () { $("#deleteBook").click(function () { //將當前的超鏈接的href的值,賦給hiddenForm表單的action屬性 $("#hiddenForm").attr("action", this.href); $(":hidden").val("DELETE");//給hidden的_method參數賦值為delete $("#hiddenForm").submit();//提交表單 return false;//改變超鏈接行為,不再提交 }) //與上同理,不一樣的是這裡原本就是表單post請求 $("#updateBook").click(function () { //帶上目標請求格式,HiddenHttpMethodFilter會自動將post請求轉成你指定的格式 $(":hidden").val("PUT");//給hidden的_method參數賦值為put }) }) </script> </head> <body> <h2>Rest風格的crud操作案例</h2> <hr/> <h3>rest風格的url 查詢書籍[get]</h3> <a href="user/book/200">點擊查詢書籍</a> <hr/> <h3>rest風格的url 添加書籍[post]</h3> <form action="user/book" method="post"> name:<input name="bookName" type="text"/><br/> <input type="submit" value="添加書籍"/> </form> <hr/> <h3>rest風格的url 刪除書籍[delete]</h3> <%--說明: 1.在預設情況下,超鏈接是get請求 2.要將get請求轉成SpringMVC可以識別的delete,就要考慮HiddenHttpMethodFilter機制: public static final String DEFAULT_METHOD_PARAM = "_method"; ------------------------------------- private static final List<String> ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name())); ------------------------------------- //獲取請求的方式,如果是post方式,就進行處理 if ("POST".equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null) { String paramValue = request.getParameter(this.methodParam); if (StringUtils.hasLength(paramValue)) { String method = paramValue.toUpperCase(Locale.ENGLISH); if (ALLOWED_METHODS.contains(method)) {//若指定method在ALLOWED_METHODS中存在 //進行包裝,轉換為springmvc可以解析的請求 requestToUse = new HttpMethodRequestWrapper(request, method); } } } -------------------------------------- 3.從上述代碼可以看到,HiddenHttpMethodFilter 過濾器可以對以 Post方式提交的delete,put,patch轉換成springmvc識別的RequestMethod.DElETE,RequestMethod.PUT... 4.但是當前的超鏈接為 get請求,怎麼將get請求轉換成 post的請求方式呢? 5.我們可以使用jquery來進行處理,讓用戶點擊超鏈接的時候,走一個表單的請求 --%> <a href="user/book/600" id="deleteBook">刪除指定id的書籍</a> <form action="" method="post" id="hiddenForm"> <input type="hidden" name="_method"/> </form> <hr/> <h3>rest風格的url 修改書籍[put]</h3> <form action="user/book/666" method="post" id="updateBook"> <input type="hidden" name="_method"> <input type="submit" value="修改書籍"/> </form> </body> </html>
-
BookHandle.java
- 下麵的代碼中可以看到,許多方法的REST風格匹配的url是一樣的,但是由於它們的請求方式不同,所以匹配到的方法不同。這也是rest風格的優點:不僅簡化了url,而且隱藏了行為。
- 所以實際上SpringMVC的Controller層的url是可以相同的,會另外根據請求方式的不同來匹配方法
package com.li.web.rest; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; /** * @author 李 * @version 1.0 * 用於處理rest風格的請求-crud */ @RequestMapping(value = "/user") @Controller public class bookHandler { //查詢[get] @RequestMapping(value = "/book/{id}", method = RequestMethod.GET) public String getBook(@PathVariable("id") String id) { System.out.println("查詢書籍 id=" + id); return "success"; } //添加[post] @PostMapping(value = "/book") public String addBook(String bookName) {//註意參數名字要和表單提交的參數名稱一致 System.out.println("添加書籍 bookName=" + bookName); return "success"; } //刪除[delete] @DeleteMapping(value = "/book/{id}") public String delBook(@PathVariable("id") String id) { System.out.println("刪除書籍 id=" + id); //return "success";//這樣寫,返回會報錯:HTTP Status 405 - JSPs only permit GET POST or HEAD //redirect:/user/success 重定向,會被解析成 /web工程路徑/user/success,然後返回給瀏覽器解析 return "redirect:/user/success";//重定向到一個沒有指定method的 Handler方法 } //如果請求是 /user/success,就轉發到success.jsp @RequestMapping(value = "/success") public String successGeneral() { return "success";//該方法轉發到success.jsp頁面 } @PutMapping(value = "/book/{id}") public String updateBook(@PathVariable("id") String id) { System.out.println("修改書籍 id=" + id); return "redirect:/user/success";//同理 } }
-
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>操作成功</title> </head> <body> <h1>恭喜,操作成功!</h1> </body> </html>
-
測試,redeployTomcat,訪問:
http://localhost:8080/springmvc/rest.jsp
,在分別點擊四種提交方式,前端頁面和後臺輸出如下:
7.3註意事項和使用細節
-
HiddenHttpMethodFilter在將 post請求轉成 delete/put請求時,是按照
_method
參數名來讀取的 -
如果web項目是運行在 Tomcat8及以上,會發現被過濾成 DELETE和 PUT請求後,到達控制器Controller時能順利執行,但是返回(forward)會報HTTP 405 提示:HTTP Status 405 - JSPs only permit GET POST or HEAD。意為JSP只允許GET POST 或 HEAD
(1)解決方式1:使用Tomcat7
(2)解決方式2:將請求轉發(forward)改為重定向(redirect),重定向到一個Handler,由Handler轉發到頁面。
-
頁面測試時,如果出現點擊修改書籍,仍然走的是刪除url,可能是瀏覽器緩存等原因,換成Chrome即可。如果再不行,使用js修改表單的hidden的_method的值
8.SpringMVC映射請求數據
8.1獲取參數值
在開發中,如何獲取到 http://xxx/url?參數名1=參數值1&參數名2=參數值2
中的參數?
之前的案例中我們知道:提交的url的參數名必須和映射的方法中的形參名保持一致。否則方法獲取的是null。
但是如果url的參數名和方法的形參名不一致,又要獲取該參數,應該解決這個問題呢?
答案是使用 @RequestParam 註解。
應用實例
- request_parameter.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>測試request_parameter</title>
</head>
<body>
<h2>獲取到超鏈接參數值</h2>
<a href="vote/vote01?name=jack">獲取超鏈接的參數</a>
</body>
</html>
- VoteHandler.java:
package com.li.web.requestparam;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* @author 李
* @version 1.0
*/
@RequestMapping(value = "/vote")
@Controller
public class VoteHandler {
/**
* 1.獲取到超鏈接傳遞的數據
* 請求為 http://localhost:8080/springmvc/vote/vote01?name=xxx
* (即提交的參數名和方法形參名不一致)
* 2.@RequestParam 表示會接收提交的參數
* 3.value = "name" 表示提交的參數名是name
* 4.required = false 表示該參數可以沒有(預設true,表示必須有該參數)
* 5.當我們使用了 @RequestParam(value = "name", required = false) 後,
* 請求的參數名和方法形參名可以不一致
*
* @param username
* @return
*/
@RequestMapping(value = "/vote01")
public String test01(@RequestParam(value = "name", required = false) String username) {
System.out.println("獲取到的username=" + username);
return "success";
}
}
-
訪問
http://localhost:8080/springmvc/request_parameter.jsp
,點擊超鏈接。 -
後臺輸出如下,說明在提交參數和方法形參名不一致的情況下,通過@RequestParam 註解可以獲取到參數
8.2獲取http請求消息頭
開發中,如何獲取到http請求的消息頭信息(使用較少)
在映射的方法的形參前添加@RequestHeader(value="要獲取的Header參數")
,即可獲取相關信息,然後將其值賦給方法的形參。
應用實例
- 在VoteHandler.java
package com.li.web.requestparam;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* @author 李
* @version 1.0
*/
@RequestMapping(value = "/vote")
@Controller
public class VoteHandler {
//需求:獲取請求頭的Accept-Encoding和 Host
@RequestMapping(value = "/vote02")
public String test02(@RequestHeader("Accept-Encoding") String ae,
@RequestHeader(value = "Host") String host) {
System.out.println("Accept-Encoding=" + ae);
System.out.println("Host=" + host);
return "success";
}
}
-
瀏覽器地址欄發送請求
http://localhost:8080/springmvc/vote/vote02
,後臺輸出:
8.3獲取Javabean形式的數據
- 在開發中,如何獲取到Javabean的數據?即如何按照java對象的形式來接收數據?
使用場景說明:例如,在實際開發中提交一個表單。表單提交後,希望在後端handler接收到表單數據時,自動地將這些數據封裝到某個Javabean中。
應用實例
- Pet.java
package com.li.entity;
/**
* @author 李
* @version 1.0
* entity
*/
public class Pet {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Pet{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
- Master.java
package com.li.entity;
/**
* @author 李
* @version 1.0
* entity
*/
public class Master {
private Integer id;
private String name;
private Pet pet;//對象的屬性級聯
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Pet getPet() {
return pet;
}
public void setPet(Pet pet) {
this.pet = pet;
}
@Override
public String toString() {
return "Master{" +
"id=" + id +
", name='" + name + '\'' +
", pet=" + pet +
'}';
}
}
- VoteHandler.java