侧边栏壁纸
博主头像
落叶人生博主等级

走进秋风,寻找秋天的落叶

  • 累计撰写 130562 篇文章
  • 累计创建 28 个标签
  • 累计收到 9 条评论
标签搜索

目 录CONTENT

文章目录

HttpServletRequest & HttpServletResponse 中 Body 的获取

2023-12-08 星期五 / 0 评论 / 0 点赞 / 22 阅读 / 7285 字

获取 HttpServletRequest 中的请求体 HttpServletRequest#getInputStream() 获取到请求的输入流,从该输入流中可以读取到请求体。不过这个流在被我们的代

获取 HttpServletRequest 中的请求体

    HttpServletRequest#getInputStream() 获取到请求的输入流,从该输入流中可以读取到请求体。不过这个流在被我们的代码 read 过后,之后的代码就会报错,因为流已经被我们读取过了 , 尝试使用 mark() , reset() 也是不行的,会抛出异常。可以通过将 HttpServletRequest 对象包装一层的方式来实现这个功能。

package org.hepeng.commons.http;import lombok.AllArgsConstructor;import lombok.Data;import org.apache.commons.io.IOUtils;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.InputStream;import java.io.InputStreamReader;/** *  * @author he peng * @date 2018/9/11 */public class BodyCachingHttpServletRequestWrapper extends HttpServletRequestWrapper {    private byte[] body;    private ServletInputStreamWrapper inputStreamWrapper;    public BodyCachingHttpServletRequestWrapper(HttpServletRequest request) throws IOException {        super(request);        this.body = IOUtils.toByteArray(request.getInputStream());        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.body);        this.inputStreamWrapper = new ServletInputStreamWrapper(byteArrayInputStream);        resetInputStream();    }    private void resetInputStream() {        this.inputStreamWrapper.setInputStream(new ByteArrayInputStream(this.body != null ? this.body : new byte[0]));    }    public byte[] getBody() {        return body;    }    @Override    public ServletInputStream getInputStream() throws IOException {        return this.inputStreamWrapper;    }    @Override    public BufferedReader getReader() throws IOException {        return new BufferedReader(new InputStreamReader(this.inputStreamWrapper));    }    @Data    @AllArgsConstructor    private static class ServletInputStreamWrapper extends ServletInputStream {        private InputStream inputStream;        @Override        public boolean isFinished() {            return true;        }        @Override        public boolean isReady() {            return false;        }        @Override        public void setReadListener(ReadListener readListener) {        }        @Override        public int read() throws IOException {            return this.inputStream.read();        }    }}

 

获取 HttpServletResponse 中的响应体

    通过使用 ByteArrayOutputStream 将原 HttpSevletResponse 进行一层包装就可以实现。ByteArrayOutputStream 是将数据写入到它内部的缓冲区中,这样我们就可以获取到这个数据了。

package org.hepeng.commons.http;import lombok.AllArgsConstructor;import lombok.Data;import javax.servlet.ServletOutputStream;import javax.servlet.WriteListener;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpServletResponseWrapper;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.OutputStreamWriter;import java.io.PrintWriter;/** * @author he peng * @date 2018/10/1 */public class BodyCachingHttpServletResponseWrapper extends HttpServletResponseWrapper {    private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();    private HttpServletResponse response;    public BodyCachingHttpServletResponseWrapper(HttpServletResponse response) {        super(response);        this.response = response;    }    public byte[] getBody() {        return byteArrayOutputStream.toByteArray();    }    @Override    public ServletOutputStream getOutputStream() {        return new ServletOutputStreamWrapper(this.byteArrayOutputStream , this.response);    }    @Override    public PrintWriter getWriter() throws IOException {        return new PrintWriter(new OutputStreamWriter(this.byteArrayOutputStream , this.response.getCharacterEncoding()));    }    @Data    @AllArgsConstructor    private static class ServletOutputStreamWrapper extends ServletOutputStream {        private ByteArrayOutputStream outputStream;        private HttpServletResponse response;        @Override        public boolean isReady() {            return true;        }        @Override        public void setWriteListener(WriteListener listener) {        }        @Override        public void write(int b) throws IOException {            this.outputStream.write(b);        }        @Override        public void flush() throws IOException {            if (! this.response.isCommitted()) {                byte[] body = this.outputStream.toByteArray();                ServletOutputStream outputStream = this.response.getOutputStream();                outputStream.write(body);                outputStream.flush();            }        }    }}

flush() 函数是必须提供的 ,否则流中的数据无法响应到客户端 , ByteArrayOutputStream 没有实现 flush() 。像 SpringMVC 这类框架会去调用这个响应输出流中的 flush() 函数 ,而且有可能在出现多次调用的情况,多次调用会产生问题使得客户端得到错误的数据,比如这样的 :

{"errorCode":30001,"errorMsg":"用户未认证","token":null,"entity":null}{"errorCode":30001,"errorMsg":"用户未认证","token":null,"entity":null}  ,出现这种情况就说明 flush() 被调用了两次。所以需要在这里判断一下 HttpServletResponse#isCommitted()  。

获取请求体、相应体的包装类在 Filter 中的使用

package org.hepeng.commons.http.filter;import com.tepin.commons.http.BodyCachingHttpServletRequestWrapper;import com.tepin.commons.http.BodyCachingHttpServletResponseWrapper;import javax.servlet.Filter;import javax.servlet.FilterChain;import javax.servlet.FilterConfig;import javax.servlet.ServletException;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/** * @author he peng * @date 2018/10/2 */public class DemoFilter implements Filter {    @Override    public void init(FilterConfig filterConfig) throws ServletException {    }    @Override    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {        BodyCachingHttpServletRequestWrapper requestWrapper =                new BodyCachingHttpServletRequestWrapper((HttpServletRequest) request);        byte[] requestBody = requestWrapper.getBody();        // TODO do something        BodyCachingHttpServletResponseWrapper responseWrapper =                new BodyCachingHttpServletResponseWrapper((HttpServletResponse) response);        chain.doFilter(requestWrapper , responseWrapper);        byte[] responseBody = responseWrapper.getBody();        // TODO do something    }    @Override    public void destroy() {    }}

 

广告 广告

评论区