Skip to content

Spring MVC @RequestHeader 注解详解 📝

概述

在 Web 开发中,HTTP 请求头(Request Headers)承载着客户端的重要信息,如浏览器类型、语言偏好、编码方式等。Spring MVC 提供了 @RequestHeader 注解,让我们能够轻松地在控制器方法中获取和使用这些请求头信息。

NOTE

@RequestHeader 是 Spring MVC 中用于绑定 HTTP 请求头到控制器方法参数的注解,它让我们能够直接访问客户端发送的头部信息。

为什么需要 @RequestHeader? 🤔

传统方式的痛点

在没有 @RequestHeader 注解之前,获取请求头信息需要通过 HttpServletRequest 对象:

kotlin
@GetMapping("/demo")
fun handle(request: HttpServletRequest) {
    // 手动从 HttpServletRequest 中获取请求头
    val encoding = request.getHeader("Accept-Encoding") 
    val keepAlive = request.getHeader("Keep-Alive")?.toLongOrNull() ?: 0L
    
    // 需要手动处理类型转换和空值检查
    if (encoding != null) {
        // 处理逻辑...
    }
}
kotlin
@GetMapping("/demo")
fun handle(
    @RequestHeader("Accept-Encoding") encoding: String, 
    @RequestHeader("Keep-Alive") keepAlive: Long
) {
    // 直接使用,Spring 自动处理类型转换
    println("编码方式: $encoding")
    println("保持连接时间: $keepAlive 秒")
}

@RequestHeader 的优势

  • 简洁性:直接在方法参数上声明,无需手动获取
  • 类型安全:自动类型转换,减少类型转换错误
  • 可读性:方法签名清晰表达了所需的请求头
  • 维护性:集中管理请求头绑定逻辑

基本用法 🚀

简单示例

让我们通过一个实际的例子来理解 @RequestHeader 的基本用法:

kotlin
@RestController
class HeaderController {
    
    @GetMapping("/user-info")
    fun getUserInfo(
        @RequestHeader("User-Agent") userAgent: String, 
        @RequestHeader("Accept-Language") language: String
    ): Map<String, Any> {
        return mapOf(
            "userAgent" to userAgent,
            "preferredLanguage" to language,
            "timestamp" to System.currentTimeMillis()
        )
    }
}

HTTP 请求示例

http
GET /user-info HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Accept-Encoding: gzip, deflate, br

高级用法 ⚙️

1. 自动类型转换

Spring 会自动将请求头的字符串值转换为目标类型:

kotlin
@GetMapping("/connection-info")
fun getConnectionInfo(
    @RequestHeader("Keep-Alive") keepAlive: Long, 
    @RequestHeader("Content-Length") contentLength: Int, 
    @RequestHeader("X-Custom-Flag") customFlag: Boolean
) {
    println("保持连接: $keepAlive 秒")
    println("内容长度: $contentLength 字节")
    println("自定义标志: $customFlag")
}

2. 可选请求头处理

使用可空类型或默认值处理可选的请求头:

kotlin
@GetMapping("/optional-headers")
fun handleOptionalHeaders(
    @RequestHeader("Authorization") authorization: String?, 
    @RequestHeader(value = "X-Request-ID", defaultValue = "unknown") requestId: String, 
    @RequestHeader(value = "X-Debug", required = false) debug: String? 
) {
    println("授权信息: ${authorization ?: "未提供"}")
    println("请求ID: $requestId")
    println("调试模式: ${debug ?: "关闭"}")
}

TIP

使用 required = falsedefaultValue 可以让某些请求头变为可选,避免客户端必须提供所有请求头。

3. 获取所有请求头

有时我们需要获取所有的请求头信息:

kotlin
@GetMapping("/all-headers")
fun getAllHeaders(
    @RequestHeader headers: Map<String, String> 
): Map<String, String> {
    println("所有请求头:")
    headers.forEach { (key, value) ->
        println("$key: $value")
    }
    return headers
}

4. 使用 HttpHeaders 对象

对于更复杂的请求头操作,可以使用 HttpHeaders

kotlin
@GetMapping("/http-headers")
fun handleHttpHeaders(
    @RequestHeader httpHeaders: HttpHeaders
) {
    // 获取 Accept 头的所有值
    val acceptTypes = httpHeaders.accept
    
    // 获取自定义头
    val customValues = httpHeaders["X-Custom-Header"]
    
    // 检查是否包含某个头
    val hasAuth = httpHeaders.containsKey("Authorization")
    
    println("接受的媒体类型: $acceptTypes")
    println("自定义头值: $customValues")
    println("是否包含授权头: $hasAuth")
}

实际业务场景 💼

