предисловие
Недавно я столкнулся с требованием в разработке, которое требует повторного чтения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
использовать
-
Создайте
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 } }
-
регистр
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 раз на протяжении всего времени жизни запроса.
Справочная статья
Причина, по которой поток в httpServletRequest можно прочитать только один раз