iT邦幫忙

2025 iThome 鐵人賽

DAY 22
0
Software Development

spring boot 3 學習筆記系列 第 22

Day22 - Spring Boot Web 篇:處理其他類型的請求資訊

  • 分享至 

  • xImage
  •  

Day19Day20Day21 三天單元中,我們學會了如何從 URL (Uniform Resource Locator) 的路徑 (@PathVariable) 和查詢參數 (@RequestParam) 中獲取資訊,也掌握了如何處理請求主體 (Request Body) 中的 JSON 資料 (@RequestBody)。

今天,我們要更上一層樓,探索 HTTP 請求 (Request) 中那些隱藏在幕後的重要資訊。這些資訊就像是每份請求的「元資料 (Metadata)」,它們雖然不直接出現在網址上,卻攜帶著客戶端的關鍵訊息。學會讀取它們,將讓你的應用程式變得更加智慧與安全。

學習目標:擴充知識,了解除了 URL 和 Body,還能從請求標頭 (Header) 和 Cookie 中獲取客戶端的請求資訊。

第一站:@RequestHeader - 讀取請求的「數位護照」

當瀏覽器或手機 App (客戶端) 向我們的伺服器發送請求時,除了主要內容外,還會附上一份「數位護照」,這就是 HTTP 標頭 (HTTP Header)。它記錄了客戶端的身份資訊、偏好設定以及請求的附加說明。

常見的標頭欄位包含:

  • User-Agent:客戶端的類型(例如:Chrome 瀏覽器、iPhone App)。
  • Accept-Language:客戶端偏好的語言(例如:zh-TW, en-US)。
  • Authorization:用於身份驗證的令牌 (Token),例如 API 金鑰或 JWT。

在 Spring Boot 中,@RequestHeader 這個註解 (Annotation) 的任務就是:幫助我們輕鬆讀取這些標頭欄位的值,並將它們綁定 (Bind) 到控制器 (Controller) 方法的參數 (Parameter) 上。

如何使用 @RequestHeader

情境一:讀取必要的標頭 (例如:API 版本)

假設我們的 API 有版本控制,並要求客戶端必須在標頭中提供 API-Version

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HeaderController {

    @GetMapping("/api/version")
    public String getApiVersion(@RequestHeader("API-Version") String apiVersion) {
        return "您正在使用的 API 版本是: " + apiVersion;
    }
}
  • 解析@RequestHeader("API-Version") 告訴 Spring:「請從請求標頭中找出 API-Version 欄位,並將其值賦予給 apiVersion 參數。」
  • 注意@RequestHeader 預設是必要的。如果客戶端發送請求時未包含 API-Version 標頭,Spring 會回傳一個 400 Bad Request 錯誤。

情境二:讀取非必要的標頭並提供預設值 (例如:語言偏好)

有時標頭是可選的,如果客戶端沒提供,我們希望能有個預設行為。

@GetMapping("/greeting")
public String getGreeting(
    @RequestHeader(name = "Accept-Language", defaultValue = "zh-TW") String language
) {
    if (language.startsWith("en")) {
        return "Hello, Guest!";
    }
    return "哈囉,訪客!";
}
  • defaultValue = "zh-TW":如果請求中沒有 Accept-Language 標頭,language 參數的值將會是 zh-TW

情境三:讀取敏感或可選標頭 (例如:認證權杖)

當處理像 Authorization 這樣的認證標頭時,我們通常不希望它不存在時就直接報錯,而是要進行邏輯判斷。這時可以使用 required = false

import java.util.Optional;

@GetMapping("/user/profile")
public String getUserProfile(
    @RequestHeader(name = "Authorization", required = false) Optional<String> authToken
) {
    // 使用 Java 8 的 Optional<T> 是處理可選值的現代且安全的作法
    return authToken
        .map(token -> "Token 驗證成功!Token: " + token)
        .orElse("訪客您好,請提供 Authorization Token 以登入。");
}
  • required = false:告訴 Spring 這個標頭是可選的。
  • Optional<String>:這是一種優雅的寫法,可以避免惱人的空指針異常 (NullPointerException),清晰地表達了「這個值可能存在,也可能不存在」的意圖。

第二站:@CookieValue - 取得客戶端的「專屬識別證」

Cookie 是網站儲存在使用者瀏覽器中的一小段資料,常用來記錄使用者的登入狀態、佈景主題偏好、購物車內容等,就像一張「專屬識別證」。

@CookieValue 的作用非常專一:將 HTTP 請求中特定名稱的 Cookie 值,綁定到方法的參數上。

如何使用 @CookieValue

讓我們來實作一個設定並讀取網站主題的功能。

import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletResponse;

@RestController
public class ThemeController {

