springboot中多个请求参数(@RequestBody)加数据校验
(BIndingR。。。
在⽇常的开发中遇到了⼀种问题 - 没办法对Controller注⼊多个参数,每个Controller的RequestMap⽅法参数只能注⼊⼀个的RequestBody。但是⼜经常使⽤多个参数,通常我们的做法是将那⼏个DTO重新组合成⼀个新的DTO,最常见的例⼦就是我们在请求分页数据的时候,每次要嘛将请求参数和分页参数组合成⼀个新的DTO,要嘛请求参数的DTO继承分页参数的DTO,这样使我们没办法从⽅法上直观的看到这个请求接⼝的参数性质。我再⽹上到了⼀种多参数注⼊的⽅式,是⼀个⼤神写的,他⾃⼰写了⼀个注解
(@MultiRequestBody),应该会有⼈看过这篇⽂章,我顺便也把链接附上,我在这边再次膜拜⼤神,这篇⽂章初期给了我很⼤的帮助,感谢。
⼈总是不满⾜的,因为在业务当中要加很多参数的判空处理,所以我就想着有没有什么⼯具可以⾃动帮我们做参数的校验,所以就到了BindingRequest,配合@Valid使⽤⼗分好⽤。但是⼤神的@MultiRequestBody没办法配合@Valid、BindingRequest使⽤,所以就很蛋疼。抱着我⼀定要⽤上可以⾃动参数校验的多个RequestBody注⼊的想法,便开始趟雷了。
趟雷的时候发现报的异常我很熟悉,异常叫什么我忘记了,但是记得内容是InputStream()⾥⾯没有值了,所以RequestBody 拿不到值(异常引起的原因是InputStream()⾥⾯的值只能取⼀次,顺便说⼀下Reader()⾥⾯的值也只能取⼀次,可能是Reader()使⽤了InputStream())。也很幸运,因为之前遇到过这个异常,感觉会不会是第⼆个RequestBody从request⾥⾯去取值的时候因为InputStream()的值被第⼀个拿⾛了,所以第⼆个RequestBody拿不到值,所以就⽹上了⼀个⼯具,先将InputStream()拿出来,然后再把它放到⼀个新的request⾥⾯,我们去使⽤这个新的request,不就解决了这个问题了。
现在就开始把这些骚操作串起来。
1、加⼊依赖
<!--@valid -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
2、加⼊重置request⾥⾯的值的⼯具
附上⼤神的⽂章链接
⼤神的代码中我们只需要⾥⾯将InputStream()重置这部分。
(1)写好request重置的⼯具
import org.apachemons.io.IOUtils;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
/** 将request中的getinputstream取出来再放到⼀个新的request中
* @author Chen KaiYang
* @date 2019/8/12 9:01
*/
public class ReaderReuseHttpServletRequestWrapper extends HttpServletRequestWrapper {
//取出来的值就放在这
private final byte[] body;
public ReaderReuseHttpServletRequestWrapper(HttpServletRequest request) throws IOException { super(request);
this.body = Reader()).getBytes(Charset.forName("UTF-8"));
}
@Override
public BufferedReader getReader() throws IOException {
Reader();
}
@Override
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
return new ServletInputStream() {
@Override
public boolean isFinished() {
return false;
}
@Override
public boolean isReady() {
return false;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
ad();
}
};
}
}
(2)使⽤新的request顶替旧的request
import cn.fig.ReaderReuseHttpServletRequestWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 权限认证过滤器<br>
springboot aop* 执⾏接⼝访问权限控制。需与控制层的AOP进⾏联动
* 跨越处理
*/
@Component
@ServletComponentScan
@WebFilter(urlPatterns = "/*",filterName = "authorFilter")
public class AuthorFilter implements Filter {
private Logger logger = Class());
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = ((HttpServletResponse) servletResponse);
String uri = RequestURI().ContextPath(), "");
ServletContext context = Session().getServletContext();
ServletRequest requestWrapper = null;
if(request instanceof HttpServletRequest){
requestWrapper = new ReaderReuseHttpServletRequestWrapper(request);
}
//设置跨域处理
response.setHeader("Access-Control-Allow-Origin", Header("Origin"));
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "0");
response.setHeader("Access-Control-Allow-Headers",
"Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token,Acce response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("XDomainRequestAllowed", "1");
/// 过滤器的控制实现代码
/// 过滤结果可以写⼊到request中,在控制层的AOP进⾏处理
//使⽤新的request顶替旧的request
filterChain.doFilter(requestWrapper, response);
}
@Override
public void destroy() {
}
}
我⾥⾯的代码可以⾃⼰增删,最重要的代码为
ServletRequest requestWrapper = null;
if(request instanceof HttpServletRequest){
requestWrapper = new ReaderReuseHttpServletRequestWrapper(request); }
//使⽤新的request顶替旧的request
filterChain.doFilter(requestWrapper, response);
3、操作演⽰
public class BaseDTO<T> implements Serializable {
private Integer code;
private String msg;
private T result;
//.....setget⾃⼰补全
}
public class PageDTO extends BaseDTO {
@Min(value = 1,message = "请求页数不能⼩于1")
/**
* 第⼏页
*/
private int pageno;
@Min(value = 0,message = "每页请求不能⼩于0")
@Max(value = 100,message = "每页请求不能超过100")
/**
* 每页个数
*/
private int pagesize;
/**
* 总数
*/
private Long total;
//.....setget⾃⼰补全
}
public class SysuserDTO {
/**
* ⾝份证号码
*/
@NotBlank(message = "⾝份证号码不能为空")
private String idnumber;
/**
*
*/
@NotBlank(message = "⼿机号码不能为空")
private String tel;
/
/。。。。。set get ⾃⼰补全
}
public class SysuserDTO2 extends PageDTO {
/**
* ⾝份证号码
*/
private String idnumber;
/**
*
*/
private String tel;
/
/。。。。。set get ⾃⼰补全
}
(1)旧的操作
@RestController
@RequestMapping("/user")
public class SysuserController {
//这边services我就不写了
@AutoWired
UserServices userServices;
/**
* 假设是根据⽤户的⾝份证和电话号码来搜索满⾜条件的⽤户列表
*/
@PostMapping("/userList")
public BaseDTO userList(@RequestBody SysUserDTO2 dto){
//StringUntil.isEmpty()⾃⼰封装的⼀个判空⽅法
if(StringUntil.Idnumber())){
//ReturnUtil ⾃⼰封装的⼀个统⼀返回信息格式
//ResponseEnum.RESPONSE_Code()这是返回的状态码⾃⼰定义的
<(ResponseEnum.RESPONSE_Code(),"⾝份证号码不能为空");
}
if(StringUntil.Tel())){
(ResponseEnum.RESPONSE_Code(),"⼿机号码不能为空"); }
//现在看起来好像不怎么繁琐但是如果你有⼗⼏个字段需要判断呢岂不是累死⽽且代码可读性极差
return userServices.userList(dto);
}
}
(2)骚操作
单个参数的我就不贴了,只贴多个参数。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论