我們來詳細解釋一下 Spring MVC 中的 @RequestHeader
注解。
@RequestHeader
注解的作用
@RequestHeader
注解用于將 HTTP 請求中的**請求頭(Request Headers)**的值綁定到 Controller 方法的參數上。
請求頭是 HTTP 請求的一部分,包含了關于請求本身、客戶端、期望的響應格式等元數據信息。常見的請求頭有 User-Agent
(瀏覽器/客戶端信息)、Accept
(客戶端可接受的內容類型)、Content-Type
(請求體的類型)、Authorization
(認證信息)、Accept-Language
(語言偏好)以及各種自定義頭(通常以 X-
開頭,如 X-API-Key
)。
@RequestHeader
允許我們在 Controller 方法中訪問這些頭信息。
基本用法
最直接的用法是指定要讀取的請求頭的名稱,并將其值賦給方法參數。
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.ResponseBody;@Controller
public class HeaderController {// 讀取 "User-Agent" 請求頭@GetMapping("/show-user-agent")@ResponseBodypublic String showUserAgent(@RequestHeader("User-Agent") String userAgent) {return "User-Agent Header is: " + userAgent;}// 讀取 "Accept-Language" 請求頭@GetMapping("/show-language")@ResponseBodypublic String showAcceptLanguage(@RequestHeader("Accept-Language") String language) {return "Accepted Languages: " + language;}// 讀取自定義請求頭 "X-Custom-Header"@GetMapping("/show-custom-header")@ResponseBodypublic String showCustomHeader(@RequestHeader("X-Custom-Header") String customHeaderValue) {return "X-Custom-Header value: " + customHeaderValue;}
}
重要: HTTP 協議中請求頭名稱是大小寫不敏感的。然而,在 @RequestHeader
注解中指定的名稱("User-Agent"
, "Accept-Language"
)會與實際發送的頭名稱進行不區分大小寫的匹配。但為了代碼清晰和避免潛在問題,最好在注解中明確使用期望的、標準的頭名稱。
@RequestHeader
的屬性
與 @RequestParam
類似,@RequestHeader
也提供了一些屬性來定制行為:
-
name
(或value
):- 必需屬性(除非參數名匹配)。指定要綁定的請求頭的名稱。這是最常用的屬性。
name
和value
是同義詞。- 示例:
@RequestHeader(name = "Authorization") String token
。 - 注意:雖然理論上如果方法參數名與頭名稱(轉換為駝峰式,例如
userAgent
對應User-Agent
)匹配,可能可以省略name
,但這依賴于編譯時參數名保留,并且對于包含-
的標準頭名稱(如User-Agent
)根本行不通。因此,強烈建議始終使用name
或value
屬性明確指定頭名稱。
-
required
:- 指定該請求頭是否必須存在于請求中。
- 類型:
boolean
。 - 默認值:
true
。如果required=true
,但請求中沒有該頭信息,Spring MVC 會拋出MissingRequestHeaderException
異常,導致 HTTP 400 (Bad Request) 響應。 - 如果請求頭是可選的,需要設置為
required = false
。
-
defaultValue
:- 當請求中沒有提供該請求頭時,為其提供一個默認值。
- 類型:
String
。Spring 會嘗試將這個字符串默認值轉換為方法參數的目標類型(盡管對于Header,目標類型通常就是String
)。 - 注意: 使用
defaultValue
隱含了required = false
的行為。提供了defaultValue
后,即使不顯式設置required = false
,該頭也不再是必需的。如果頭不存在,就會使用默認值,不會拋出異常。
處理可選請求頭和默認值
場景 1:請求頭可選,如果不存在則為 null
import java.util.Optional;
// ...@GetMapping("/optional-header")
@ResponseBody
public String processOptionalHeader(@RequestHeader(name = "X-Optional-Info", required = false) String optionalInfo,@RequestHeader(name = "X-Another-Optional", required = false) Optional<String> anotherOptional) {String infoMessage = (optionalInfo != null) ? "X-Optional-Info: " + optionalInfo : "X-Optional-Info header is missing";String anotherMessage;if (anotherOptional.isPresent()) {anotherMessage = "X-Another-Optional: " + anotherOptional.get();} else {anotherMessage = "X-Another-Optional header is missing";}return infoMessage + "\n" + anotherMessage;
}
// 請求 /optional-header (無對應Header) -> 輸出兩條 "header is missing"
// 請求 /optional-header 且帶 Header X-Optional-Info: hello -> 輸出 "X-Optional-Info: hello" 和另一條 missing
// 請求 /optional-header 且帶 Header X-Another-Optional: world -> 輸出第一條 missing 和 "X-Another-Optional: world"
- 使用
required = false
,如果 Header 不存在,對應的 String 參數會是null
。 - 使用
Optional<String>
(Spring 4.1+) 也可以處理可選值。
場景 2:請求頭可選,如果不存在則使用默認值
@GetMapping("/theme")
@ResponseBody
public String getTheme(@RequestHeader(name = "X-Theme-Preference", defaultValue = "light") String theme) {// 如果請求中沒有 X-Theme-Preference 頭,theme 的值將是 "light"// 如果請求頭是 X-Theme-Preference: dark,theme 的值將是 "dark"return "Using theme: " + theme;
}
處理多值請求頭
有些請求頭可能包含多個值(通常是逗號分隔的,如 Accept
頭)。我們可以將方法參數聲明為 List
或數組類型來接收所有值。
import java.util.List;
// ...@GetMapping("/accepted-types")
@ResponseBody
public String showAcceptedTypes(@RequestHeader("Accept") String[] acceptTypes) {// 如果 Accept 頭是 "application/json, application/xml", acceptTypes 會是 ["application/json", "application/xml"]return "Client accepts: " + Arrays.toString(acceptTypes);
}@GetMapping("/cache-control")
@ResponseBody
public String showCacheControl(@RequestHeader("Cache-Control") List<String> cacheDirectives) {// 如果 Cache-Control 頭是 "no-cache, no-store", cacheDirectives 會是 ["no-cache", "no-store"]return "Cache-Control directives: " + cacheDirectives;
}
Spring 會自動根據逗號 ,
來分割頭的值。
綁定所有請求頭到 Map 或 HttpHeaders
如果我們想訪問請求中的所有頭信息,可以將 @RequestHeader
應用于 Map<String, String>
、MultiValueMap<String, String>
或 HttpHeaders
對象。
import org.springframework.http.HttpHeaders;
import org.springframework.util.MultiValueMap;
import java.util.Map;
// ...@GetMapping("/all-headers-map")
@ResponseBody
public String getAllHeadersMap(@RequestHeader Map<String, String> headers) {// headers 包含所有請求頭的 key-value 對 (key 是小寫形式)// 注意: 如果一個頭有多個值, Map 可能只保留一個return "All headers (Map): " + headers.toString();
}@GetMapping("/all-headers-multimap")
@ResponseBody
public String getAllHeadersMultiMap(@RequestHeader MultiValueMap<String, String> headers) {// MultiValueMap 可以正確處理一個頭有多個值的情況 (key 是小寫形式)return "All headers (MultiValueMap): " + headers.toString();
}@GetMapping("/all-headers-object")
@ResponseBody
public String getAllHeadersObject(@RequestHeader HttpHeaders headers) {// HttpHeaders 是 Spring 提供的專用對象,提供了方便的方法來訪問頭信息// 例如: headers.getContentType(), headers.getAcceptLanguageAsLocales(), headers.getFirst("X-Custom-Header")long contentLength = headers.getContentLength();return "All headers (HttpHeaders object). Content-Length: " + contentLength + ". Headers: " + headers.toString();
}
- 推薦使用
HttpHeaders
對象,因為它提供了類型安全的方法來訪問常見的頭,并且能正確處理多值頭和大小寫問題。
總結
@RequestHeader
用于將 HTTP 請求頭 的值綁定到方法參數。- 必須 使用
name
或value
屬性指定要讀取的頭名稱(除非特殊情況,但不推薦省略)。 - 使用
required = false
使頭變為可選(不存在時為null
或Optional.empty()
)。 - 使用
defaultValue = "value"
為可選頭提供默認值(頭不存在時生效,隱含required=false
)。 - 可以將多值頭(逗號分隔)綁定到
List
或數組。 - 可以通過將
@RequestHeader
應用于Map
,MultiValueMap
或(推薦的)HttpHeaders
對象來訪問所有頭信息。 - 與
@RequestParam
(用于查詢/表單參數)和@PathVariable
(用于路徑變量)不同,@RequestHeader
專注于 HTTP Headers。