SpringBoot2.x系列教程82--構建RESTful風格的API接口
作者:一一哥
一. RESTful架構介紹
1. RESTful架構概述
RESTful架構,是目前最流行的一種互聯網軟件架構風格,它結構清晰、符合標準、易於理解、擴展方便,所以正得到越來越多的網站採用。
但是,RESTful架構到底是怎麼一回事呢?
2. RESTful架構起源
REST這個詞,是Roy Thomas Fielding在他2000年的博士論文中提出的,Roy Thomas Fielding是一個很重要的人,他是HTTP協議(1.0版和1.1版)的主要設計者、Apache服務器軟件的作者之一、Apache基金會的第一任主席。
所以,他的這篇論文一發表,就引起了很多人的關注,並且立即對互聯網開發產生了深遠的影響。
Roy Thomas Fielding將他對互聯網軟件的架構原則,定名爲REST,也就是REpresentational State Transfer(3個單詞的首字母)的縮寫,一般翻譯爲"表現層狀態轉化"或者是”表述性狀態轉移“。
3. RESTful架構含義
如果我們想理解RESTful架構風格,則需要先理解RESTful這個單詞的組成。
-
資源(Resources)
REST的名稱是"表現層狀態轉化",
其實是資源(Resources)的"表現層狀態轉化"。這裏所謂的"資源",其實就是網絡上存儲的一個具體信息或數據,它可以是一段文本、一張圖片、一首歌曲、一種服務,總之就是一個具體的數據。
我們可以用一個URI(統一資源標識符)指向這些資源,每種資源都對應一個特定的URI。要獲取這個資源,通過訪問它對應的URI就可以了,因此該URI就代表了每一個資源的地址或獨一無二的識別符號。
-
表述性(REpresentational):
"資源"是一種信息實體,它可以有多種外在的表現形式,我們把"資源"具體呈現出來的形式,叫做它的"表現層"(Representation)。
REST 資源實際上可以用各種形式來進行表現,比如文本可以用txt格式表現,也可以用HTML格式、XML格式、JSON格式表現,甚至可以採用二進制格式;圖片可以用JPG格式表現,也可以用PNG格式表現,我們可以使用最適合資源的任意形式。
-
狀態(State): 當使用 REST 的時候,我們更關注資源的狀態而不是對資源採取的行爲。
-
轉義(Transfer): REST 涉及到轉移資源數據,它以某種表述性形式從一個應用轉移到另一個應用。
如果客戶端想要操作服務器,必須通過某種手段,讓服務器端發生"狀態轉化"(State Transfer),而這種轉化是建立在表現層之上的,所以就是"表現層狀態轉化"。
客戶端用到的手段,只能是HTTP協議。具體來說,就是HTTP協議裏面,四個表示操作方式的動詞:GET、POST、PUT、DELETE。它們分別對應四種基本操作:GET用來獲取資源,POST用來新建資源(也可以用於更新資源),PUT用來更新資源,DELETE用來刪除資源。
4. RESTful架構
綜合上面的解釋,我們總結一下什麼是RESTful架構:
-
每一個URI都代表一種資源;
-
客戶端通過四個HTTP動詞,對服務器端資源進行操作,實現"表現層狀態轉化”。
簡單的說,RESTful就是將資源的狀態以適合客戶端或服務端的形式從服務端轉移到客戶端(或者反過來)。在 RESTful中,資源通過 URI 進行識別和定位,然後通過某種行爲(即 HTTP的方法)來完成某種功能。
也就是說如果一個架構符合REST原則,就稱它爲RESTful架構。
二. 實例講解
1. Http請求方法
我們在平時的 Web 開發中,常用的http請求方法是 GET 和 POST,但是實際上,HTTP 方法還有 PATCH、DELETE、PUT 等其他方法,這些方法通常會匹配爲如下的 CRUD 動作:

