zoukankan      html  css  js  c++  java
  • SpringBoot 拦截器中校验Json数据

    SpringBoot 拦截器中校验Json数据

    背景

    做开发的时候,经常会使用@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将值从拿到。

    但是使用request.getParameter()是无法获取json数据的。

    从request中取出json的工具类

    使用下面的工具类,可以将request中的json数据取出:

    package cn.rayfoo.common.util.json;
    
    import com.alibaba.fastjson.JSONObject;
    
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    
    /**
     * @author rayfoo@qq.com
     * @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 = request.getMethod();
            // GET
            if (submitMehtod.equals("GET")) {
                return new String(request.getQueryString().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 = request.getContentLength();
            if(contentLength<0){
                return null;
            }
            byte buffer[] = new byte[contentLength];
            for (int i = 0; i < contentLength;) {
    
                int readlen = request.getInputStream().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 = request.getCharacterEncoding();
            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 = GetRequestJsonUtil.getRequestJsonObject(request);
           
            //获取json中具体的参数
            String name = json.getString("name");
            String password = json.getString("password");
            String phoneNumber = json.getString("phoneNumber");
            String email = json.getString("email");
            String code = json.getString("code");
           
           //校验
            .....
    }
    ...
    

    问题:此时拦截器执行后request中的数据丢失

    解决Request流自动关闭导致数据丢失

    解决办法:重写HttpServletRequestWrapper方法

    这种方法就是通过重写HttpServletRequestWrapper把request的保存下来,然后通过过滤器保存下来的request

    在填充进去,这样就可以多次读取request了

    创建下述类:

    package cn.rayfoo.common.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.com
     * @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 = StreamUtils.copyToByteArray(request.getInputStream());
            } 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 {
                    return bais.read();
                }
            };
        }
    
        /**
         * 重写 getReader()
         */
        @Override
        public BufferedReader getReader() throws IOException {
            return new BufferedReader(new InputStreamReader(getInputStream()));
        }
    }
    

    加入过滤器,包装request

    package cn.rayfoo.common.filter;
    
    import cn.rayfoo.common.util.json.MyHttpServletRequestWrapper;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    
    
    
    /**
     * @author rayfoo@qq.com
     * @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 = GetRequestJsonUtil.getRequestJsonObject(myWrapper);
    
    
    
            //获取前端参数
            String name = json.getString("name");
            String password = json.getString("password");
            String phoneNumber = json.getString("phoneNumber");
            String email = json.getString("email");
            String code = json.getString("code");
            
            //过滤参数
            ...
            }
    

    别忘了注册拦截器哦,此时,就可以正常的在拦截器中校验json数据啦~

    注:切面在拦截器之后执行,如果拦截器进行了拦截,切面将无法被执行

    本文部分内容参考了CSDN帖中的内容

  • 相关阅读:
    HDU 1800 Flying to the Mars 字典树,STL中的map ,哈希树
    字典树 HDU 1075 What Are You Talking About
    字典树 HDU 1251 统计难题
    最小生成树prim算法 POJ2031
    POJ 1287 Networking 最小生成树
    次小生成树 POJ 2728
    最短路N题Tram SPFA
    poj2236 并查集
    POJ 1611并查集
    Number Sequence
  • 原文地址:https://www.cnblogs.com/zhangruifeng/p/13449504.html
Copyright © 2011-2022 走看看