处理HTTP请求体:精通`@RequestBody`、`@RequestHeader`与`@CookieValue`
摘要: 在上一章中,我们精通了如何从URL中提取信息,但这通常只用于定位资源或传递简单参数。当客户端需要向服务器发送复杂的数据结构(如创建一个新用户的完整信息)时,HTTP请求体(Request Body)便成为了主角。本章,我们将深入学习处理HTTP请求中“内容”部分的三大关键注解:
@RequestBody
、@RequestHeader
和@CookieValue
。通过掌握它们,你将能够轻松处理POST、PUT等请求中携带的JSON数据,读取请求头信息,并与客户端的Cookie进行交互,从而构建功能完备的Web API。
引言:超越URL的交互
在第13章,我们学会了如何像一个侦探一样,从URL的各个角落(路径、查询参数、矩阵变量)中搜集线索。这对于GET请求来说通常已经足够。但Web世界远不止于此。当我们需要创建(POST)、更新(PUT)或删除(DELETE)资源时,往往需要传递更丰富、更结构化的信息。这些信息,正是存在HTTP请求的“包裹”——**请求体(Request Body)**中。
想象一下,注册一个新用户需要填写用户名、密码、邮箱、年龄等十几个字段,如果把这些都塞进URL里,那将是一场灾难。正确的做法是将这些数据打包成一个JSON对象,放在请求体中发送。
本章,我们将聚焦于如何打开并处理这个“数据包裹”,以及如何读取附在包裹上的“标签”(请求头)和“特殊凭证”(Cookie)。
一、@RequestBody
:接收和转换请求体数据
@RequestBody
是处理现代API(尤其是RESTful API)时最重要、最常用的注解之一。它告诉Spring Boot将HTTP请求体中的内容(通常是JSON或XML格式)反序列化成一个Java对象。
1. 场景
客户端希望创建一个新用户,它会发送一个POST请求到/users
,请求体中包含如下JSON数据:
{"username": "CoderBO","email": "coderbo@example.com","age": 28
}
我们需要在后端接收这个JSON,并将其转换为一个User
对象进行处理。
2. 创建数据传输对象 (DTO)
为了承载请求体中的数据,最佳实践是创建一个专门的DTO(Data Transfer Object)。
在com.example.myfirstapp
下创建一个dto
包,并新建UserCreationDTO.java
:
package com.example.myfirstapp.dto;// DTO不需要是实体类,它只是一个简单的数据载体
public class UserCreationDTO {private String username;private String email;private Integer age;// 为了让Spring的Jackson库能正确反序列化,// 必须提供 public 的 getter 和 setter 方法。// 一个无参构造函数也是推荐的,尽管不总是必需。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 Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return "UserCreationDTO{" +"username='" + username + '\'' +", email='" + email + '\'' +", age=" + age +'}';}
}
3. 代码实战
在UserController
中,添加一个处理POST请求的方法:
import com.example.myfirstapp.dto.UserCreationDTO;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
// ... 其他import@RestController
@RequestMapping("/users")
public class UserController {// ... 其他GET方法 .../*** 使用 @RequestBody 接收POST请求的JSON数据体* @param userDTO Spring会自动将请求体中的JSON映射到这个DTO对象的字段上* @return 返回一个表示操作成功的Map*/@PostMappingpublic Map<String, Object> createUser(@RequestBody UserCreationDTO userDTO) {// 在实际应用中,这里会调用Service层将DTO转换为实体并存入数据库System.out.println("Received user to create: " + userDTO);return Map.of("success", true,"message", "User created successfully","createdUser", userDTO );}
}
代码解读:
@PostMapping
: 这是一个快捷注解,等同于@RequestMapping(method = RequestMethod.POST)
。它将此方法绑定到对/users
的POST请求。@RequestBody UserCreationDTO userDTO
: 这是核心。@RequestBody
指示Spring查看HTTP请求的内容部分,并使用内置的HttpMessageConverter
(对于JSON,默认是Jackson)将其转换为UserCreationDTO
类型的Java对象。
4. 效果演示
这次我们必须使用能发送POST请求并附带请求体的工具,如curl
或Postman。
使用curl
:
curl -X POST http://localhost:8080/users \
-H "Content-Type: application/json" \
-d '{"username": "CoderBO","email": "coderbo@example.com","age": 28
}'
响应:
{"success": true,"message": "User created successfully","createdUser": {"username": "CoderBO","email": "coderbo@example.com","age": 28}
}
同时,你会在应用的控制台看到打印的日志。
二、@RequestHeader
:读取请求头信息
HTTP请求头包含了关于请求的元数据,如客户端类型(User-Agent
)、期望的响应格式(Accept
)等。@RequestHeader
可以方便地将这些值注入到方法参数中。
1. 场景
我们需要记录下是哪个客户端(浏览器、移动App、爬虫)发起了请求,或者根据特定的头信息(如X-Client-Version
)执行不同的逻辑。
2. 代码实战
在UserController
中添加一个新方法:
import org.springframework.web.bind.annotation.RequestHeader;
// ... 其他import@RestController
@RequestMapping("/users")
public class UserController {// ... 其他方法 ...@GetMapping("/headers")public Map<String, String> getHeaders(@RequestHeader("User-Agent") String userAgent,@RequestHeader(value = "Accept-Language", defaultValue = "en-US") String acceptLanguage,@RequestHeader(value = "X-Custom-Header", required = false) String customHeader) {return Map.of("User-Agent", userAgent,"Accept-Language", acceptLanguage,"X-Custom-Header", customHeader != null ? customHeader : "not present");}
}
代码解读:
@RequestHeader
的用法与@RequestParam
非常相似,同样支持value
、defaultValue
和required
属性。@RequestHeader("User-Agent")
: 直接获取名为User-Agent
的头信息。@RequestHeader(value = "X-Custom-Header", required = false)
: 获取一个自定义的、非必需的头信息。
3. 效果演示
使用curl
发送一个带自定义头的请求:
curl http://localhost:8080/users/headers \
-H "Accept-Language: zh-CN" \
-H "X-Custom-Header: my-app-v1.2.3"
响应:
{"User-Agent": "curl/7.79.1","Accept-Language": "zh-CN","X-Custom-Header": "my-app-v1.2.3"
}
三、@CookieValue
:获取Cookie值
Cookie是服务器发送到用户浏览器并保存在本地的一小块数据。它会在浏览器下次向同一服务器再次发起请求时被携带上,用于维持用户登录状态、跟踪用户行为等。@CookieValue
可以轻松读取这些Cookie。
1. 场景
假设用户登录后,服务器在客户端种下了一个名为session-id
的Cookie。后续的请求都需要携带这个Cookie,我们需要在后端读取它以识别用户。
2. 代码实战
import org.springframework.web.bind.annotation.CookieValue;
// ... 其他import@RestController
@RequestMapping("/users")
public class UserController {// ... 其他方法 ...@GetMapping("/cookies")public Map<String, String> getCookies(@CookieValue("JSESSIONID") String sessionId, // 通常由Web容器自动生成@CookieValue(value = "my-custom-cookie", required = false, defaultValue = "default-value") String customCookie) {return Map.of("JSESSIONID", sessionId,"my-custom-cookie", customCookie);}
}
代码解读:
@CookieValue
的用法也和@RequestParam
高度一致,支持value
、defaultValue
和required
。
3. 效果演示
使用curl
模拟一个带Cookie的请求:
curl http://localhost:8080/users/cookies \
--cookie "JSESSIONID=ABCDEFG12345; my-custom-cookie=hello-world"
响应:
{"JSESSIONID": "ABCDEFG12345","my-custom-cookie": "hello-world"
}
四、总结
本章我们掌握了处理HTTP请求“内容”部分的三个核心注解,它们与上一章的URL参数注解共同构成了Spring MVC参数绑定的基石。
通过这两章的学习,你已经具备了处理绝大部分Web请求参数的能力,无论是来自URL还是请求本身。
预告:Spring MVC的参数绑定功能非常强大,但如果遇到一些特殊的、非标准的请求格式,比如
"42,user,active"
这样的字符串,我们希望直接将其绑定到一个自定义对象中,该怎么做呢?下一章,我们将学习一个更高级的技巧:实现自定义参数绑定:将复杂请求灵活映射至Java对象,让你的参数处理能力更上一层楼。