场景1:API 版本控制

kotlin
@RestController
class ApiVersionController {
    
    @GetMapping("/api/users")
    fun getUsers(
        @RequestHeader(value = "API-Version", defaultValue = "v1") version: String
    ): List<Any> {
        return when (version) {
            "v1" -> getUsersV1()
            "v2" -> getUsersV2()
            else -> throw IllegalArgumentException("不支持的 API 版本: $version")
        }
    }
    
    private fun getUsersV1(): List<Any> = listOf("用户数据 V1")
    private fun getUsersV2(): List<Any> = listOf("用户数据 V2 - 增强版")
}

场景2:国际化支持

kotlin
@RestController
class I18nController {
    
    @GetMapping("/welcome")
    fun getWelcomeMessage(
        @RequestHeader(value = "Accept-Language", defaultValue = "en") language: String
    ): Map<String, String> {
        val message = when {
            language.startsWith("zh") -> "欢迎使用我们的服务!"
            language.startsWith("en") -> "Welcome to our service!"
            language.startsWith("ja") -> "私たちのサービスへようこそ!"
            else -> "Welcome to our service!"
        }
        
        return mapOf(
            "message" to message,
            "language" to language
        )
    }
}

场景3:安全认证

kotlin
@RestController
class SecurityController {
    
    @GetMapping("/protected")
    fun getProtectedData(
        @RequestHeader("Authorization") authorization: String
    ): Map<String, Any> {
        // 验证 Bearer Token
        if (!authorization.startsWith("Bearer ")) {
            throw IllegalArgumentException("无效的授权格式")
        }
        
        val token = authorization.substring(7) // 移除 "Bearer " 前缀
        
        // 这里应该验证 token 的有效性
        if (isValidToken(token)) {
            return mapOf(
                "data" to "敏感数据",
                "user" to getUserFromToken(token)
            )
        } else {
            throw IllegalArgumentException("无效的访问令牌")
        }
    }
    
    private fun isValidToken(token: String): Boolean = token.isNotEmpty()
    private fun getUserFromToken(token: String): String = "用户123"
}

请求头处理流程 🔃

让我们通过时序图来理解 @RequestHeader 的工作流程:

常见问题与最佳实践 💡

1. 请求头名称大小写问题

WARNING

HTTP 请求头名称是大小写不敏感的,但建议使用标准的大小写格式。

kotlin
// 这些都是等效的
@RequestHeader("content-type") contentType1: String
@RequestHeader("Content-Type") contentType2: String
@RequestHeader("CONTENT-TYPE") contentType3: String

2. 处理多值请求头

某些请求头可能包含多个值,用逗号分隔:

kotlin
@GetMapping("/accept-types")
fun handleAcceptTypes(
    @RequestHeader("Accept") acceptTypes: List<String> 
) {
    println("客户端接受的媒体类型:")
    acceptTypes.forEach { type ->
        println("- $type")
    }
}

3. 自定义请求头验证

kotlin
@GetMapping("/custom-validation")
fun handleCustomValidation(
    @RequestHeader("X-API-Key") apiKey: String
) {
    if (!isValidApiKey(apiKey)) {
        throw IllegalArgumentException("无效的 API 密钥")
    }
    // 处理业务逻辑...
}

private fun isValidApiKey(apiKey: String): Boolean {
    return apiKey.matches(Regex("^[A-Za-z0-9]{32}$"))
}

性能考虑 ⚡

避免过度使用

kotlin
// ❌ 不推荐:获取所有请求头但只使用少数几个
@GetMapping("/inefficient")
fun inefficientHandler(@RequestHeader headers: Map<String, String>) {
    val userAgent = headers["User-Agent"]
    // 只使用了一个请求头,但获取了所有
}

// ✅ 推荐:只获取需要的请求头
@GetMapping("/efficient")
fun efficientHandler(@RequestHeader("User-Agent") userAgent: String) {
    // 直接获取需要的请求头
}

总结 📝

@RequestHeader 注解是 Spring MVC 中处理 HTTP 请求头的强大工具,它提供了:

  • 简洁的语法:直接在方法参数上声明所需的请求头
  • 自动类型转换:无需手动处理字符串到其他类型的转换
  • 灵活的配置:支持可选参数、默认值等配置
  • 多种绑定方式:支持单个值、多个值、Map 等不同的绑定方式

通过合理使用 @RequestHeader,我们可以构建更加健壮、易维护的 Web 应用程序,有效处理客户端发送的各种请求头信息。

TIP

在实际项目中,建议结合业务需求合理使用 @RequestHeader,避免过度获取不必要的请求头信息,以保持代码的简洁性和性能。