SpringBoot中校验Json数据SpringBoot 中校验Json数据
背景
springboot和过滤器
做开发的时候,经常会使⽤@RequestBody注解,这个注解是⾮常的好⽤。但是如果你想在请求参数传到后台的
时候做⼀个参数检验,当然可以!使⽤SpringMVC的,在⾥把request的数据读取出来不就⾏
了!!,但是在使⽤了的时候会出现⼀个问题你在读取了request的数据,在Controller⾥
⾯@RequestBody注解获取Json就会失败就读取不到数据那就是RequestBody是流的形式读取的,流读
取⼀次就没有了!!
为什么使⽤RequestBody只能读取⼀遍请求数据流?
那是因为流对应的是数据,数据放在内存中,有的是部分放在内存中。read ⼀次标记⼀次当前位置(mark
position),第⼆次read就从标记位置继续读(从内存中copy)数据。 所以这就是为什么读了⼀次第⼆次是空
了。 怎么让它不为空呢?只要inputstream 中的pos 变成0就可以重写读取当前内存中的数据。javaAPI中有⼀个
⽅法public void reset() 这个⽅法就是可以重置pos为起始位置,但是不是所有的IO读取流都可以调⽤该⽅法!ServletInputStream是不能调⽤reset⽅法,这就导致了只能调⽤⼀次getInputStream()。
案例:在SpringBoot中校验json值
如果controller类中使⽤的是json值,需要在中进⾏值校验,就必须使⽤request将值从拿到。
但是使⽤Parameter()是⽆法获取json数据的。
从request中取出json的⼯具类
使⽤下⾯的⼯具类,可以将request中的json数据取出:
package cn.rayfoomon.util.json;
import com.alibaba.fastjson.JSONObject;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* @author rayfoo@qq
* @version 1.0
* <p> 将http请求中的request数据转换为json </p>
* @date 2020/8/6 19:02
*/
public class GetRequestJsonUtil {
public static JSONObject getRequestJsonObject(HttpServletRequest request) throws IOException {        String json = getRequestJsonString(request);
return JSONObject.parseObject(json);
}
/***
* 获取 request 中 json 字符串的内容
*
* @param request
* @return : <code>byte[]</code>
* @throws IOException
*/
public static String getRequestJsonString(HttpServletRequest request)
throws IOException {
String submitMehtod = Method();
// GET
if (submitMehtod.equals("GET")) {
return new QueryString().getBytes("iso-8859-1"),"utf-8").replaceAll("%22", "\"");
// POST
} else {
return getRequestPostStr(request);
}
}
/**
* 描述:获取 post 请求的 byte[] 数组
* <pre>
* 举例:
* </pre>
* @param request
* @return
* @throws IOException
*/
public static byte[] getRequestPostBytes(HttpServletRequest request)
throws IOException {
int contentLength = ContentLength();
if(contentLength<0){
return null;
}
byte buffer[] = new byte[contentLength];
for (int i = 0; i < contentLength;) {
int readlen = InputStream().read(buffer, i,
contentLength - i);
if (readlen == -1) {
break;
}
i += readlen;
}
return buffer;
}
/**
* 描述:获取 post 请求内容
* <pre>
* 举例:
* </pre>
* @param request
* @return
* @throws IOException
*/
public static String getRequestPostStr(HttpServletRequest request)
throws IOException {
byte buffer[] = getRequestPostBytes(request);
String charEncoding = CharacterEncoding();
if (charEncoding == null) {
charEncoding = "UTF-8";
}
return new String(buffer, charEncoding);
}
}
从request中取出json中的内容
.
..
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        //获取json数据
JSONObject json = RequestJsonObject(request);
//获取json中具体的参数
String name = String("name");
String password = String("password");
String phoneNumber = String("phoneNumber");
String email = String("email");
String code = String("code");
//校验
.....
}
...
问题:此时执⾏后request中的数据丢失
解决Request流⾃动关闭导致数据丢失
解决办法:重写HttpServletRequestWrapper⽅法
这种⽅法就是通过重写HttpServletRequestWrapper把request的保存下来,然后通过过滤器保存下来的request
在填充进去,这样就可以多次读取request了
创建下述类:
package cn.rayfoomon.util.json;
import org.springframework.util.StreamUtils;
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.io.InputStreamReader;
/**
* @author rayfoo@qq
* @version 1.0
* <p></p>
* @date 2020/8/6 20:49
*/
public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {    private byte[] requestBody = null;
public MyHttpServletRequestWrapper (HttpServletRequest request) {
super(request);
//缓存请求body
try {
requestBody = InputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 重写 getInputStream()
*/
@Override
public ServletInputStream getInputStream() throws IOException {
if(requestBody == null){
requestBody= new byte[0];
}
final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
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();
}
};
}
/**
* 重写 getReader()
*/
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(getInputStream()));
}
}
加⼊过滤器,包装request
package cn.rayfoomon.filter;
import cn.rayfoomon.util.json.MyHttpServletRequestWrapper;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/
**
* @author rayfoo@qq
* @version 1.0
* <p>创建⼀个实现Filter的类,重写doFilter⽅法,将ServletRequest替换为⾃定义的request类 </p>
* @date 2020/8/6 20:53
*/
@WebFilter(urlPatterns = "/*",filterName = "requestReplaced")
public class HttpServletRequestReplacedFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
ServletRequest requestWrapper = null;
if(request instanceof HttpServletRequest) {
requestWrapper = new MyHttpServletRequestWrapper((HttpServletRequest) request);
}
if(requestWrapper == null) {
chain.doFilter(request, response);
} else {
chain.doFilter(requestWrapper, response);
}
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
在启动器上加⼊下⾯的注解,扫描filter
@ServletComponentScan
重写编写
在中,使⽤myWrapper代替request
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        //封装request
MyHttpServletRequestWrapper myWrapper= new MyHttpServletRequestWrapper(request);
//获取json数据
JSONObject json = RequestJsonObject(myWrapper);
//获取前端参数
String name = String("name");
String password = String("password");
String phoneNumber = String("phoneNumber");
String email = String("email");
String code = String("code");
//过滤参数
...
}
别忘了注册哦,此时,就可以正常的在中校验json数据啦~
注:切⾯在之后执⾏,如果进⾏了拦截,切⾯将⽆法被执⾏
本⽂部分内容参考了中的内容