但這並不是嚴格的限制,有時候 PUT 也可以用來創建新的資源,POST 也可以用來更新資源。實際上,POST 請求非冪等的特性(即同一個 URL 可以得到不同的結果)使其成一個非常靈活地方法,對於無法適應其他 HTTP 方法語義的操作,它都能夠勝任。
2. HTTP請求方法的用法
HTTP方法中的GET和HEAD請求都是安全的,無論請求多少次,都不會改變服務器狀態。GET、HEAD、PUT和DELETE請求都是滿足冪等性要求的,也就是無論對資源操作多少次,結果總是一樣的,後面的請求並不會產生比第一次更多的影響。
-
冪等性:對同一REST接口的多次訪問,得到的資源狀態是相同的。
-
安全性:對該REST的接口訪問,不會使服務器端資源的狀態發生改變。
-
GET:安全且冪等;
-
POST:不安全且不冪等;
-
PUT:不安全但冪等;
-
DELETE:不安全但冪等;
3. RESTful案例
在使用 RESTful 風格之前,我們如果想要增加一條分類數據通常是這樣的:
/addCategory?name=xxx
但是使用了 RESTful 風格之後就會變成:
/category
我們可以使用同一個 URI ,通過約定不同的 HTTP 方法來實現不同的業務,例如下圖所示:

4. RESTful的反面案例
RESTful風格的接口要求使用標準的HTTP方法對資源進行操作,所以URI只應該用來表示資源的名稱,而不應該包括對資源的動作操作。
通俗來說,URI不應該使用動作來描述。例如,下面是一些不符合統一接口要求的URI:
GET /getUser/1
POST /createUser
PUT /updateUser/1
DELETE /deleteUser/1
三. SpringBoot中實現RESTful架構風格
1. 需求分析
我們以操作用戶相關的業務爲例,如果採用RESTful API 設計,可以如下所示:

2. 添加依賴包
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--簡化bean代碼-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
3. 創建一個實體類User
package com.yyg.boot.domain;
import lombok.Data;
import java.io.Serializable;
/**
* @Author 一一哥Sun
* @Date Created in 2020/5/13
* @Description Description
*/
@Data
public class User implements Serializable {
private Long id;
private String name;
private Integer age;
}
4. 創建Controller接口方法
package com.yyg.boot.web;
import com.yyg.boot.domain.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.*;
/**
* @Author 一一哥Sun
* @Date Created in 2020/5/13
* @Description Description
*/
@Slf4j
@RestController
@RequestMapping("/users")
public class UserController {
/**
* 創建線程安全的Map
*/
private static Map<Long, User> users = Collections.synchronizedMap(new HashMap<>());
/**
* 處理GET請求,用來獲取用戶列表.
* 可以通過@RequestParam獲取從頁面中傳遞進來的查詢條件或者翻頁信息等參數.
*/
@GetMapping(value="/")
public List<User> getUserList() {
return new ArrayList<>(users.values());
}
/**
* 處理POST請求,用來創建User.
* 除了@ModelAttribute綁定參數之外,還可以通過@RequestParam從頁面中傳遞參數.
*/
@PostMapping(value="/")
public String addUser(@RequestBody User user) {
users.put(user.getId(), user);
log.warn("user==="+users.get(user.getId()));
return "success";
}
/**
* 處理GET請求,用來獲取url中id值的User信息;
* url中的id可通過@PathVariable綁定到函數的參數中.
*/
@GetMapping(value="/{id}")
public User getUser(@PathVariable Long id) {
return users.get(id);
}
/**
* 處理PUT請求,用來更新User信息.
*/
@PutMapping(value="/{id}")
public String updateUser(@PathVariable Long id, @RequestBody User user) {
User u = users.get(id);
u.setName(user.getName());
u.setAge(user.getAge());
users.put(id, u);
return "success";
}
/**
* 處理DELETE請求,用來刪除User
*/
@DeleteMapping(value="/{id}")
public String deleteUser(@PathVariable Long id) {
users.remove(id);
return "success";
}
}
5. 創建入口類
package com.yyg.boot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @Author 一一哥Sun
* @Date Created in 2020/5/13
* @Description Description
*/
@SpringBootApplication
public class RestfulApplication {
public static void main(String[] args){
SpringApplication.run(RestfulApplication.class,args);
}
}
四. 運行測試
1. 添加一個新用戶
執行post請求,傳遞json格式的參數.

2. 查詢全部用戶
執行get請求,查詢剛纔添加的用戶,不需要傳遞任何參數.

3. 更新用戶信息
執行put請求,傳遞id作爲參數.

查詢更新後的結果

4. 刪除用戶
執行delete請求,刪除id爲1的用戶

查詢刪除後的結果

至此我們實現了SpringBoot中的RESTful風格的uri設計。