HttpServletRequest неоднократно считывает и устанавливает пользовательские заголовки

Java

предисловие

Недавно я столкнулся с требованием в разработке, которое требует повторного чтенияHttpServletRequestЗапросите контент и установите пользовательские заголовки для передачи, но они появятся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-запроса, а затем сервлет сможет прочитать его позже. По сути, содержимое тела запроса кэшируется в объекте-оболочке, поэтому это может быть N раз на протяжении всего времени жизни запроса.

Справочная статья

HttpServletRequestWrapper

Причина, по которой поток в httpServletRequest можно прочитать только один раз

read httpservletrequest twice