前言
在 Web 开发中,请求参数处理和响应数据格式化是最基础也是最重要的环节。Spring Boot 提供了丰富的注解和配置,让请求响应处理变得简单高效。
请求参数处理
@RequestParam 查询参数
@RestController
@RequestMapping("/api/users")
public class UserController {
/**
* 简单查询参数
* GET /api/users/search?keyword=java
*/
@GetMapping("/search")
public List<UserDTO> search(
@RequestParam String keyword
) {
return userService.search(keyword);
}
/**
* 可选查询参数
* GET /api/users/filter?status=ACTIVE
*/
@GetMapping("/filter")
public List<UserDTO> filter(
@RequestParam(required = false) String status,
@RequestParam(required = false, defaultValue = "10") int size
) {
return userService.filter(status, size);
}
/**
* 多值查询参数
* GET /api/users/by-ids?ids=1,2,3
*/
@GetMapping("/by-ids")
public List<UserDTO> getByIds(
@RequestParam List<Long> ids
) {
return userService.findByIds(ids);
}
/**
* Map 接收参数
* GET /api/users/map?name=张三&age=25
*/
@GetMapping("/map")
public List<UserDTO> getByMap(
@RequestParam Map<String, String> params
) {
return userService.getByParams(params);
}
}
@PathVariable 路径参数
@RestController
@RequestMapping("/api/users")
public class UserController {
/**
* 单一路径参数
* GET /api/users/123
*/
@GetMapping("/{id}")
public UserDTO getUser(@PathVariable Long id) {
return userService.findById(id);
}
/**
* 多个路径参数
* GET /api/users/123/orders/456
*/
@GetMapping("/{userId}/orders/{orderId}")
public OrderDTO getOrder(
@PathVariable Long userId,
@PathVariable Long orderId
) {
return orderService.findById(orderId);
}
/**
* 路径参数绑定
* GET /api/users/123
*/
@GetMapping("/{id}")
public UserDTO getUser(@PathVariable("id") Long userId) {
return userService.findById(userId);
}
}
@RequestBody 请求体
@RestController
@RequestMapping("/api/users")
public class UserController {
/**
* 接收 JSON 对象
* POST /api/users
* Content-Type: application/json
* {"username": "张三", "email": "zhangsan@example.com"}
*/
@PostMapping
public UserDTO create(@RequestBody UserDTO dto) {
return userService.create(dto);
}
/**
* 接收 JSON 数组
* POST /api/users/batch
* [{"username": "张三"}, {"username": "李四"}]
*/
@PostMapping("/batch")
public List<UserDTO> batchCreate(@RequestBody List<UserDTO> dtos) {
return userService.batchCreate(dtos);
}
/**
* 接收 Map
* POST /api/users/map
* {"name": "张三", "age": 25}
*/
@PostMapping("/map")
public UserDTO createFromMap(@RequestBody Map<String, Object> params) {
return userService.createFromMap(params);
}
}
@ModelAttribute 表单参数
@RestController
@RequestMapping("/api/users")
public class UserController {
/**
* 接收表单数据
* POST /api/users/form
* Content-Type: application/x-www-form-urlencoded
* username=张三&email=zhangsan@example.com
*/
@PostMapping("/form")
public UserDTO createFromForm(
@ModelAttribute UserDTO dto
) {
return userService.create(dto);
}
/**
* 接收表单数据(带验证)
*/
@PostMapping("/form-valid")
public UserDTO createFromFormValid(
@Valid @ModelAttribute UserDTO dto
) {
return userService.create(dto);
}
}
@RequestHeader 请求头
@RestController
@RequestMapping("/api/users")
public class UserController {
/**
* 获取请求头
* GET /api/users/me
* Authorization: Bearer xxx
*/
@GetMapping("/me")
public UserDTO getMe(
@RequestHeader("Authorization") String authorization
) {
String token = authorization.replace("Bearer ", "");
return userService.getByToken(token);
}
/**
* 可选请求头
*/
@GetMapping("/info")
public UserInfo getInfo(
@RequestHeader(required = false) String userAgent
) {
return userService.getInfo(userAgent);
}
/**
* 获取所有请求头
*/
@GetMapping("/headers")
public Map<String, String> getHeaders(
@RequestHeaders Map<String, String> headers
) {
return headers;
}
}
@CookieValue Cookie
@RestController
@RequestMapping("/api/users")
public class UserController {
/**
* 获取 Cookie
*/
@GetMapping("/cookie")
public UserDTO getByCookie(
@CookieValue("SESSION_ID") String sessionId
) {
return userService.getBySessionId(sessionId);
}
/**
* 可选 Cookie
*/
@GetMapping("/cookie-optional")
public UserDTO getByCookieOptional(
@CookieValue(value = "SESSION_ID", required = false, defaultValue = "") String sessionId
) {
return userService.getBySessionId(sessionId);
}
}
@RequestAttribute 请求属性
@RestController
@RequestMapping("/api/users")
public class UserController {
/**
* 获取请求属性(通常在拦截器中设置)
*/
@GetMapping("/info")
public UserDTO getInfo(
@RequestAttribute("userId") Long userId
) {
return userService.findById(userId);
}
}
响应处理
响应状态码
@RestController
@RequestMapping("/api/users")
public class UserController {
/**
* 200 OK
*/
@GetMapping("/{id}")
public ResponseEntity<UserDTO> getUser(@PathVariable Long id) {
UserDTO user = userService.findById(id);
return ResponseEntity.ok(user);
}
/**
* 201 Created
*/
@PostMapping
public ResponseEntity<UserDTO> createUser(@RequestBody UserDTO dto) {
UserDTO created = userService.create(dto);
return ResponseEntity
.status(HttpStatus.CREATED)
.header("Location", "/api/users/" + created.getId())
.body(created);
}
/**
* 204 No Content
*/
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.noContent().build();
}
/**
* 302 Redirect
*/
@GetMapping("/redirect")
public ResponseEntity<Void> redirect() {
return ResponseEntity
.status(HttpStatus.FOUND)
.location(URI.create("/api/users/list"))
.build();
}
}
响应头
@RestController
@RequestMapping("/api/users")
public class UserController {
/**
* 设置响应头
*/
@GetMapping("/{id}")
public ResponseEntity<UserDTO> getUser(@PathVariable Long id) {
UserDTO user = userService.findById(id);
return ResponseEntity
.ok()
.header("X-User-Id", user.getId().toString())
.header("X-Request-Id", UUID.randomUUID().toString())
.body(user);
}
/**
* 设置缓存
*/
@GetMapping("/cache")
public ResponseEntity<List<UserDTO>> getUsers() {
List<UserDTO> users = userService.findAll();
return ResponseEntity
.ok()
.cacheControl(CacheControl.maxAge(1, TimeUnit.HOURS))
.eTag("\"users-v1\"")
.body(users);
}
/**
* 文件下载
*/
@GetMapping("/export")
public ResponseEntity<byte[]> export() {
byte[] data = userService.exportExcel();
return ResponseEntity
.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=users.xlsx")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(data);
}
}
响应格式化
@Data
public class UserDTO {
private Long id;
/**
* 日期格式化
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private LocalDateTime createdAt;
/**
* 日期格式化(只日期)
*/
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private LocalDate birthday;
/**
* 数值格式化
*/
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long salary;
/**
* 枚举序列化
*/
@JsonValue
private UserStatus status;
/**
* 字段重命名
*/
@JsonProperty("user_name")
private String username;
/**
* 忽略字段
*/
@JsonIgnore
private String password;
/**
* 条件序列化
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
private String remark;
}
public enum UserStatus {
ACTIVE("启用"),
INACTIVE("禁用");
private final String description;
UserStatus(String description) {
this.description = description;
}
@JsonValue
public String getDescription() {
return description;
}
}
消息转换器
配置 JSON 转换器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
// 添加 FastJSON 转换器
MappingFastJsonConverter converter = new MappingFastJsonConverter();
converter.setSupportedMediaTypes(List.of(
MediaType.APPLICATION_JSON
));
// 配置序列化选项
SerializeConfig serializeConfig = new SerializeConfig();
serializeConfig.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;
converter.setSerializeConfig(serializeConfig);
converters.add(0, converter);
}
}
自定义消息转换器
public class CsvMessageConverter implements HttpMessageConverter<List<UserDTO>> {
@Override
public boolean canRead(Class<?> clazz, MediaType mediaType) {
return false;
}
@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return List.class.isAssignableFrom(clazz) &&
MediaType.TEXT_PLAIN.equals(mediaType);
}
@Override
public void write(List<UserDTO> users, MediaType contentType,
HttpOutputMessage outputMessage) throws IOException {
PrintWriter writer = new PrintWriter(outputMessage.getBody());
writer.println("id,username,email");
for (UserDTO user : users) {
writer.printf("%d,%s,%s%n", user.getId(), user.getUsername(), user.getEmail());
}
writer.flush();
}
}
全局配置
配置 Jackson
spring:
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
serialization:
write-dates-as-timestamps: false
indent-output: true
deserialization:
fail-on-unknown-properties: false
default-property-inclusion: non_null
配置 MVC
@Configuration
public class WebConfig implements WebMvcConfigurer {
/**
* 配置路径匹配
*/
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer
.setUseTrailingSlashMatch(true)
.setUsePrefixPattern(false);
}
/**
* 配置内容协商
*/
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer
.favorPathExtension(false)
.favorParameter(true)
.parameterName("format")
.defaultContentType(MediaType.APPLICATION_JSON)
.mediaType("json", MediaType.APPLICATION_JSON)
.mediaType("xml", MediaType.APPLICATION_XML);
}
}
最佳实践
1. 使用 DTO 而非 Entity
// ✅ 推荐
@PostMapping
public UserDTO create(@RequestBody UserCreateDTO dto) {
return userService.create(dto);
}
// ❌ 不推荐
@PostMapping
public User create(@RequestBody User user) {
return userService.save(user);
}
2. 参数验证
@PostMapping
public UserDTO create(@Valid @RequestBody UserCreateDTO dto) {
return userService.create(dto);
}
@Data
public class UserCreateDTO {
@NotBlank(message = "用户名不能为空")
@Size(min = 2, max = 20, message = "用户名长度 2-20")
private String username;
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
private String email;
@NotNull(message = "年龄不能为空")
@Min(value = 1, message = "年龄最小为 1")
@Max(value = 150, message = "年龄最大为 150")
private Integer age;
}
3. 使用 Record(JDK 16+)
// JDK 21 Record
public record UserDTO(
Long id,
String username,
String email,
LocalDateTime createdAt
) {}
// 使用
@GetMapping("/{id}")
public UserDTO getUser(@PathVariable Long id) {
return userService.findById(id);
}
总结
请求响应处理要点:
- ✅ @RequestParam - 查询参数
- ✅ @PathVariable - 路径参数
- ✅ @RequestBody - 请求体
- ✅ @RequestHeader - 请求头
- ✅ @CookieValue - Cookie
- ✅ ResponseEntity - 响应状态码和头
- ✅ JSON 格式化 - 日期、数值、枚举
- ✅ 消息转换器 - 自定义序列化
掌握请求响应处理,是 Web 开发的基础。