Spring Boot框架中Controller层API接口如何支持使用多个@RequestBody注解接受请求体参数

本文介绍了如何在SpringBoot框架中处理一个Controller层API接口,当请求体包含多个参数时,如何通过增加HttpServletRequest适配器、定义请求过滤器并配置它们来支持多个@RequestBody注解的接收。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前言

众所周知,在Spring Boot框架中,Controller层API接口编码获取请求体参数时,在参数上会使用@RequestBody注解;如果一次请求中,请求体参数携带的内容需要用多个参数接收时,能不能多次使用@RequestBody注解呢?

下面我们先测试一下,参考代码:

package com.learn.springboot.controller;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RequestBodyController {

    @Getter
    @Setter
    @ToString
    static class User {
        private Integer age;
        private String name;
    }

    @Getter
    @Setter
    @ToString
    static class Home {
        private String addr;
        private String phone;
    }

    @Getter
    @Setter
    @ToString
    static class Car {
        private String number;
    }

    @RequestMapping("/req/test")
    public String test(@RequestBody User user, @RequestBody Home home, @RequestBody Car car) {
        return new StringBuilder(user.toString()).append("--").append(home.toString()).append("--").append(car.toString()).toString();
    }
}

PostMan进行请求:
在这里插入图片描述

服务端后端日志:
在这里插入图片描述

由上面的测试代码可以看出,Spring Boot框架原生是不支持多个参数使用@RequestBody注解的,那么要怎么做才能支持呢?

二、Spring Boot支持多个@RequestBody注解接收参数

1. 增加HttpServletRequest对象输入流获取参数逻辑适配器
import cn.hutool.core.io.IoUtil;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;

public class MultiRequestBodyWrapper extends HttpServletRequestWrapper {

    private final byte[] paramBody;

    public MultiRequestBodyWrapper(HttpServletRequest request, ServletResponse response) throws IOException {
        super(request);
        request.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());

        paramBody = IoUtil.readBytes(request.getInputStream(), false);
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(paramBody);
        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return bais.read();
            }

            @Override
            public int available() throws IOException {
                return paramBody.length;
            }

            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }
        };
    }
}

2. 定义请求过滤器,使定义的适配器生效

import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class MultiRequestBodyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        ServletRequest requestWrapper = null;
        // 拦截请求格式是JSON格式的报文
        if (request instanceof HttpServletRequest
                && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) {
            requestWrapper = new MultiRequestBodyWrapper((HttpServletRequest) request, response);
        }
        if (null == requestWrapper) {
            chain.doFilter(request, response);
        } else {
            chain.doFilter(requestWrapper, response);
        }
    }
}

3. 配置过滤器,使自定义Filter生效

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MultiRequestBodyFilterConfig {

    @Bean
    public FilterRegistrationBean multiRequestBodyFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setFilter(new MultiRequestBodyFilter());
        registration.addUrlPatterns("/req/*");
        registration.setName("multiRequestBodyFilter");
        return registration;
    }

}

4. 重启应用,功能测试

代码:

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class RequestBodyController {

    @Getter
    @Setter
    @ToString
    static class User {
        private Integer age;
        private String name;
    }

    @Getter
    @Setter
    @ToString
    static class Home {
        private String addr;
        private String phone;
    }

    @Getter
    @Setter
    @ToString
    static class Car {
        private String number;
    }

    @RequestMapping("/req/test")
    public String test(@RequestBody User user, @RequestBody Home home, @RequestBody Car car) {
        return new StringBuilder(user.toString()).append("--").append(home.toString()).append("--").append(car.toString()).toString();
    }
}

PostMan请求:
在这里插入图片描述

通过测试结果可以看到,此时多个@RequestBody修饰参数进行请求体接收的功能就实现啦。

