在Day19 和 Day20 兩天單元中,我們學會了如何建立 API 端點 (@RestController
, @GetMapping
),以及如何從 URL 中獲取簡單的資料 (@PathVariable
, @RequestParam
)。
然而,在真實世界的應用中,客戶端(例如網頁或手機 App)需要傳送的資料往往更複雜。例如,要新增一位使用者,我們可能需要同時傳送姓名、電子郵件、密碼、年齡等多個欄位的資訊。如果把這些都放在 URL 中,會變得非常冗長且不安全。
這時,我們就需要透過 請求主體 (Request Body) 來傳送結構化的 JSON 資料。本單元將帶你掌握接收與回傳 JSON 的核心技巧,打通 API 資料交換的「雙向道」。
完成本單元後,你將能夠:
@RequestBody
將傳入的 JSON 字串自動轉換為 Java 物件。@PostMapping
與 @PutMapping
的使用時機,並建立「新增」與「更新」資料的 API。@RequestBody
- 將 JSON 包裹自動拆開的魔法師當客戶端發送一個包含 JSON 資料的請求時,這些 JSON 內容會被放在 HTTP 請求的「主體 (Body)」中。在 Spring Boot 這端,我們需要一個機制來讀取這些資料,並將其轉換成我們熟悉的 Java 物件。這個偉大的任務就交給了 @RequestBody
。
@RequestBody
的核心作用:它會告訴 Spring 框架:「請檢查這次請求的 Body,裡面應該有 JSON 格式的資料。請你運用內建的函式庫 (Library) 將這段 JSON 字串『反序列化 (Deserialize)』,並自動對應填充到我指定的 Java 物件中。」
你可以把 @RequestBody
想像成一個智慧包裹拆箱員。
@RequestBody
:自動辨識包裹上的標籤,精準地將裡面的零件一個個取出來,組裝成一個你定義好的 Java 物件,讓你直接在程式碼中使用。@RequestBody
?使用 @RequestBody
通常有兩個步驟:
步驟一:定義一個「容器」來裝載資料 - 資料傳輸物件 (Data Transfer Object, DTO)
我們需要先建立一個普通的 Java 類別 (Class),它的屬性 (Field) 必須和我們預期收到的 JSON 的鍵 (Key) 相對應。這個類別通常被稱為 DTO。
範例:建立一個 CreateUserRequest
DTO
// 這是一個純粹的資料容器,用來接收新增使用者時傳入的 JSON 資料
public class CreateUserRequest {
private String username;
private String email;
private int age;
// 為了讓 Spring Boot 能夠順利轉換 JSON,
// getter 和 setter 方法是必須的!
// (現代開發中,也可以使用 Lombok 的 @Data 註解 (Annotation) 來自動產生)
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
步驟二:在控制器 (Controller) 方法的參數上使用 @RequestBody
現在,我們可以在控制器的方法中使用這個 DTO,並在它前面加上 @RequestBody
註解 (Annotation)。
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
// 使用 @PostMapping 來處理 "新增" 資源的請求
@PostMapping("/users")
public String createUser(@RequestBody CreateUserRequest request) {
// 當程式執行到這裡時,Spring 已經幫我們把 JSON 轉換成 request 物件了!
System.out.println("接收到的使用者名稱: " + request.getUsername());
System.out.println("接收到的電子郵件: " + request.getEmail());
System.out.println("接收到的年齡: " + request.getAge());
// 實務上,這裡會是將資料存入資料庫的邏輯
return "成功建立使用者:" + request.getUsername();
}
}
客戶端如何發送請求?
要測試這個 API,你不能再只用瀏覽器輸入網址(因為那只會發送 GET 請求)。你需要使用像 Postman、Insomnia 或 curl
指令等 API 測試工具,並設定:
POST
http://localhost:8080/users
Content-Type: application/json
raw
和 JSON
格式):{
"username": "Vincent",
"email": "vincent@example.com",
"age": 28
}
當你發送這個請求後,Spring Boot 就會自動將這個 JSON 內容填充到 CreateUserRequest
物件中。
@PostMapping
vs @PutMapping
在 RESTful API 的設計風格中,不同的 HTTP 方法有其特定的語意。當搭配 @RequestBody
使用時,最常見的是 @PostMapping
和 @PutMapping
。
@PostMapping
(新增 Create)
POST /users
, POST /products
。@PutMapping
(更新/替換 Update/Replace)
@PathVariable
結合使用。例如 PUT /users/123
(更新 ID 為 123 的使用者)。範例:結合 @PutMapping
、@PathVariable
和 @RequestBody
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.PathVariable;
// ...其他 import
@RestController
public class UserController {
// ... 上面的 @PostMapping("/users") 方法 ...
// 使用 @PutMapping 來處理 "更新" 指定資源的請求
@PutMapping("/users/{id}")
public String updateUser(@PathVariable Long id, @RequestBody CreateUserRequest request) {
// {id} 從 URL 路徑中獲取
// request 從 Request Body 中獲取
System.out.println("準備更新 ID 為 " + id + " 的使用者...");
System.out.println("新的使用者名稱: " + request.getUsername());
System.out.println("新的電子郵件: " + request.getEmail());
System.out.println("新的年齡: " + request.getAge());
// 實務上,這裡會是根據 id 查詢資料庫,然後用 request 的內容去更新資料
return "成功更新 ID: " + id + " 的使用者資料。";
}
}
現在,讓我們將「接收」和「回傳」結合起來。一個好的 POST
API 在成功建立資源後,應該回傳新建資源的詳細資訊(例如,包含由資料庫產生的 ID)。
1. 建立一個 User
回應物件
// 這個物件代表我們系統中的使用者資源,通常會包含一個唯一的 ID
public class User {
private Long id;
private String username;
private String email;
private int age;
// ... 省略 getter 和 setter ...
}
2. 改造 createUser
方法
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.atomic.AtomicLong;
@RestController
public class UserController {
// 為了範例簡單,我們用一個原子計數器來模擬資料庫自動產生的 ID
private final AtomicLong counter = new AtomicLong();
@PostMapping("/users")
// 修改回傳類型為 User 物件
public User createUser(@RequestBody CreateUserRequest request) {
// --- 模擬儲存到資料庫的過程 ---
User newUser = new User();
newUser.setId(counter.incrementAndGet()); // 產生一個新的 ID
newUser.setUsername(request.getUsername());
newUser.setEmail(request.getEmail());
newUser.setAge(request.getAge());
// --- 模擬結束 ---
// 因為我們使用了 @RestController,Spring Boot 會自動將回傳的 newUser 物件
// 序列化 (Serialize) 成 JSON 格式,並回傳給客戶端!
return newUser;
}
// ... 其他方法 ...
}
現在,當客戶端發送 POST /users
請求後,它會收到類似下面這樣的回應,其中包含了伺服器分配的 id
:
{
"id": 1,
"username": "Vincent",
"email": "vincent@example.com",
"age": 28
}
恭喜你!你已經掌握了現代 API 開發中最核心的資料交換技能。透過 @RequestBody
接收 JSON,並利用 @RestController
的特性回傳 JSON,你已經能夠建構出功能完整的 CRUD (Create, Read, Update, Delete) API 中的 "Create" 和 "Update" 部分了。