    // 1. 設定主題並存入 Cookie
    @GetMapping("/set-theme")
    public String setTheme(@RequestParam String theme, HttpServletResponse response) {
        // 建立一個新的 Cookie
        Cookie themeCookie = new Cookie("user-theme", theme);
        themeCookie.setPath("/"); // 讓整個網站都可存取此 Cookie
        themeCookie.setMaxAge(60 * 60 * 24 * 30); // 設定生命週期為 30 天

        // 將 Cookie 加入到 HTTP 回應 (Response) 中,瀏覽器收到後會儲存起來
        response.addCookie(themeCookie);
        return "您的主題已設定為:" + theme;
    }

    // 2. 讀取 Cookie 中的主題
    @GetMapping("/my-theme")
    public String getTheme(
        @CookieValue(name = "user-theme", defaultValue = "light") String userTheme
    ) {
        return "您目前的主題是:" + userTheme;
    }
}
  • 測試流程
    1. 先訪問 http://localhost:8080/set-theme?theme=dark 來設定 Cookie。
    2. 再訪問 http://localhost:8080/my-theme,你會看到 @CookieValue 成功讀取到了 dark 這個值。
  • defaultValue = "light":我們也為首次來訪、還沒有 Cookie 的使用者提供了預設的 light 主題,避免了程式因找不到 Cookie 而報錯。

第三站:@ModelAttribute - 傳統網頁表單的資料裝訂工

現在,讓我們來談談一個在傳統 Spring MVC (Model-View-Controller) 開發模式中非常重要的註解 (Annotation)。

想像一下使用者註冊時需要填寫一個包含許多欄位的 HTML 表單。如果用 @RequestParam 一個個去接收,程式碼會非常冗長。@ModelAttribute 就是為了解決這個問題而生,它可以自動將前端表單提交的參數,綁定到一個 Java 物件上

@ModelAttribute vs. @RequestBody 的關鍵區別

這是初學者最容易混淆的觀念,但也是區分傳統 MVC 與現代 API 的關鍵。它們的核心區別在於資料來源使用情境

特性 @ModelAttribute @RequestBody
資料來源 來自請求參數 (Parameters) 來自請求主體 (Body)
內容格式 (Content-Type) application/x-www-form-urlencodedmultipart/form-data application/json, application/xml
比喻 像填寫一份紙本問卷,一欄一欄各自獨立 像寄送一個密封的包裹,所有內容都在裡面
使用情境 傳統的 HTML <form> 標籤提交 現代前後端分離架構,由 JavaScript (如 Vue, React) 發送的 AJAX/Fetch 請求
數量限制 一個方法中可以有多個 一個方法中只能有一個

簡單判斷法則

  • 前端是透過 <form action="..." method="post"> 提交的嗎?用 @ModelAttribute
  • 前端是使用 JavaScript 發送一個 JSON 字串過來的嗎?用 @RequestBody

如何使用 @ModelAttribute

第一步:建立一個資料傳輸物件 (Data Transfer Object, DTO)

這個 DTO 物件的屬性,要和 HTML 表單中 <input> 標籤的 name 屬性相對應。

public class UserRegistrationDto {
    private String username;
    private String password;
    private String email;
    // ... Getters and Setters ...
}

第二步:在 Controller 方法中使用 @ModelAttribute

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller // 注意:這裡是 @Controller,因為通常會搭配回傳網頁視圖
public class RegistrationController {

    @PostMapping("/register")
    @ResponseBody // 為了方便演示,我們直接回傳字串,實務上可能會重導向到成功頁面
    public String processRegistration(@ModelAttribute UserRegistrationDto userDto) {
        // 當程式執行到這裡,Spring 已經自動將表單資料填充到 userDto 物件中了!
        System.out.println("新註冊使用者: " + userDto.getUsername());
        return "註冊成功!歡迎 " + userDto.getUsername();
    }
}

當使用者提交表單時,Spring 會自動建立 UserRegistrationDto 物件,並根據請求參數的名稱 (username, password...) 呼叫對應的 setter 方法,最後將填充好的物件傳遞給你的方法。

單元成果與總結

恭喜你完成了本日的學習!現在,你已經掌握了 Spring Boot 中處理各種請求資訊的全面技能:

  • @PathVariable / @RequestParam:處理來自 URL 的資料。
  • @RequestBody:處理來自 Request Body 的 JSON 資料,用於現代 API。
  • @RequestHeader:處理來自 Request Header 的元資料,常用於認證、獲取客戶端資訊。
  • @CookieValue:處理來自 Cookie 的資料,常用於會話管理。
  • @ModelAttribute:處理來自 HTML 表單的參數,常用於傳統 MVC 應用。

你現在已經具備處理更複雜 Web 互動的能力,例如開發一個需要檢查 Authorization 標頭才有權限訪問的 API,或是根據使用者的 Cookie 提供個人化的內容。繼續加油!

相關資料來源


上一篇
Day21 - Spring Boot Web 篇:處理複雜的請求資料 (JSON)
下一篇
Day23 - Spring Data JPA 入門:從 H2 到 PostgreSQL
系列文
spring boot 3 學習筆記30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言