zoukankan      html  css  js  c++  java
  • 拦截器中,request中getReader()和getInputStream()只能调用一次,构建可重复读取inputStream的request.

    由于 request中getReader()和getInputStream()只能调用一次

    在项目中,可能会出现需要针对接口参数进行校验等问题。

    因此,针对这问题,给出一下解决方案

    实现方法:先将RequestBody保存为一个byte数组,然后通过Servlet自带的HttpServletRequestWrapper类覆盖getReader()和getInputStream()方法,使流从保存的byte数组读取。然后再Filter中将ServletRequest替换为ServletRequestWrapper。代码如下: 

    BodyReaderHttpServletRequestWrapper类包装ServletRequest,将流保存为byte[],然后将getReader()和getInputStream()方法的流的读取指向byte[] 

    http://zhangbo-peipei-163-com.iteye.com/blog/2022460

    step 1:

    添加RepeatedlyReadRequestWrapper 类并继承 HttpServletRequestWrapper 包装类

    package com.config;
    
    import org.apache.commons.lang3.StringUtils;
    
    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;
    import java.nio.charset.Charset;
    
    public class RepeatedlyReadRequestWrapper extends HttpServletRequestWrapper {
        private final byte[] body;
    
        public RepeatedlyReadRequestWrapper(HttpServletRequest request)
                throws IOException {
            super(request);
            body = readBytes(request.getReader(), "utf-8");
        }
    
        @Override
        public BufferedReader getReader() throws IOException {
            return new BufferedReader(new InputStreamReader(getInputStream()));
        }
    
        @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 listener) {
    
                }
    
                @Override
                public int read() throws IOException {
                return bais.read();
                }
            };
        }
    
        /**
         * 通过BufferedReader和字符编码集转换成byte数组
         * @param br
         * @param encoding
         * @return
         * @throws IOException
         */
        private byte[] readBytes(BufferedReader br,String encoding) throws IOException{
            String str = null,retStr="";
            while ((str = br.readLine()) != null) {
                retStr += str;
            }
            if (StringUtils.isNotBlank(retStr)) {
                return retStr.getBytes(Charset.forName(encoding));
            }
            return null;
        }
    }

    step 2:

    添加 RepeatedlyReadFilter 实现 filter 过滤器接口方法,当客户端的请求先 过滤 进入SpringMvc Dispatch 路由前先包装下

    package com.filter;
    
    import com.config.RepeatedlyReadRequestWrapper;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import java.io.IOException;
    
    /**
     * 复制请求数据包body
     * 以提供 拦截器下 可数次获取Body数据包*/
    public class RepeatedlyReadFilter implements Filter {
    
        private static final Logger logger = LoggerFactory.getLogger(RepeatedlyReadFilter.class);
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            logger.debug("复制request.getInputStream流");
            ServletRequest requestWrapper = null;
            if (request instanceof HttpServletRequest) {
                requestWrapper = new RepeatedlyReadRequestWrapper((HttpServletRequest) request);
            }
            if (null == requestWrapper) {
                chain.doFilter(request, response);
            } else {
                chain.doFilter(requestWrapper, response);
            }
        }
    
        @Override
        public void destroy() {
    
        }
    }

    step 3:

    添加拦截器 RepeatedlyReadInterceptor 继承 HandlerInterceptorAdapter 拦截适配器

    package com.interceptor;
    
    import com.config.RepeatedlyReadRequestWrapper;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
    
    import javax.servlet.ServletInputStream;
    import javax.servlet.ServletRequest;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.*;
    import java.nio.charset.Charset;
    
    public class RepeatedlyReadInterceptor extends HandlerInterceptorAdapter {
    
        private static final Logger logger = LoggerFactory.getLogger(RepeatedlyReadInterceptor.class);
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            /**
             * 对来自后台的请求统一进行日志处理
             */
            RepeatedlyReadRequestWrapper requestWrapper;
            if (request instanceof RepeatedlyReadRequestWrapper) {
                // 签名处理过程 start.... 
                requestWrapper = (RepeatedlyReadRequestWrapper) request;
                logger.info("请求Body: {} ", getBodyString(requestWrapper));
                // 签名处理过程 end....
            }
            // 默认记录后台接口请求日志记录
            String url = request.getRequestURL().toString();
            String method = request.getMethod();
            String uri = request.getRequestURI();
            String queryString = request.getQueryString();
            logger.info(String.format("请求参数, url: %s, method: %s, uri: %s, params: %s ", url, method, uri, queryString));
            return super.preHandle(request, response, handler);
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            RepeatedlyReadRequestWrapper requestWrapper;
            if (request instanceof RepeatedlyReadRequestWrapper) {
                // 测试再次获取Body start.... 
                requestWrapper = (RepeatedlyReadRequestWrapper) request;
                logger.info("请求Body: {} ", getBodyString(requestWrapper));
                // 测试再次获取Body end....
            }
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    
        }
    
        /**
         * 获取请求Body
         *
         * @param request
         *
         * @return
         */
        public static String getBodyString(final ServletRequest request) {
            StringBuilder sb = new StringBuilder();
            InputStream inputStream = null;
            BufferedReader reader = null;
            try {
                inputStream = cloneInputStream(request.getInputStream());
                reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
                String line = "";
                while ((line = reader.readLine()) != null) {
                sb.append(line);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                }
                if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                }
            }
            return sb.toString();
        }
    
        /**
         * Description: 复制输入流</br>
         *
         * @param inputStream
         *
         * @return</br>
         */
        public static InputStream cloneInputStream(ServletInputStream inputStream) {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len;
            try {
                while ((len = inputStream.read(buffer)) > -1) {
                byteArrayOutputStream.write(buffer, 0, len);
                }
                byteArrayOutputStream.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
            InputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            return byteArrayInputStream;
        }
    }

    step 4:

    配置过滤器与拦截器 WebMvcConfig

    package com.config;
    
    import com.filter.RepeatedlyReadFilter;
    import com.interceptor.MyInterceptor;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.*;
    
    /**
     * SpringMVC 配置类*/
    @Configuration
    public class WebMvcConfig extends WebMvcConfigurerAdapter {
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new RepeatedlyReadInterceptor()).addPathPatterns("/**");
            super.addInterceptors(registry);
        }
    
        @Bean
        public FilterRegistrationBean repeatedlyReadFilter() {
            FilterRegistrationBean registration = new FilterRegistrationBean();
            RepeatedlyReadFilter repeatedlyReadFilter = new RepeatedlyReadFilter();
            registration.setFilter(repeatedlyReadFilter);
            registration.addUrlPatterns("/*");
            return registration;
        }
    }
  • 相关阅读:
    Animate.css 一款强大的预设css3动画库
    关于js返回上一页的实现方法
    jquery判断字符串中是否包含特定字符的方法总结
    去掉select在苹果手机上的原生样式
    html5中如何去掉input type date默认样式
    JS和jQuery中ul li遍历获取对应的下角标
    滚动一定的高度底色递增
    喵哈哈村的狼人杀大战(5)
    喵哈哈村的狼人杀大战(2)
    One Card Poker
  • 原文地址:https://www.cnblogs.com/alter888/p/8919865.html
Copyright © 2011-2022 走看看