1. Spring MVC 中使用 RESTFul 程式設計風格
@
- 1. Spring MVC 中使用 RESTFul 程式設計風格
- 2. RESTFul 程式設計風格
- 2.1 RESTFul 是什麼
- 2.2 RESTFul風格與傳統方式對比
- 3. Spring MVC 中使用 RESTFul 程式設計風格(增刪改查)的使用
- 3.1 準備工作
- 3.2 RESTFul 風格的 “查詢” 所有(RESTFul 規範 需要傳送 GET請求)
- 3.3 RESTFul 風格的 根據 “id 查詢”( RESTFul 規範 需要傳送 GET請求)
- 3.4 RESTFul 風格的 “增加資料” (RESTFul 規範 需要傳送 POST 請求)
- 3.5 RESTFul 風格的 “修改資料” (RESTFul 規範 需要傳送 PUT 請求)
- 3.6 RESTFul 風格的 “刪除資料” 資料(RESTFul 規範 需要傳送 DELETE 請求)
- 4. 補充: HiddenHttpMethodFilter 過濾器原始碼說明
- 5. 總結:
- 6. 最後:
2. RESTFul 程式設計風格
2.1 RESTFul 是什麼
RESTFul 是 web 伺服器介面
的一種設計風格。
RESTFul 定義了一組約束條件和規範,可以讓 web伺服器介面
更加簡潔,易於理解,易於擴充套件,安全可靠。
RESTFUl 對一個 web 伺服器介面
都規定了哪些東西 ?
- 對請求的 URL 格式有約束和規範
- 對 HTTP 的請求方式有約束和規範
- 對請求和響應的資料格式有約束和規範
- 對 HTTP 狀態碼有約束和規範
- 等......
REST 對請求方式的約束是這樣的:
- 查詢必須傳送 GET 請求
- 新增必須傳送 POST 請求
- 修改必須傳送 PUT 請求
- 刪除必須傳送 DELETE 請求
REST對 URL 的約束時這樣的:
- 傳統的 URL : get 請求,/springmvc/getUserById?id=1
- REST風格的 URL:get 請求,/springmvc/user/1
- 傳統的URL :get 請求,/springmvc/deleteUserById?id=1
- REST風格的URL:delete 請求,/springmvc/user/1
RESTFul 對 URL 的約束和規範的核心時:透過採用 :不同的請求方式
+ URL
來確定 web 服務中的資源。
RESTFul 的英文全稱時:Representational State Transfer(表述性狀態轉移),簡稱 REST。
表述性(Representational) 是:URL + 請求方式。
狀態(State) 是 :伺服器端的資料。
轉移(Transfer) 是:變化。
表述性轉移是指:透過 URL + 請求方式來控制伺服器端資料的變化。
2.2 RESTFul風格與傳統方式對比
統的 URL 與 RESTful URL 的區別是傳統的 URL 是基於方法名進行資源訪問和操作,而 RESTful URL 是基於資源的結構和狀態進行操作的。下面是一張表格,展示兩者之間的具體區別:
傳統的 URL | RESTful URL |
---|---|
GET /getUserById?id=1 | GET /user/1 |
GET /getAllUser | GET /user |
POST /addUser | POST /user |
POST /modifyUser | PUT /user |
GET /deleteUserById?id=1 | DELETE /user/1 |
從上表中我們可以看出,傳統的 URL是基於動作的,而 RESTful URL 是基於資源和狀態的,因此 RESTful URL 更加清晰和易於理解,這也是 REST 架構風格被廣泛使用的主要原因之一。
3. Spring MVC 中使用 RESTFul 程式設計風格(增刪改查)的使用
3.1 準備工作
匯入相關依賴 jar
包。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.rainbowsea</groupId>
<artifactId>springmvc-007</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<!--springmvc依賴-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.1.4</version>
</dependency>
<!--logback依賴-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.5.3</version>
</dependency>
<!--servlet依賴-->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
<!--thymeleaf和spring6整合的依賴-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring6</artifactId>
<version>3.1.2.RELEASE</version>
</dependency>
</dependencies>
</project>
相關包 / 目錄的建立,配置。
springmvc.xml 配置檔案的配置;
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 元件掃描-->
<context:component-scan base-package="com.rainbowsea.springmvc.controller"></context:component-scan>
<!-- 檢視解析器-->
<bean id="thymeleafViewResolver" class="org.thymeleaf.spring6.view.ThymeleafViewResolver">
<!--作用於檢視渲染的過程中,可以設定檢視渲染後輸出時採用的編碼字符集-->
<property name="characterEncoding" value="UTF-8"/>
<!--如果配置多個檢視解析器,它來決定優先使用哪個檢視解析器,它的值越小優先順序越高-->
<property name="order" value="1"/>
<!--當 ThymeleafViewResolver 渲染模板時,會使用該模板引擎來解析、編譯和渲染模板-->
<property name="templateEngine">
<bean class="org.thymeleaf.spring6.SpringTemplateEngine">
<!--用於指定 Thymeleaf 模板引擎使用的模板解析器。模板解析器負責根據模板位置、模板資源名稱、檔案編碼等資訊,載入模板並對其進行解析-->
<property name="templateResolver">
<bean class="org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver">
<!--設定模板檔案的位置(字首)-->
<property name="prefix" value="/WEB-INF/templates/"/>
<!--設定模板檔案字尾(字尾),Thymeleaf副檔名不一定是html,也可以是其他,例如txt,大部分都是html-->
<property name="suffix" value=".html"/>
<!--設定模板型別,例如:HTML,TEXT,JAVASCRIPT,CSS等-->
<property name="templateMode" value="HTML"/>
<!--用於模板檔案在讀取和解析過程中採用的編碼字符集-->
<property name="characterEncoding" value="UTF-8"/>
</bean>
</property>
</bean>
</property>
</bean>
<!-- 開啟註解驅動-->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 檢視控制器, 這個配置可以只寫 對應 index的檢視,不寫對應的Controller,簡化配置 -->
<mvc:view-controller path="/" view-name="index"></mvc:view-controller>
</beans>
3.2 RESTFul 風格的 “查詢” 所有(RESTFul 規範 需要傳送 GET請求)
RESTFul 規範中規定,如果要查詢資料,需要傳送 GET
請求。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首頁</title>
</head>
<body>
<h1>測試 RESTFul 程式設計風格</h1>
<hr>
<!--RESTFul 程式設計風格,檢視用列表-->
<a th:href="@{/user}">檢視使用者列表</a> <br>
</body>
</html>
控制器 Controller:
import com.rainbowsea.springmvc.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller // 交給 Spring IOC 容器進行管理
public class UserController {
@RequestMapping(value = "/user", method = RequestMethod.GET)
public String getAll() {
System.out.println("正在查詢所有使用者資訊...");
return "ok";
}
}
ok 的頁面檢視:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>OK頁面</title>
</head>
<body>
<h1>OK !</h1>
</body>
</html>
啟動伺服器,測試:http://localhost:8080/springmvc
3.3 RESTFul 風格的 根據 “id 查詢”( RESTFul 規範 需要傳送 GET請求)
RESTFul 規範中規定,如果要查詢資料,需要傳送GET請求。
首頁index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首頁</title>
</head>
<body>
<h1>測試 RESTFul 程式設計風格</h1>
<hr>
<!--RESTFul 程式設計風格,檢視用列表-->
<a th:href="@{/user}">檢視使用者列表</a> <br>
<!--RESTFul 風格的,根據 id 查詢使用者資訊-->
<a th:href="@{/user/1}">查詢id=1的這個使用者資訊</a><br>
</body>
</html>
控制器 Controller:
import com.rainbowsea.springmvc.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller // 交給 Spring IOC 容器進行管理
public class UserController {
//@RequestMapping(value = "/user/{佔位符}",method = RequestMethod.GET)
@RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
public String getById(@PathVariable(value = "id") String id) {
System.out.println("正在根據使用者 id 查詢使用者資訊...使用者 id 是" + id);
return "ok";
}
}
啟動伺服器測試:
3.4 RESTFul 風格的 “增加資料” (RESTFul 規範 需要傳送 POST 請求)
RESTFul規範中規定,如果要進行儲存操作,需要傳送POST請求。
這裡我們新增一個 User Bean 類,用於作為物件進行儲存。
package com.rainbowsea.springmvc.bean;
public class User {
private String username;
private String password;
private Integer age;
public User() {
}
public User(String username, String password, Integer age) {
this.username = username;
this.password = password;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
'}';
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
頁面編寫:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首頁</title>
</head>
<body>
<h1>測試 RESTFul 程式設計風格</h1>
<hr>
<!--RESTFul 程式設計風格,檢視用列表-->
<a th:href="@{/user}">檢視使用者列表</a> <br>
<!--RESTFul 風格的,根據 id 查詢使用者資訊-->
<a th:href="@{/user/1}">查詢id=1的這個使用者資訊</a><br>
<!--RESTFul 風格的,新增使用者資訊,新增必須傳送POST請求,需要使用 form 表單-->
<form th:action="@{/user}" method="post">
使用者名稱: <input type="text" name="username"><br>
密碼:<input type="password" name="password"><br>
年齡: <input type="text" name="age"><br>
<input type="submit" value="儲存">
</form>
<hr>
</body>
</html>
控制器 Controller:
啟動伺服器測試:
3.5 RESTFul 風格的 “修改資料” (RESTFul 規範 需要傳送 PUT 請求)
RESTFul規範中規定,如果要進行儲存操作,需要傳送PUT請求。
如何傳送PUT請求?
第一步:首先你必須是一個POST請求。
第二步:在傳送POST請求的時候,提交這樣的資料:_method=PUT
,使用隱藏域進行配置
第三步:在web.xml檔案配置SpringMVC提供的過濾器:HiddenHttpMethodFilter
注意:
<!-- 隱藏域--> <input type="hidden" name="_method" value="put"> 隱藏域的 name 必須只能是 “_method”, value是 put(大小寫忽略)
第一步:首先你必須是一個POST請求。
第二步:在傳送POST請求的時候,提交這樣的資料:_method=PUT
<h2>修改</h2>
<!-- RESTFul 風格的,修改使用者資訊,修改必須傳送 put 請求,要傳送 put 請求,首先必須是一個 Post 請求-->
<form th:action="@{/user}" method="post">
<!-- 隱藏域-->
<input type="hidden" name="_method" value="put">
使用者名稱: <input type="text" name="username"><br>
密碼:<input type="password" name="password"><br>
年齡: <input type="text" name="age"><br>
<input type="submit" value="修改">
</form>
第三步:在web.xml檔案配置SpringMVC提供的過濾器:HiddenHttpMethodFilter
注意:該過濾器一定要在字元編碼過濾器後面配置,不然,先設定的話,可能會出現獲取到的請求資料是亂碼
<!-- 新增一個過濾器,這個過濾器是springmvc提前寫好的,直接用就行了,這個過濾器可以幫助你將請求
POST轉換成PUT請求/DELETE請求-->
<!-- 同時注意: -->
<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>
頁面編寫:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首頁</title>
</head>
<body>
<h1>測試 RESTFul 程式設計風格</h1>
<hr>
<!--RESTFul 程式設計風格,檢視用列表-->
<a th:href="@{/user}">檢視使用者列表</a> <br>
<!--RESTFul 風格的,根據 id 查詢使用者資訊-->
<a th:href="@{/user/1}">查詢id=1的這個使用者資訊</a><br>
<!--RESTFul 風格的,新增使用者資訊,新增必須傳送POST請求,需要使用 form 表單-->
<form th:action="@{/user}" method="post">
使用者名稱: <input type="text" name="username"><br>
密碼:<input type="password" name="password"><br>
年齡: <input type="text" name="age"><br>
<input type="submit" value="儲存">
</form>
<hr>
<h2>修改</h2>
<!-- RESTFul 風格的,修改使用者資訊,修改必須傳送 put 請求,要傳送 put 請求,首先必須是一個 Post 請求-->
<form th:action="@{/user}" method="post">
<!-- 隱藏域-->
<input type="hidden" name="_method" value="put">
使用者名稱: <input type="text" name="username"><br>
密碼:<input type="password" name="password"><br>
年齡: <input type="text" name="age"><br>
<input type="submit" value="修改">
</form>
</body>
</html>
控制器 Controller:
啟動伺服器測試:
3.6 RESTFul 風格的 “刪除資料” 資料(RESTFul 規範 需要傳送 DELETE 請求)
RESTFul規範中規定,如果要進行 刪除 操作,需要傳送DELETE 請求。
如何傳送 DELETE 請求?,和 傳送 PUT 請求的三步是一樣的,只需要將 value 的值改為 delete 即可
第一步:首先你必須是一個POST請求。
第二步:在傳送POST請求的時候,提交這樣的資料:_method=PUT
,使用隱藏域進行配置
第三步:在web.xml檔案配置SpringMVC提供的過濾器:HiddenHttpMethodFilter
注意:
<!-- 隱藏域--> <input type="hidden" name="_method" value="delete"> 隱藏域的 name 必須只能是 “_method”, value是 delete (大小寫忽略)
頁面編寫:
<hr>
<h2>刪除使用者</h2>
<!--RESTful風格的,刪除使用者西悉尼-->
<!--刪除必須傳送 DELETE 請求,和 PUT 請求實現方式相同-->
<!--傳送 DELETE 請求的前提是POST請求,並且需要透過隱藏域提交,_method="delete"-->
<a th:href="@{user/120}" onclick="del(event)">刪除使用者id = 120 的使用者資訊</a>
<form id="delForm" method="post">
<input type="hidden" name="_method" value="delete">
</form>
<script>
function del(event) {
// 獲取表單
let delForm = document.getElementById("delForm");
// 給 form 的 action 賦值
delForm.action = event.target.href;
// 傳送POST 請求提交表單
delForm.submit();
// 非常重要,你需要阻止超連結的預設行為
event.preventDefault();
}
</script>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首頁</title>
</head>
<body>
<h1>測試 RESTFul 程式設計風格</h1>
<hr>
<!--RESTFul 程式設計風格,檢視用列表-->
<a th:href="@{/user}">檢視使用者列表</a> <br>
<!--RESTFul 風格的,根據 id 查詢使用者資訊-->
<a th:href="@{/user/1}">查詢id=1的這個使用者資訊</a><br>
<!--RESTFul 風格的,新增使用者資訊,新增必須傳送POST請求,需要使用 form 表單-->
<form th:action="@{/user}" method="post">
使用者名稱: <input type="text" name="username"><br>
密碼:<input type="password" name="password"><br>
年齡: <input type="text" name="age"><br>
<input type="submit" value="儲存">
</form>
<hr>
<h2>修改</h2>
<!-- RESTFul 風格的,修改使用者資訊,修改必須傳送 put 請求,要傳送 put 請求,首先必須是一個 Post 請求-->
<form th:action="@{/user}" method="post">
<!-- 隱藏域-->
<input type="hidden" name="_method" value="put">
使用者名稱: <input type="text" name="username"><br>
密碼:<input type="password" name="password"><br>
年齡: <input type="text" name="age"><br>
<input type="submit" value="修改">
</form>
<hr>
<h2>刪除使用者</h2>
<!--RESTful風格的,刪除使用者西悉尼-->
<!--刪除必須傳送 DELETE 請求,和 PUT 請求實現方式相同-->
<!--傳送 DELETE 請求的前提是POST請求,並且需要透過隱藏域提交,_method="delete"-->
<a th:href="@{user/120}" onclick="del(event)">刪除使用者id = 120 的使用者資訊</a>
<form id="delForm" method="post">
<input type="hidden" name="_method" value="delete">
</form>
<script>
function del(event) {
// 獲取表單
let delForm = document.getElementById("delForm");
// 給 form 的 action 賦值
delForm.action = event.target.href;
// 傳送POST 請求提交表單
delForm.submit();
// 非常重要,你需要阻止超連結的預設行為
event.preventDefault();
}
</script>
</body>
</html>
控制器 Controller:
package com.rainbowsea.springmvc.controller;
import com.rainbowsea.springmvc.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller // 交給 Spring IOC 容器進行管理
public class UserController {
//@RequestMapping(value = "/user/{佔位符}",method = RequestMethod.GET)
@RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
public String getById(@PathVariable(value = "id") String id) {
System.out.println("正在根據使用者 id 查詢使用者資訊...使用者 id 是" + id);
return "ok";
}
@RequestMapping(value = "/user", method = RequestMethod.GET)
public String getAll() {
System.out.println("正在查詢所有使用者資訊...");
return "ok";
}
@RequestMapping(value = "/user", method = RequestMethod.POST)
public String save(User user) {
System.out.println("正在儲存使用者資訊");
System.out.println(user);
return "ok";
}
@RequestMapping(value = "/user", method = RequestMethod.PUT)
public String modify(User user) {
System.out.println("正在修改使用者資訊" + user);
return "ok";
}
@RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)
public String del(@PathVariable(value = "id") String id) {
System.out.println("正刪除使用者 : " + id);
return "ok";
}
}
啟動伺服器測試:
4. 補充: HiddenHttpMethodFilter 過濾器原始碼說明
HiddenHttpMethodFilter是Spring MVC框架提供的,專門用於RESTFul程式設計風格。
實現原理可以透過原始碼檢視:
透過原始碼可以看到,if語句中,首先判斷是否為POST請求,如果是POST請求,呼叫request.getParameter(this.methodParam)
。可以看到this.methodParam
是_method
,這樣就要求我們在提交請求方式的時候必須採用這個格式:_method=put
。獲取到請求方式之後,呼叫了toUpperCase轉換成大寫了。因此前端頁面中小寫的put或者大寫的PUT都是可以的。if語句中巢狀的if語句說的是,只有請求方式是 PUT,DELETE,PATCH的時候會建立HttpMethodRequestWrapper物件。而HttpMethodRequestWrapper物件的構造方法是這樣的:
這樣method就從POST變成了:PUT/DELETE/PATCH
。
重點注意事項:CharacterEncodingFilter和HiddenHttpMethodFilter的順序
細心的同學應該注意到了,在HiddenHttpMethodFilter原始碼中有這樣一行程式碼:
)
大家是否還記得,字元編碼過濾器執行之前不能呼叫 request.getParameter方法,如果提前呼叫了,亂碼問題就無法解決了。因為request.setCharacterEncoding()方法的執行必須在所有request.getParameter()方法之前執行。因此這兩個過濾器就有先後順序的要求,在web.xml檔案中,應該先配置CharacterEncodingFilter,然後再配置HiddenHttpMethodFilter。
5. 總結:
RESTFul風格與傳統方式對比區別
RESTFul 風格的 “查詢” 所有(RESTFul 規範 需要傳送 GET請求)
RESTFul 風格的 根據 “id 查詢”( RESTFul 規範 需要傳送 GET請求)
RESTFul 風格的 “增加資料” (RESTFul 規範 需要傳送 POST 請求)
RESTFul 風格的 “修改資料” (RESTFul 規範 需要傳送 PUT 請求)
如何傳送PUT請求?
第一步:首先你必須是一個POST請求。
第二步:在傳送POST請求的時候,提交這樣的資料:_method=PUT
,使用隱藏域進行配置
第三步:在web.xml檔案配置SpringMVC提供的過濾器:HiddenHttpMethodFilter**
注意:
<!-- 隱藏域--> <input type="hidden" name="_method" value="put"> 隱藏域的 name 必須只能是 “_method”, value是 put(大小寫忽略)
RESTFul 風格的 “刪除資料” 資料(RESTFul 規範 需要傳送 DELETE 請求);如何傳送 DELETE 請求?,和 傳送 PUT 請求的三步是一樣的,只需要將 value 的值改為 delete 即可
HiddenHttpMethodFilter 該過濾器一定要在字元編碼過濾器後面配置,不然,先設定的話,可能會出現獲取到的請求資料是亂碼。
6. 最後:
“在這個最後的篇章中,我要表達我對每一位讀者的感激之情。你們的關注和回覆是我創作的動力源泉,我從你們身上吸取了無盡的靈感與勇氣。我會將你們的鼓勵留在心底,繼續在其他的領域奮鬥。感謝你們,我們總會在某個時刻再次相遇。”