阅读 1512

HttpServletRequest 重复读取及设置自定义header

前言

最近在开发中遇到了一个需求,需要重复读取HttpServletRequest请求内容及设置自定义header向下传递,但是会出现Required request body is missing ,原因是ServletInputStream的流只能读取一次,那么我们只需要让请求内容可以重复读取即可,可以通过HttpServletRequestWrapper来实现。

实现步骤

1.自定义MutableHttpServletRequest
public class MutableHttpServletRequest extends HttpServletRequestWrapper {
    private final String body;
    private byte[] bytes;
    private final Map<String, String> customHeaders;

    public MutableHttpServletRequest(HttpServletRequest request) throws IOException {
        super(request);
        this.customHeaders = new HashMap<String, String>();
        this.bytes = StreamUtils.copyToByteArray(request.getInputStream());
        this.body = new String(this.bytes, StandardCharsets.UTF_8);
    }

    public void putHeader(String name, String value) {
        this.customHeaders.put(name, value);
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream(), StandardCharsets.UTF_8));
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (bytes == null) {
            bytes = new byte[0];
        }
        final ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        return new ServletInputStream() {
            @Override
            public boolean isFinished() {
                return false;
            }

            @Override
            public boolean isReady() {
                return false;
            }

            @Override
            public void setReadListener(ReadListener readListener) {

            }

            @Override
            public int read() throws IOException {
                return bais.read();
            }
        };
    }

    @Override
    public String getHeader(String name) {
        String headerValue = customHeaders.get(name);

        if (headerValue != null) {
            return headerValue;
        }
        return ((HttpServletRequest) getRequest()).getHeader(name);
    }

    @Override
    public Enumeration<String> getHeaderNames() {
        Set<String> set = new HashSet<String>(customHeaders.keySet());

        @SuppressWarnings("unchecked")
        Enumeration<String> e = ((HttpServletRequest) getRequest()).getHeaderNames();
        while (e.hasMoreElements()) {
            String n = e.nextElement();
            set.add(n);
        }
        return Collections.enumeration(set);
    }
}
复制代码
2. MutableHttpServletRequest 使用
  1. 创建filter

    public class ReadRequestBodyFilter implements Filter {
        
        @Override
        public void init(FilterConfig filterConfiguration) throws ServletException {
            // do nothing
        }
        
      	@Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)throws IOException, ServletException {
            request = new MutableHttpServletRequest((HttpServletRequest) request);
            // do nothing
            chain.doFilter(request, response);
        }
       
        @Override
        public void destroy() {
            // do nothing
        }
    }
    复制代码
  2. 注册filtel

    @Bean
    public FilterRegistrationBean<BodyReaderFilter> Filters() {
        FilterRegistrationBean<ReadRequestBodyFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new ReadRequestBodyFilter());
        registrationBean.addUrlPatterns("/*");
        registrationBean.setName("readRequestBody-filter");
        registrationBean.setOrder(1);
        return registrationBean;
    }
    复制代码

总结

http请求内容只能被读取一次,如果您在过滤器中读取body,则目标servlet将无法重新读取它,这也会导致IllegalStateException。 使用上面给定的HttpServletRequestWrapper,就可以读取HTTP请求正文,然后Servlet以后仍可以读取它。 本质上,请求主体内容被缓存在包装对象中,因此在整个请求生命周期中,它可以是N次。

参考文章

HttpServletRequestWrapper

httpServletRequest中的流只能读取一次的原因

read httpservletrequest twice

文章分类
后端
文章标签