Skip to content
清晨的一缕阳光
返回

Spring Boot 请求与响应处理

前言

在 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;
    }
}
@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);
}

总结

请求响应处理要点:

掌握请求响应处理,是 Web 开发的基础。


分享这篇文章到:

上一篇文章
Spring Boot RESTful API 开发
下一篇文章
Seata AT 模式实战