拦截器获取HttpServletRequest里body数据

1、问题

经过在拦截器中获取request中的json数据,咱们能够实现对参数进行校验和改写。问题是参数只能在拦截器里获取一次,日后在controller层就没法获取数据,提示body为空。java

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

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

 

2、解决办法

HttpServletRequestWrapper是 httpServletRequest 的包装类app

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

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 的转换post

@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数据ui

 

为何须要在 filter 里进行对 httpServletRequest 的包装转换,直接在拦截器里进行包装不行嘛?url

过滤器(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);
}
相关文章
相关标签/搜索