拦截器中,request中getReader()和getInputStream()只能调用一次,构建可重复读取inputStream的request.

来源:互联网 发布:下载青岛网络干部app 编辑:程序博客网 时间:2024/05/21 09:37

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

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

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

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;/** * @author zhangmz * @version 1.0.0 * @date 2017/09/21 */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数据包 * @author zhangmz * @version 1.0.0 * @date 2017/09/21 */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;/** * @author zhangmz * @version 1.0.0 * @date 2017/09/21 */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 配置类 * * @author zhangmz * @create 2017/6/20 */@Configurationpublic 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;    }}

step 5:

添加测试Controller类 TestController

package com.controller;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.http.MediaType;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.ResponseBody;/** * @author zhangmz * @version 1.0.0 * @date 2017/09/21 */@Controller@RequestMapping( "/test" )public class TestController {    private static final Logger logger = LoggerFactory.getLogger(TestController.class);    @ResponseBody    @RequestMapping( value = "/str", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_UTF8_VALUE )    public String queryStr(@RequestBody String json) {        logger.info("json : {}", json);        return "ok";    }}

控制台结果:

2017-09-21 15:48:30.582 DEBUG 35564 -- [nio-8080-exec-1] c.filter.RepeatedlyReadFilter   : 复制request.getInputStream2017-09-21 15:48:30.591  INFO 35564 -- [nio-8080-exec-1] com.interceptor.RepeatedlyReadInterceptor   : 请求Body: {"aa":"bb"} 2017-09-21 15:48:30.592  INFO 35564 -- [nio-8080-exec-1] com.interceptor.RepeatedlyReadInterceptor   : 请求参数, url: http://localhost:8080/test/str, method: POST, uri: /test/str, params: null 2017-09-21 15:48:30.602  INFO 35564 -- [nio-8080-exec-1] com.controller.TestController   : json : {"aa":"bb"}2017-09-21 15:48:30.609  INFO 35564 -- [nio-8080-exec-1] com.interceptor.RepeatedlyReadInterceptor   : 请求Body: {"aa":"bb"} 

参考资料:

构建可重复读取inputStream的request
ServletRequest中getReader()和getInputStream()只能调用一次的解决办法-续网友
解决request.getParameter() 、 request.getInputStream()和request.getReader()只能使用一次的问题

阅读全文
0 0