iT邦幫忙

2025 iThome 鐵人賽

DAY 21
0
Software Development

spring boot 3 學習筆記系列 第 21

Day21 - Spring Boot Web 篇:處理複雜的請求資料 (JSON)

  • 分享至 

  • xImage
  •  

Day19Day20 兩天單元中,我們學會了如何建立 API 端點 (@RestController, @GetMapping),以及如何從 URL 中獲取簡單的資料 (@PathVariable, @RequestParam)。

然而,在真實世界的應用中,客戶端(例如網頁或手機 App)需要傳送的資料往往更複雜。例如,要新增一位使用者,我們可能需要同時傳送姓名、電子郵件、密碼、年齡等多個欄位的資訊。如果把這些都放在 URL 中,會變得非常冗長且不安全。

這時,我們就需要透過 請求主體 (Request Body) 來傳送結構化的 JSON 資料。本單元將帶你掌握接收與回傳 JSON 的核心技巧,打通 API 資料交換的「雙向道」。

學習目標

完成本單元後,你將能夠:

  1. 理解如何透過請求主體 (Request Body) 傳遞複雜的 JSON 資料。
  2. 熟練運用 @RequestBody 將傳入的 JSON 字串自動轉換為 Java 物件。
  3. 掌握 @PostMapping@PutMapping 的使用時機,並建立「新增」與「更新」資料的 API。
  4. 親手打造一個接收 JSON 資料來新增使用者的完整 API。

第一站:@RequestBody - 將 JSON 包裹自動拆開的魔法師

當客戶端發送一個包含 JSON 資料的請求時,這些 JSON 內容會被放在 HTTP 請求的「主體 (Body)」中。在 Spring Boot 這端,我們需要一個機制來讀取這些資料,並將其轉換成我們熟悉的 Java 物件。這個偉大的任務就交給了 @RequestBody

@RequestBody 的核心作用:它會告訴 Spring 框架:「請檢查這次請求的 Body,裡面應該有 JSON 格式的資料。請你運用內建的函式庫 (Library) 將這段 JSON 字串『反序列化 (Deserialize)』,並自動對應填充到我指定的 Java 物件中。」

你可以把 @RequestBody 想像成一個智慧包裹拆箱員

  1. 客戶端:將一堆零件(姓名、年齡等資料)打包成一個 JSON 包裹(Request Body)。
  2. Spring Boot API:收到這個包裹。
  3. @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 測試工具,並設定:

  • 方法 (Method)POST
  • 網址 (URL)http://localhost:8080/users
  • 標頭 (Header)Content-Type: application/json
  • 主體 (Body) (選擇 rawJSON 格式):
{
    "username": "Vincent",
    "email": "vincent@example.com",
    "age": 28
}

當你發送這個請求後,Spring Boot 就會自動將這個 JSON 內容填充到 CreateUserRequest 物件中。

第二站:選擇正確的 HTTP 方法 - @PostMapping vs @PutMapping

在 RESTful API 的設計風格中,不同的 HTTP 方法有其特定的語意。當搭配 @RequestBody 使用時,最常見的是 @PostMapping@PutMapping

  • @PostMapping (新增 Create)

    • 用途:當你想要建立一個全新的資源時使用。
    • 語意:客戶端告訴伺服器:「請幫我用這些資料,新增一筆紀錄。」
    • URL 結構:通常作用在一個集合資源上,例如 POST /users, POST /products
  • @PutMapping (更新/替換 Update/Replace)

    • 用途:當你想要完整更新或替換一個已存在的資源時使用。
    • 語意:客戶端告訴伺服器:「請把指定 ID 的那筆紀錄,用我提供的新資料完全覆蓋掉。」
    • URL 結構:通常作用在一個特定的資源上,因此會和 @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 + " 的使用者資料。";
    }
}

單元成果:打造一個更完整的 API

現在,讓我們將「接收」和「回傳」結合起來。一個好的 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" 部分了。

相關資料來源


上一篇
Day20 - Spring Boot Web 篇:接收來自 URL 的動態參數
下一篇
Day22 - Spring Boot Web 篇:處理其他類型的請求資訊
系列文
spring boot 3 學習筆記30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言