• 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帖中的内容

  • 相关阅读:
    win10应用 UWP 使用MD5算法
    win10应用 UWP 使用MD5算法
    用git上传代码到新浪云
    用git上传代码到新浪云
    redis三节点sentinel部署
    [HNOI/AHOI2018]转盘
    用git上传代码到新浪云
    Windows-universal-samples-master示例 XamlCommanding
    Windows-universal-samples-master示例 XamlCommanding
    Windows-universal-samples-master示例 XamlCommanding
  • 原文地址:https://www.cnblogs.com/zhangruifeng/p/13449504.html
Copyright © 2020-2023  润新知