C语言学习网

拦截器如何获取HttpServletRequest里body数据

发表于:2022-11-27 作者:安全数据网编辑
编辑最后更新 2022年11月27日,这篇文章主要讲解了"拦截器如何获取HttpServletRequest里body数据",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"拦截器如何获取Htt

这篇文章主要讲解了"拦截器如何获取HttpServletRequest里body数据",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"拦截器如何获取HttpServletRequest里body数据"吧!

一、问题

通过在拦截器中获取request中的json数据,我们可以实现对参数进行校验和改写。问题是参数只能在拦截器里获取一次,往后在controller层就无法获取数据,提示body为空。

在网上查找资料后发现,request的输入流只能读取一次,那么这是为什么呢?

那是因为流对应的是数据,数据放在内存中,有的是部分放在内存中。read 一次标记一次当前位置(mark position),第二次read就从标记位置继续读(从内存中copy)数据。 所以这就是为什么读了一次第二次是空了。 怎么让它不为空呢?只要inputstream 中的pos 变成0就可以重写读取当前内存中的数据。javaAPI中有一个方法public void reset() 这个方法就是可以重置pos为起始位置,但是不是所有的IO读取流都可以调用该方法!ServletInputStream是不能调用reset方法,这就导致了只能调用一次getInputStream()

二、解决办法

HttpServletRequestWrapper是 httpServletRequest 的包装类

新建一个类继承HttpServletRequestWrapper实现对 httpServletRequest 的装饰,用来获取 body 数据

public class BodyReaderHttpServletRequestWrapper extends HttpServletRequestWrapper {    private final byte[] body;    private String bodyStr;    public BodyReaderHttpServletRequestWrapper(HttpServletRequest request) throws IOException {        super(request);        String bodyString = getBodyString(request);        body = bodyString.getBytes(Charset.forName("UTF-8"));        bodyStr=bodyString;    }    public String getBodyStr() {        return bodyStr;    }    @Override    public ServletInputStream getInputStream() throws IOException {        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body);        return new ServletInputStream() {            @Override            public int read() throws IOException {                return byteArrayInputStream.read();            }            @Override            public boolean isFinished() {                return false;            }            @Override            public boolean isReady() {                return false;            }            @Override            public void setReadListener(ReadListener readListener) {            }        };    }    public  String getBodyString(HttpServletRequest request) throws IOException {        StringBuilder sb = new StringBuilder();        InputStream inputStream = null;        BufferedReader reader = null;        try {            inputStream = request.getInputStream();            reader = new BufferedReader(                    new InputStreamReader(inputStream, Charset.forName("UTF-8")));            char[] bodyCharBuffer = new char[1024];            int len = 0;            while ((len = reader.read(bodyCharBuffer)) != -1) {                sb.append(new String(bodyCharBuffer, 0, len));            }        } 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();    }}

再新建一个 filter 实现对传入的 httpServletRequest 的转换

@WebFilter(filterName = "httpServletRequestWrapperFilter", urlPatterns = {"/*"})public class HttpServletRequestWrapperFilter implements Filter {    @Override    public void init(FilterConfig filterConfig) throws ServletException {    }    @Override    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)            throws IOException, ServletException {        ServletRequest requestWrapper = null;        if (request instanceof HttpServletRequest) {            HttpServletRequest httpRequest = (HttpServletRequest) request;            //遇到post方法才对request进行包装            String methodType = httpRequest.getMethod();            if ("POST".equals(methodType)) {                requestWrapper = new BodyReaderHttpServletRequestWrapper(                        (HttpServletRequest) request);            }        }        if (null == requestWrapper) {            chain.doFilter(request, response);        } else {            chain.doFilter(requestWrapper, response);        }    }    @Override    public void destroy() {    }}

最后在拦截器就可以获取request中body数据

 if(request instanceof  BodyReaderHttpServletRequestWrapper ){            System.out.println(((BodyReaderHttpServletRequestWrapper) request).getBodyStr());        }

经测试发现并不影响controller层获取body数据

为什么需要在 filter 里进行对 httpServletRequest 的包装转换,直接在拦截器里进行包装不行嘛?

过滤器(Filter)和拦截器(Interceptor)之间的最大区别就是,过滤器可以包装Request和Response,而拦截器并不能用代码描述拦截器和过滤器的流程大概就是这样的:拦截器:void run () {    Request request = new Request();    preHandle(request);    service(request);}preHandler(Request request) {    request = new RequestWrapper(request);  //在这里修改Request的引用,不会影响到service方法的request}过滤器void run () {    Request request = new Request();    doFilter(request);}doFilter(Request request) {    request = new RequestWrapper(request);  //在这里修改Request的引用,会影响到service方法的request    service(request);}

感谢各位的阅读,以上就是"拦截器如何获取HttpServletRequest里body数据"的内容了,经过本文的学习后,相信大家对拦截器如何获取HttpServletRequest里body数据这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

0