众所周知,在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注解的,那么要怎么做才能支持呢?
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) { } }; } }
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); } } }
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; } }
代码:
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修饰参数进行请求体接收的功能就实现啦。