<think>好的,用户现在问的是“使用@RequestBody注解自动绑定JSON”,我需要详细解释这个注解Spring框架中的作用和用法。用户之前已经询问过后端如何处理JSON数据,现在可能是在具体实现中遇到了问题,或者想深入理解Spring中的相关注解。 首先,我需要确认用户对Spring框架的基础了解程度。可能用户正在学习Spring Boot,或者在实际开发中遇到了数据绑定问题。用户可能想知道@RequestBody如何将前端传来的JSON自动转换为Java对象,这样他们就不用手动解析JSON了。 接下来,我需要解释@RequestBody的基本功能,比如它如何将HTTP请求体中的JSON数据映射到方法的参数对象上。要提到常见的用例,比如在Controller的方法参数使用注解,并绑定到DTO或实体类。 还要注意对比其他方法,比如不使用@RequestBody时可能需要手动解析请求体,这样代码会更繁琐。同时,需要提到相关的依赖,比如Spring Boot默认使用Jackson库来处理JSON的序列化和反序列化,所以用户需要确保项目中包含相应的依赖。 可能用户会遇到常见的问题,比如400错误,这时候需要说明可能的原因,比如JSON结构不匹配、字段类型错误等。要给出解决方案,比如检查JSON格式、使用@Valid进行验证等。 另外,用户可能需要知道如何验证数据,所以需要引入@Valid注解和Bean Validation的结合使用,比如@NotNull、@Size等注解使用示例。 还要补充一些高级用法,比如处理嵌套对象、日期格式等,这些是实际开发中常见的情况。同时,提醒用户注意字段名称的匹配,以及如何自定义映射关系,比如使用@JsonProperty。 最后,确保回答结构清晰,步骤明确,示例代码正确,帮助用户一步步理解和应用@RequestBody注解。可能用户需要示例代码来参考,所以提供Spring BootController示例,并展示请求和响应的JSON结构。</think>--- ### **一、@RequestBody 注解核心作用** **核心功能**:将 HTTP 请求体中的 JSON 数据**自动映射**到 Java 对象,开发者无需手动解析 JSON 字符串。 --- ### **二、使用场景与基本用法** #### **1. 典型应用场景** - 接收前端 POST/PUT 请求的 JSON 数据 - 处理 RESTful API请求体 - 替代传统 `HttpServletRequest` 手动解析方式 #### **2. 基础代码示例** ```java @RestController public class UserController { @PostMapping("/users") // 重点注解 ↓ public ResponseEntity<User> createUser(@RequestBody UserDTO userDTO) { User savedUser = userService.save(userDTO); return ResponseEntity.ok(savedUser); } } // DTO 类定义 public class UserDTO { private String username; private String email; private Integer age; // 必须包含 getter/setter } ``` --- ### **三、底实现原理** ```mermaid flowchart TD A[HTTP请求到达] --> B[DispatcherServlet] B --> C[查找HandlerMapping] C --> D[调用HandlerAdapter] D --> E[使用HttpMessageConverter] E -->|Jackson| F[JSON转Java对象] F --> G[注入@RequestBody参数] ``` 1. **关键组件**:`MappingJackson2HttpMessageConverter` 2. **依赖库**:Spring Boot 默认集成 **Jackson** 库 3. **必要依赖**(Maven): ```xml <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> ``` --- ### **四、数据绑定规则** | JSON 特征 | Java 对应规则 | 示例说明 | |---------------------|-----------------------------------|----------------------------| | 字段名称 | 严格匹配对象属性名 | `{"age": 25}` → `private Integer age` | | 嵌套对象 | 支持多级对象嵌套 | `{"address": {"city": "北京"}}` → `Address address` 属性 | | 数组/集合 | 自动转换为 List/Set | `{"tags": ["A","B"]}` → `private List<String> tags` | | 日期格式 | 需配合 `@JsonFormat` 注解 | `@JsonFormat(pattern = "yyyy-MM-dd")` | --- ### **五、数据验证最佳实践** **推荐组合使用**:`@RequestBody` + `@Valid` ```java @PostMapping("/users") public ResponseEntity<?> createUser( @Valid @RequestBody UserDTO userDTO, // 重点组合 BindingResult result) { if (result.hasErrors()) { // 处理验证错误 } // ...业务逻辑 } // DTO 添加验证注解 public class UserDTO { @NotBlank(message = "用户名不能为空") @Size(min=3, max=20) private String username; @Email(message = "邮箱格式无效") private String email; } ``` --- ### **六、常见问题与解决方案** #### **问题1:400 Bad Request 错误** **可能原因**: 1. JSON 字段与 Java 属性名称不匹配 2. 缺少无参构造函数 3. 数据类型不兼容(如 JSON 字符串映射到 Integer 字段) **调试方法**: ```java // 开启详细错误日志 @RestControllerAdvice public class ExceptionHandler { @ExceptionHandler(HttpMessageNotReadableException.class) public void handleException(HttpMessageNotReadableException ex) { ex.printStackTrace(); // 查看具体解析错误 } } ``` #### **问题2:日期格式处理** **解决方案**: ```java public class OrderDTO { @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime createTime; } ``` --- ### **七、高级用法示例** #### **1. 接收动态JSON(无固定结构)** ```java @PostMapping("/dynamic") public void handleDynamicData(@RequestBody Map<String, Object> payload) { // 自由处理任意结构数据 String value = (String) payload.get("customField"); } ``` #### **2. 自定义字段映射** ```java public class ProductDTO { @JsonProperty("product_name") // 映射JSON字段名 private String productName; @JsonIgnore // 忽略字段 private String internalCode; } ``` --- ### **八、性能优化建议** 1. **避免大对象嵌套**:超过 5 嵌套会显著降低解析速度 2. **禁用未知属性**:防止恶意攻击 ```java @JsonIgnoreProperties(ignoreUnknown = true) public class MyDTO { /*...*/ } ``` 3. **批量处理优化**: ```java @PostMapping("/batch") public void batchCreate(@RequestBody List<UserDTO> users) { // 使用批量插入SQL优化性能 } ``` --- 通过合理使用 `@RequestBody` 注解,开发者可以显著提升后端接口开发效率。实际使用时需注意与验证机制、异常处理等组件的配合,同时根据业务需求选择合适的数据绑定策略。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Coder-文小白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值