Запрос HTTP-сервлета теряет параметры из тела POST после его однократного чтения

Я пытаюсь получить доступ к двум параметрам HTTP-запроса в фильтре сервлета Java, здесь ничего нового, но с удивлением обнаружил, что параметры уже были использованы! Из-за этого он больше не доступен в цепочке фильтров.

Кажется, что это происходит только тогда, когда параметры входят в тело запроса POST (например, отправка формы).

Есть ли способ прочитать параметры и НЕ использовать их?

Пока я нашел только эту ссылку: Фильтр сервлетов, использующий request.getParameter, теряет данные формы.

Спасибо!


person amuniz    schedule 18.04.2012    source источник
comment
может показать фрагмент кода, как вы это делаете?   -  person Pavel Veller    schedule 18.04.2012
comment
Получили ли вы getInputStream () или getReader ()? Похоже, именно они будут мешать выполнению getParameter ()   -  person evanwong    schedule 18.04.2012


Ответы (13)


В качестве альтернативы, альтернативный способ решения этой проблемы - не использовать цепочку фильтров и вместо этого создать свой собственный компонент перехватчика, возможно, используя аспекты, которые могут работать с анализируемым телом запроса. Это также, вероятно, будет более эффективным, поскольку вы только один раз преобразуете запрос InputStream в свой собственный объект модели.

Однако я по-прежнему считаю разумным читать тело запроса более одного раза, особенно когда запрос проходит через цепочку фильтров. Я обычно использую цепочки фильтров для определенных операций, которые я хочу сохранить на уровне HTTP, отделенными от компонентов службы.

Как было предложено Уиллом Хартунгом, я добился этого, расширив HttpServletRequestWrapper, потребив запрос InputStream и по существу кэшировав байты.

public class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
  private ByteArrayOutputStream cachedBytes;

  public MultiReadHttpServletRequest(HttpServletRequest request) {
    super(request);
  }

  @Override
  public ServletInputStream getInputStream() throws IOException {
    if (cachedBytes == null)
      cacheInputStream();

      return new CachedServletInputStream();
  }

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

  private void cacheInputStream() throws IOException {
    /* Cache the inputstream in order to read it multiple times. For
     * convenience, I use apache.commons IOUtils
     */
    cachedBytes = new ByteArrayOutputStream();
    IOUtils.copy(super.getInputStream(), cachedBytes);
  }

  /* An inputstream which reads the cached request body */
  public class CachedServletInputStream extends ServletInputStream {
    private ByteArrayInputStream input;

    public CachedServletInputStream() {
      /* create a new input stream from the cached request body */
      input = new ByteArrayInputStream(cachedBytes.toByteArray());
    }

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

Теперь тело запроса можно прочитать более одного раза, если обернуть исходный запрос перед его передачей через цепочку фильтров:

public class MyFilter implements Filter {
  @Override
  public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain) throws IOException, ServletException {

    /* wrap the request in order to read the inputstream multiple times */
    MultiReadHttpServletRequest multiReadRequest = new MultiReadHttpServletRequest((HttpServletRequest) request);

    /* here I read the inputstream and do my thing with it; when I pass the
     * wrapped request through the filter chain, the rest of the filters, and
     * request handlers may read the cached inputstream
     */
    doMyThing(multiReadRequest.getInputStream());
    //OR
    anotherUsage(multiReadRequest.getReader());
    chain.doFilter(multiReadRequest, response);
  }
}

Это решение также позволит вам несколько раз читать тело запроса с помощью getParameterXXX методов, потому что базовый вызов - это getInputStream(), который, конечно, будет читать кешированный запрос InputStream.

Изменить

Для более новой версии интерфейса ServletInputStream. Вам необходимо предоставить реализацию еще нескольких методов, таких как isReady, setReadListener и т. Д. Обратитесь к этому вопросу, как указано в комментарии ниже.

person pestrella    schedule 16.06.2013
comment
Это правда? Базовым вызовом является getInputStream () по исходному запросу, байты которого вы уже прочитали. Базовый запрос ничего не знает о вашей оболочке, так как он узнает, что нужно вызвать getInputStream () оболочки? - person Frans; 01.10.2013
comment
Чтобы быть точным, getInputStream вызывается в my оболочке, поскольку это экземпляр ServletRequest, который я передаю в цепочку фильтров. Если вы все еще сомневаетесь, прочтите исходный код для ServletRequestWrapper и ServletRequest интерфейса. - person pestrella; 03.10.2013
comment
Если бы я мог сделать это +100, я бы сделал это. Я пытался заставить это работать правильно в течение 3-4 часов. Спасибо за наглядный пример и объяснение! Я рад, что нашел этот пост! - person Doug; 04.03.2014
comment
Любые предложения, как заставить эту работу работать с Servlet-api 3.0+? ServletInputStream теперь имеет абстрактный isReady(). isFinished() и setReadListener() для работы с неблокирующим вводом-выводом, который должен быть реализован. Я думаю, что ReadListener можно оставить пустым, но я не знаю, что делать с isFinished() и / или isReady(). - person Eric B.; 16.09.2014
comment
Работает ли он в любой среде? В случае Tomcat 7 методы getParameter () не вызывают getInputStream (). По крайней мере, в моем окружении этого не происходит ... - person 30thh; 25.09.2014
comment
Код работает для servlet-api 2.5, боюсь, вам придется протестировать 3.0 и Tomcat 7 (который также использует 3.0). - person pestrella; 25.09.2014
comment
CachedServletInputStream, расширяющий ServletInputStream, похоже, не реализует другие методы в соответствии с последней спецификацией. не могли бы вы опубликовать, как эти методы должны быть реализованы, чтобы эта оболочка работала - person yathirigan; 30.04.2015
comment
Я считаю, что вам также следует перезаписать getStream, поскольку этот метод используется readChunkedPostBody, используемым parseParameters, который заполняет атрибут параметров объекта coyote.Request. Проблема в том, что getStream не генерирует исключение IOException, а IOUtils.copy делает. Так что ловить его надо там, но толкового тут нет. - person Silver; 22.11.2015
comment
Я работаю над версией для Tomcat 8. Вы должны перезаписать readPostBody и readChunkedPostBody и использовать getStream, чтобы флаги usingInputStream и usingReader не устанавливались, поскольку они будут мешать Request # parseParameter (). Вы также можете перезаписать Request # parseParameter (), но если вам не нужно использовать читатель, он слишком усложняет ситуацию. Я постараюсь сформулировать полный ответ, когда мой код заработает. - person Silver; 23.11.2015
comment
Проблема с решением-оболочкой заключается в том, что изменение поведения getStream, readPostBody или readChunkedPostBody не имеет никакого эффекта, когда эти методы вызываются parseParameter, поскольку parseParameter выполняется в запросе, а не в WrappedRequest. Это не наследование ... Я заметил это, когда моя реализация не работала и методы, которые я модифицировал, никогда не вызывались. Я считаю, что этим нужно заниматься внутри Tomcat. Но, вероятно, сначала нужно изменить спецификацию сервлета. - person Silver; 23.11.2015
comment
@EricB. интересно, есть ли у вас решение для Servlet-api 3.0+ в конце концов? Я нашел много примеров для оболочки, но все они находятся под старым API, а именно не реализуют isReady (). isFinished () и setReadListener () - person dcc; 17.02.2016
comment
@wuha - я, честно говоря, не помню, что делал. К сожалению, я даже не помню, для какого проекта это был проект, поэтому я не могу копать старый исходный код. - person Eric B.; 18.02.2016
comment
@EricB. Спасибо, в любом случае. Позже я нашел решение для нового интерфейса api, просто вставил сюда на случай, если кому-то интересно. stackoverflow.com/questions/ 29208456 / - person dcc; 18.02.2016
comment
@Silver - есть ли у вас решение проблемы parseParameters запроса tomcat? Я потратил несколько часов на то, чтобы решить эту проблему (см. Заголовок stackoverflow.com/questions/36521793/) - person Lin; 10.04.2016
comment
Большое спасибо @pestrella за решение, сработавшее для меня. Не могли бы вы также помочь с MultiReadHttpServletResponse? Я пробовал аналогичный подходу MultiReadHttpServletRequest, но он почему-то не кешируется. - person skryvets; 04.08.2019

Я знаю, что опаздываю, но этот вопрос все еще был актуален для меня, и этот пост SO был одним из самых популярных в Google. Я собираюсь опубликовать свое решение в надежде, что кто-то еще может сэкономить пару часов.

В моем случае мне нужно было регистрировать все запросы и ответы с их телами. Используя Spring Framework, ответ на самом деле довольно прост, просто используйте заголовок ContentCachingRequestWrapper и ContentCachingResponseWrapper.

import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class LoggingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void destroy() {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        ContentCachingRequestWrapper requestWrapper = new ContentCachingRequestWrapper((HttpServletRequest) request);
        ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper((HttpServletResponse) response);

        try {
            chain.doFilter(requestWrapper, responseWrapper);
        } finally {

            String requestBody = new String(requestWrapper.getContentAsByteArray());
            String responseBody = new String(responseWrapper.getContentAsByteArray());
            // Do not forget this line after reading response content or actual response will be empty!
            responseWrapper.copyBodyToResponse();

            // Write request and response body, headers, timestamps etc. to log files

        }

    }

}
person Mikk    schedule 25.08.2016
comment
У меня это не сработало. И requestBody, и responseBody были пустыми строками - person Abhijith Madhav; 11.09.2016
comment
Виноват. Я делал chain.doFilter(request, response); вместо chain.doFilter(requestWrapper, responseWrapper); - person Abhijith Madhav; 11.09.2016
comment
Классы ContentCaching*Wrapper имеют высокую цену за потребление входного потока, поэтому кеширование выполняется с помощью метода getContentAsByteArray, но этот класс не кэширует входной поток, который может потребоваться другим фильтрам в цепочке фильтров (что является моим вариантом использования). Imho, это неожиданное поведение класса кэширования контента, поэтому я поднял это улучшение в весенней команде jira.spring.io/browse/SPR-16028 - person Federico Piazza; 29.09.2017
comment
Вы можете использовать AbstractRequestLoggingFilter из Spring, где большая часть работы уже выполнена Spring, и вам нужно только переопределить 1 или 2 простых метода. - person harsh; 12.12.2017
comment
У меня это не работает с spring-web-4.3.12.RELEASE. Когда я проверил источник, я обнаружил, что переменная cachedContent используется для хранения различного содержимого, такого как параметры запроса и inputStream запроса. Пусто, если звонить только getContentAsByteArray(). Для получения тела запроса необходимо позвонить в getInputStream(). Но опять же, это сделает inputStream недоступным для других фильтров и обработчика. - person Ivan Huang; 12.04.2018

Приведенные выше ответы были очень полезны, но в моем опыте все еще были некоторые проблемы. В сервлете Tomcat 7 3.0 также пришлось перезаписать getParamter и getParamterValues. Решение здесь включает в себя как параметры запроса получения, так и тело сообщения. Это позволяет легко получить необработанную строку.

Как и другие решения, он использует Apache commons-io и Googles Guava.

В этом решении методы getParameter * не генерируют IOException, но они используют super.getInputStream () (для получения тела), который может вызывать IOException. Я ловлю его и бросаю runtimeException. Это не так уж и приятно.

import com.google.common.collect.Iterables;
import com.google.common.collect.ObjectArrays;

import org.apache.commons.io.IOUtils;
import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.ContentType;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

/**
 * Purpose of this class is to make getParameter() return post data AND also be able to get entire
 * body-string. In native implementation any of those two works, but not both together.
 */
public class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
    public static final String UTF8 = "UTF-8";
    public static final Charset UTF8_CHARSET = Charset.forName(UTF8);
    private ByteArrayOutputStream cachedBytes;
    private Map<String, String[]> parameterMap;

    public MultiReadHttpServletRequest(HttpServletRequest request) {
        super(request);
    }

    public static void toMap(Iterable<NameValuePair> inputParams, Map<String, String[]> toMap) {
        for (NameValuePair e : inputParams) {
            String key = e.getName();
            String value = e.getValue();
            if (toMap.containsKey(key)) {
                String[] newValue = ObjectArrays.concat(toMap.get(key), value);
                toMap.remove(key);
                toMap.put(key, newValue);
            } else {
                toMap.put(key, new String[]{value});
            }
        }
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (cachedBytes == null) cacheInputStream();
        return new CachedServletInputStream();
    }

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

    private void cacheInputStream() throws IOException {
    /* Cache the inputStream in order to read it multiple times. For
     * convenience, I use apache.commons IOUtils
     */
        cachedBytes = new ByteArrayOutputStream();
        IOUtils.copy(super.getInputStream(), cachedBytes);
    }

    @Override
    public String getParameter(String key) {
        Map<String, String[]> parameterMap = getParameterMap();
        String[] values = parameterMap.get(key);
        return values != null && values.length > 0 ? values[0] : null;
    }

    @Override
    public String[] getParameterValues(String key) {
        Map<String, String[]> parameterMap = getParameterMap();
        return parameterMap.get(key);
    }

    @Override
    public Map<String, String[]> getParameterMap() {
        if (parameterMap == null) {
            Map<String, String[]> result = new LinkedHashMap<String, String[]>();
            decode(getQueryString(), result);
            decode(getPostBodyAsString(), result);
            parameterMap = Collections.unmodifiableMap(result);
        }
        return parameterMap;
    }

    private void decode(String queryString, Map<String, String[]> result) {
        if (queryString != null) toMap(decodeParams(queryString), result);
    }

    private Iterable<NameValuePair> decodeParams(String body) {
        Iterable<NameValuePair> params = URLEncodedUtils.parse(body, UTF8_CHARSET);
        try {
            String cts = getContentType();
            if (cts != null) {
                ContentType ct = ContentType.parse(cts);
                if (ct.getMimeType().equals(ContentType.APPLICATION_FORM_URLENCODED.getMimeType())) {
                    List<NameValuePair> postParams = URLEncodedUtils.parse(IOUtils.toString(getReader()), UTF8_CHARSET);
                    params = Iterables.concat(params, postParams);
                }
            }
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
        return params;
    }

    public String getPostBodyAsString() {
        try {
            if (cachedBytes == null) cacheInputStream();
            return cachedBytes.toString(UTF8);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /* An inputStream which reads the cached request body */
    public class CachedServletInputStream extends ServletInputStream {
        private ByteArrayInputStream input;

        public CachedServletInputStream() {
            /* create a new input stream from the cached request body */
            input = new ByteArrayInputStream(cachedBytes.toByteArray());
        }

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

    @Override
    public String toString() {
        String query = dk.bnr.util.StringUtil.nullToEmpty(getQueryString());
        StringBuilder sb = new StringBuilder();
        sb.append("URL='").append(getRequestURI()).append(query.isEmpty() ? "" : "?" + query).append("', body='");
        sb.append(getPostBodyAsString());
        sb.append("'");
        return sb.toString();
    }
}
person arberg    schedule 14.04.2016
comment
Отлично! Я пытался понять это несколько дней, и это работает с сервлетом 3.1. Один вопрос: почему вы делаете decode(getPostBodyAsString(), result); в getParameterMap()? Это создает параметр с ключом = тело запроса и значение = ноль, что довольно странно. - person orlade; 16.09.2016
comment
Вместо того, чтобы выполнять весь синтаксический анализ строки, почему вы не вызываете super.getParameterMap() в своем getParameterMap? Что в любом случае даст вам карту <String, String[]>. - person Ean V; 26.10.2017
comment
Так что я столкнулся с некоторыми проблемами и с paramMap. См. Мой ответ и ПРИЛОЖЕНИЕ от января 2021 г. там есть волшебная строка (из еще одного ответа на этот вопрос) .. которая может быть триггером для того, чтобы не сохранять вручную paramertMaps. Магическая часть кода super.getParameterMap (); // инициализируем кеш в ContentCachingRequestWrapper. ЭТО ВАЖНЫЙ ВЫЗОВ, так что параметры @RequestParam Map ‹String, String› заполняются в контроллере REST. - person granadaCoder; 28.01.2021

Единственный способ - использовать весь входной поток самостоятельно в фильтре, взять из него то, что вы хотите, а затем создать новый InputStream для прочитанного контента и поместить этот InputStream в ServletRequestWrapper (или HttpServletRequestWrapper).

Обратной стороной является то, что вам придется анализировать полезную нагрузку самостоятельно, стандарт не делает эту возможность доступной для вас.

Дополнения -

Как я уже сказал, вам нужно посмотреть HttpServletRequestWrapper.

В фильтре вы продолжаете, вызывая FilterChain.doFilter (запрос, ответ).

Для тривиальных фильтров запрос и ответ такие же, как и те, которые передаются фильтру. Так не должно быть. Вы можете заменить их своими собственными запросами и / или ответами.

HttpServletRequestWrapper специально разработан для этого. Вы передаете ему исходный запрос, а затем можете перехватывать все вызовы. Вы создаете свой собственный подкласс этого и заменяете метод getInputStream одним из ваших собственных. Вы не можете изменить входной поток исходного запроса, поэтому вместо этого у вас есть эта оболочка и вы возвращаете свой собственный входной поток.

Самый простой случай - использовать исходный входной поток запросов в байтовый буфер, делать с ним все, что угодно, а затем создавать новый ByteArrayInputStream из этого буфера. Это то, что возвращается в вашей оболочке, которая передается методу FilterChain.doFilter.

Вам нужно будет создать подкласс ServletInputStream и создать другую оболочку для вашего ByteArrayInputStream, но это тоже не имеет большого значения.

person Will Hartung    schedule 18.04.2012
comment
Мне не удается прочитать InputStream и восстановить его после, нет методов get / set для прямого доступа к потоку. Ваше предложение кажется хорошим, но я не вижу, как его реализовать. - person amuniz; 19.04.2012

Итак, это в основном ответ Лати, НО обновленный для новых требований для ServletInputStream.

А именно (для ServletInputStream) необходимо реализовать:

public abstract boolean isFinished();

public abstract boolean isReady();

public abstract void setReadListener(ReadListener var1);

Это отредактированный объект Лати

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

public class RequestWrapper extends HttpServletRequestWrapper {

    private String _body;

    public RequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        _body = "";
        BufferedReader bufferedReader = request.getReader();
        String line;
        while ((line = bufferedReader.readLine()) != null){
            _body += line;
        }
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {

        CustomServletInputStream kid = new CustomServletInputStream(_body.getBytes());
        return kid;
    }

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

и где-то (??) я нашел это (это первоклассный класс, который имеет дело с дополнительными методами.

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

public class CustomServletInputStream extends ServletInputStream {

    private byte[] myBytes;

    private int lastIndexRetrieved = -1;
    private ReadListener readListener = null;

    public CustomServletInputStream(String s) {
        try {
            this.myBytes = s.getBytes("UTF-8");
        } catch (UnsupportedEncodingException ex) {
            throw new IllegalStateException("JVM did not support UTF-8", ex);
        }
    }

    public CustomServletInputStream(byte[] inputBytes) {
        this.myBytes = inputBytes;
    }

    @Override
    public boolean isFinished() {
        return (lastIndexRetrieved == myBytes.length - 1);
    }

    @Override
    public boolean isReady() {
        // This implementation will never block
        // We also never need to call the readListener from this method, as this method will never return false
        return isFinished();
    }

    @Override
    public void setReadListener(ReadListener readListener) {
        this.readListener = readListener;
        if (!isFinished()) {
            try {
                readListener.onDataAvailable();
            } catch (IOException e) {
                readListener.onError(e);
            }
        } else {
            try {
                readListener.onAllDataRead();
            } catch (IOException e) {
                readListener.onError(e);
            }
        }
    }

    @Override
    public int read() throws IOException {
        int i;
        if (!isFinished()) {
            i = myBytes[lastIndexRetrieved + 1];
            lastIndexRetrieved++;
            if (isFinished() && (readListener != null)) {
                try {
                    readListener.onAllDataRead();
                } catch (IOException ex) {
                    readListener.onError(ex);
                    throw ex;
                }
            }
            return i;
        } else {
            return -1;
        }
    }
};

В конце концов, я просто пытался регистрировать запросы. И приведенные выше фрагменты, соединенные вместе франкенштейном, помогли мне создать нижеследующее.

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.Principal;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;

//one or the other based on spring version
//import org.springframework.boot.autoconfigure.web.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorAttributes;

import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.filter.OncePerRequestFilter;


/**
 * A filter which logs web requests that lead to an error in the system.
 */
@Component
public class LogRequestFilter extends OncePerRequestFilter implements Ordered {

    // I tried apache.commons and slf4g loggers.  (one or the other in these next 2 lines of declaration */
    //private final static org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory.getLog(LogRequestFilter.class);
    private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(LogRequestFilter.class);

    // put filter at the end of all other filters to make sure we are processing after all others
    private int order = Ordered.LOWEST_PRECEDENCE - 8;
    private ErrorAttributes errorAttributes;

    @Override
    public int getOrder() {
        return order;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        String temp = ""; /* for a breakpoint, remove for production/real code */

        /* change to true for easy way to comment out this code, remove this if-check for production/real code */
        if (false) {
            filterChain.doFilter(request, response);
            return;
        }

        /* make a "copy" to avoid issues with body-can-only-read-once issues */
        RequestWrapper reqWrapper = new RequestWrapper(request);

        int status = HttpStatus.INTERNAL_SERVER_ERROR.value();
        // pass through filter chain to do the actual request handling
        filterChain.doFilter(reqWrapper, response);
        status = response.getStatus();

        try {
            Map<String, Object> traceMap = getTrace(reqWrapper, status);
            // body can only be read after the actual request handling was done!
            this.getBodyFromTheRequestCopy(reqWrapper, traceMap);
            
            /* now do something with all the pieces of information gatherered */
            this.logTrace(reqWrapper, traceMap);
        } catch (Exception ex) {
            logger.error("LogRequestFilter FAILED: " + ex.getMessage(), ex);
        }
    }

    private void getBodyFromTheRequestCopy(RequestWrapper rw, Map<String, Object> trace) {
        try {
            if (rw != null) {
                byte[] buf = IOUtils.toByteArray(rw.getInputStream());
                //byte[] buf = rw.getInputStream();
                if (buf.length > 0) {
                    String payloadSlimmed;
                    try {
                        String payload = new String(buf, 0, buf.length, rw.getCharacterEncoding());
                        payloadSlimmed = payload.trim().replaceAll(" +", " ");
                    } catch (UnsupportedEncodingException ex) {
                        payloadSlimmed = "[unknown]";
                    }

                    trace.put("body", payloadSlimmed);
                }
            }
        } catch (IOException ioex) {
            trace.put("body", "EXCEPTION: " + ioex.getMessage());
        }
    }

    private void logTrace(HttpServletRequest request, Map<String, Object> trace) {
        Object method = trace.get("method");
        Object path = trace.get("path");
        Object statusCode = trace.get("statusCode");

        logger.info(String.format("%s %s produced an status code '%s'. Trace: '%s'", method, path, statusCode,
                trace));
    }

    protected Map<String, Object> getTrace(HttpServletRequest request, int status) {
        Throwable exception = (Throwable) request.getAttribute("javax.servlet.error.exception");

        Principal principal = request.getUserPrincipal();

        Map<String, Object> trace = new LinkedHashMap<String, Object>();
        trace.put("method", request.getMethod());
        trace.put("path", request.getRequestURI());
        if (null != principal) {
            trace.put("principal", principal.getName());
        }
        trace.put("query", request.getQueryString());
        trace.put("statusCode", status);

        Enumeration headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = (String) headerNames.nextElement();
            String value = request.getHeader(key);
            trace.put("header:" + key, value);
        }

        if (exception != null && this.errorAttributes != null) {
            trace.put("error", this.errorAttributes
                    .getErrorAttributes((WebRequest) new ServletRequestAttributes(request), true));
        }

        return trace;
    }
}

Пожалуйста, отнеситесь к этому коду с недоверием.

САМЫЙ важный тест - работает ли POST с полезной нагрузкой. Это то, что может вызвать проблемы с двойным чтением.

псевдо пример кода

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("myroute")
public class MyController {
    @RequestMapping(method = RequestMethod.POST, produces = "application/json")
    @ResponseBody
    public String getSomethingExample(@RequestBody MyCustomObject input) {

        String returnValue = "";

        return returnValue;
    }
}

Вы можете заменить MyCustomObject простым ole Object, если хотите просто протестировать.

Этот ответ основан на нескольких различных сообщениях и примерах SOF ... но потребовалось время, чтобы собрать все воедино, поэтому я надеюсь, что он поможет будущему читателю.

Пожалуйста, проголосуйте за ответ Лати перед моим. Без этого я бы не продвинулся так далеко.

Ниже приведено одно / некоторые из исключений, которые я получил во время работы над этим.

getReader () уже был вызван для этого запроса

Похоже, что некоторые из мест, которые я позаимствовал здесь:

http://slackspace.de/articles/log-request-body-with-spring-boot/

https://github.com/c0nscience/spring-web-logging/blob/master/src/main/java/org/zalando/springframework/web/logging/LoggingFilter.java

https://howtodoinjava.com/servlets/httpservletrequestwrapper-example-read-request-body/

https://www.oodlestechnologies.com/blogs/How-to-create-duplicate-object-of-httpServletRequest-object

https://github.com/c0nscience/spring-web-logging/blob/master/src/main/java/org/zalando/springframework/web/logging/LoggingFilter.java

Январь 2021 г. ПРИЛОЖЕНИЕ.

Я на собственном горьком опыте убедился, что приведенный выше код НЕ работает для

x-www-form-urlencoded

Рассмотрим пример ниже:

   @CrossOrigin
    @ResponseBody
    @PostMapping(path = "/mypath", consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
    public ResponseEntity myMethodName(@RequestParam Map<String, String> parameters
    ) {
        /* DO YOU GET ANY PARAMETERS HERE?  Or are they empty because of logging/auditing filter ?*/
        return new ResponseEntity(HttpStatus.OK);

    }

Здесь мне пришлось просмотреть несколько других примеров.

Я придумал обертку, которая явно работает для APPLICATION_FORM_URLENCODED_VALUE

import org.apache.commons.io.IOUtils;
import org.springframework.http.MediaType;
import org.springframework.web.util.ContentCachingRequestWrapper;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * Makes a "copy" of the HttpRequest so the body can be accessed more than 1 time.
 * WORKS WITH APPLICATION_FORM_URLENCODED_VALUE
 * See : https://stackoverflow.com/questions/44182370/why-do-we-wrap-httpservletrequest-the-api-provides-an-httpservletrequestwrappe/44187955#44187955
 */
public final class AppFormUrlEncodedSpecificContentCachingRequestWrapper extends ContentCachingRequestWrapper {

    public static final String ERROR_MSG_CONTENT_TYPE_NOT_SUPPORTED = "ContentType not supported. (Input ContentType(s)=\"%1$s\", Supported ContentType(s)=\"%2$s\")";

    public static final String ERROR_MSG_PERSISTED_CONTENT_CACHING_REQUEST_WRAPPER_CONSTRUCTOR_FAILED = "AppFormUrlEncodedSpecificContentCachingRequestWrapper constructor failed";

    private static final org.slf4j.Logger LOGGER = org.slf4j.LoggerFactory.getLogger(AppFormUrlEncodedSpecificContentCachingRequestWrapper.class);

    private byte[] body;

    private ServletInputStream inputStream;

    public AppFormUrlEncodedSpecificContentCachingRequestWrapper(HttpServletRequest request) {
        super(request);
        super.getParameterMap(); // init cache in ContentCachingRequestWrapper.  THIS IS THE VITAL CALL so that "@RequestParam Map<String, String> parameters" are populated on the REST Controller.  See https://stackoverflow.com/questions/10210645/http-servlet-request-lose-params-from-post-body-after-read-it-once/64924380#64924380

        String contentType = request.getContentType();
        /* EXPLICTLY check for APPLICATION_FORM_URLENCODED_VALUE and allow nothing else */
        if (null == contentType || !contentType.equalsIgnoreCase(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
            IllegalArgumentException ioex = new IllegalArgumentException(String.format(ERROR_MSG_CONTENT_TYPE_NOT_SUPPORTED, contentType, MediaType.APPLICATION_FORM_URLENCODED_VALUE));
            LOGGER.error(ERROR_MSG_PERSISTED_CONTENT_CACHING_REQUEST_WRAPPER_CONSTRUCTOR_FAILED, ioex);
            throw ioex;
        }

        try {
            loadBody(request);
        } catch (IOException ioex) {
            throw new RuntimeException(ioex);
        }
    }

    private void loadBody(HttpServletRequest request) throws IOException {
        body = IOUtils.toByteArray(request.getInputStream());
        inputStream = new CustomServletInputStream(this.getBody());
    }

    private byte[] getBody() {
        return body;
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (inputStream != null) {
            return inputStream;
        }
        return super.getInputStream();
    }
}

Обратите внимание на ответ Эндрю Снека на этой же странице. Это примерно так: https://programmersought.com/article/23981013626/

У меня не было времени согласовать две вышеупомянутые реализации (мои две).

Итак, я создал Factory, чтобы выбирать из двух:

import org.springframework.http.MediaType;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.IOException;

/**
 * Factory to return different concretes of HttpServletRequestWrapper. APPLICATION_FORM_URLENCODED_VALUE needs a different concrete.
 */
public class HttpServletRequestWrapperFactory {

    public static final String ERROR_MSG_HTTP_SERVLET_REQUEST_WRAPPER_FACTORY_CREATE_HTTP_SERVLET_REQUEST_WRAPPER_FAILED = "HttpServletRequestWrapperFactory createHttpServletRequestWrapper FAILED";

    public static HttpServletRequestWrapper createHttpServletRequestWrapper(final HttpServletRequest request) {
        HttpServletRequestWrapper returnItem = null;

        if (null != request) {
            String contentType = request.getContentType();
            if (null != contentType && contentType.equalsIgnoreCase(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
                returnItem = new AppFormUrlEncodedSpecificContentCachingRequestWrapper(request);
            } else {
                try {
                    returnItem = new PersistedBodyRequestWrapper(request);
                } catch (IOException ioex) {
                    throw new RuntimeException(ERROR_MSG_HTTP_SERVLET_REQUEST_WRAPPER_FACTORY_CREATE_HTTP_SERVLET_REQUEST_WRAPPER_FAILED, ioex);
                }
            }
        }

        return returnItem;
    }

}

Ниже приведен еще один, который работает с JSON и т. Д. Это еще один конкретный объект, который Factory может выводить. Я поместил его здесь, чтобы мое ПРИЛОЖЕНИЕ от января 2021 года было согласованным ... Я не знаю, идеально ли приведенный ниже код соответствует моему исходному ответу:

import org.springframework.http.MediaType;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
 * Makes a "copy" of the HttpRequest so the body can be accessed more than 1 time.
 * See : https://stackoverflow.com/questions/44182370/why-do-we-wrap-httpservletrequest-the-api-provides-an-httpservletrequestwrappe/44187955#44187955
 * DOES NOT WORK WITH APPLICATION_FORM_URLENCODED_VALUE
 */
public final class PersistedBodyRequestWrapper extends HttpServletRequestWrapper {

    public static final String ERROR_MSG_CONTENT_TYPE_NOT_SUPPORTED = "ContentType not supported. (ContentType=\"%1$s\")";

    public static final String ERROR_MSG_PERSISTED_BODY_REQUEST_WRAPPER_CONSTRUCTOR_FAILED = "PersistedBodyRequestWrapper constructor FAILED";

    private static final org.slf4j.Logger LOGGER = org.slf4j.LoggerFactory.getLogger(PersistedBodyRequestWrapper.class);

    private String persistedBody;

    private final Map<String, String[]> parameterMap;

    public PersistedBodyRequestWrapper(final HttpServletRequest request) throws IOException {
        super(request);

        String contentType = request.getContentType();
        /* Allow everything EXCEPT APPLICATION_FORM_URLENCODED_VALUE */
        if (null != contentType && contentType.equalsIgnoreCase(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
            IllegalArgumentException ioex = new IllegalArgumentException(String.format(ERROR_MSG_CONTENT_TYPE_NOT_SUPPORTED, MediaType.APPLICATION_FORM_URLENCODED_VALUE));
            LOGGER.error(ERROR_MSG_PERSISTED_BODY_REQUEST_WRAPPER_CONSTRUCTOR_FAILED, ioex);
            throw ioex;
        }

        parameterMap = request.getParameterMap();
        this.persistedBody = "";
        BufferedReader bufferedReader = request.getReader();
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            this.persistedBody += line;
        }
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        CustomServletInputStream csis = new CustomServletInputStream(this.persistedBody.getBytes(StandardCharsets.UTF_8));
        return csis;
    }

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

    @Override
    public Map<String, String[]> getParameterMap() {
        return this.parameterMap;
    }
}
person granadaCoder    schedule 18.01.2019
comment
Просто вау !!!!! - person KnockingHeads; 21.05.2021

У меня тоже была такая же проблема, и я считаю, что приведенный ниже код более простой и работает для меня,

public class MultiReadHttpServletRequest extends  HttpServletRequestWrapper {

 private String _body;

public MultiReadHttpServletRequest(HttpServletRequest request) throws IOException {
   super(request);
   _body = "";
   BufferedReader bufferedReader = request.getReader();           
   String line;
   while ((line = bufferedReader.readLine()) != null){
       _body += line;
   }
}

@Override
public ServletInputStream getInputStream() throws IOException {
   final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(_body.getBytes());
   return new ServletInputStream() {
       public int read() throws IOException {
           return byteArrayInputStream.read();
       }
   };
}

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

в классе java фильтра,

HttpServletRequest properRequest = ((HttpServletRequest) req);
MultiReadHttpServletRequest wrappedRequest = new MultiReadHttpServletRequest(properRequest);
req = wrappedRequest;
inputJson = IOUtils.toString(req.getReader());
System.out.println("body"+inputJson);

Пожалуйста, дайте мне знать, если у вас возникнут вопросы

person Lathy    schedule 05.08.2015

Spring имеет встроенную поддержку для этого с AbstractRequestLoggingFilter:

@Bean
public Filter loggingFilter(){
    final AbstractRequestLoggingFilter filter = new AbstractRequestLoggingFilter() {
        @Override
        protected void beforeRequest(final HttpServletRequest request, final String message) {

        }

        @Override
        protected void afterRequest(final HttpServletRequest request, final String message) {

        }
    };

    filter.setIncludePayload(true);
    filter.setIncludeQueryString(false);
    filter.setMaxPayloadLength(1000000);

    return filter;
}

К сожалению, вы по-прежнему не сможете прочитать полезную нагрузку непосредственно из запроса, но параметр сообщения String будет включать полезную нагрузку, поэтому вы можете получить ее оттуда следующим образом:

String body = message.substring(message.indexOf("{"), message.lastIndexOf("]"));

person Markoorn    schedule 26.02.2019
comment
Я надеялся использовать ваше решение для создания журнала аудита, но мне нужно записать, был ли запрос успешным, могу ли я подключиться к HTTP-ответу и получить код в этом классе. - person jonesy; 21.06.2019

Я нашел хорошее решение для любого формата тела запроса. Я тестировал application/x-www-form-urlencoded и application/json, оба работали очень хорошо. Проблема ContentCachingRequestWrapper, которая предназначена только для x-www-form-urlencoded тела запроса, но не работает, например, с json. Я нашел решение для json link. У него была проблема, что он не поддерживал x-www-form-urlencoded. Я присоединился к обоим в своем коде:

import org.apache.commons.io.IOUtils;
import org.springframework.web.util.ContentCachingRequestWrapper;

import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;

public class MyContentCachingRequestWrapper extends ContentCachingRequestWrapper {

    private byte[] body;

    public MyContentCachingRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        super.getParameterMap(); // init cache in ContentCachingRequestWrapper
        body = super.getContentAsByteArray(); // first option for application/x-www-form-urlencoded
        if (body.length == 0) {
          try {
            body = IOUtils.toByteArray(super.getInputStream()); // second option for other body formats
          } catch (IOException ex) {
            body = new byte[0];
          }
        }
    }

    public byte[] getBody() {
        return body;
    }

    @Override
    public ServletInputStream getInputStream() {
        return new RequestCachingInputStream(body);
    }

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

    private static class RequestCachingInputStream extends ServletInputStream {

        private final ByteArrayInputStream inputStream;

        public RequestCachingInputStream(byte[] bytes) {
            inputStream = new ByteArrayInputStream(bytes);
        }

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

        @Override
        public boolean isFinished() {
            return inputStream.available() == 0;
        }

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

        @Override
        public void setReadListener(ReadListener readlistener) {
        }

    }

}
person Andrew Sneck    schedule 20.11.2020
comment
Ваш вызов super.getParameterMap (); было волшебством для меня. - person granadaCoder; 27.01.2021
comment
Я обновил свой ответ ... но в вашем ответе (за который я проголосовал) была волшебная строка ... которая помогала моему коду. и хорошо расположенный комментарий рядом с вашим кодом. Благодарю. super.getParameterMap (); // кеш инициализации в ContentCachingRequestWrapper - person granadaCoder; 28.01.2021
comment
@granadaCoder, вы должны нажать на метод супер, и вы увидите, что происходит - person Andrew Sneck; 21.05.2021

Просто перезапись getInputStream() в моем случае не сработала. Моя реализация сервера, похоже, анализирует параметры без вызова этого метода. Я не нашел другого способа, но также повторно реализовал все четыре метода getParameter *. Вот код getParameterMap (используются Apache Http Client и библиотека Google Guava):

@Override
public Map<String, String[]> getParameterMap() {
    Iterable<NameValuePair> params = URLEncodedUtils.parse(getQueryString(), NullUtils.UTF8);

    try {
        String cts = getContentType();
        if (cts != null) {
            ContentType ct = ContentType.parse(cts);
            if (ct.getMimeType().equals(ContentType.APPLICATION_FORM_URLENCODED.getMimeType())) {
                List<NameValuePair> postParams = URLEncodedUtils.parse(IOUtils.toString(getReader()), NullUtils.UTF8);
                params = Iterables.concat(params, postParams);
            }
        }
    } catch (IOException e) {
        throw new IllegalStateException(e);
    }
    Map<String, String[]> result = toMap(params);
    return result;
}

public static Map<String, String[]> toMap(Iterable<NameValuePair> body) {
    Map<String, String[]> result = new LinkedHashMap<>();
    for (NameValuePair e : body) {
        String key = e.getName();
        String value = e.getValue();
        if (result.containsKey(key)) {
            String[] newValue = ObjectArrays.concat(result.get(key), value);
            result.remove(key);
            result.put(key, newValue);
        } else {
            result.put(key, new String[] {value});
        }
    }
    return result;
}
person 30thh    schedule 25.09.2014
comment
К сожалению, у Jetty есть эта проблема, grepcode.com/file/repo1.maven.org/maven2/org.eclipse.jetty/ - person mikeapr4; 01.10.2015
comment
Вы, вероятно, используете Tomcat 7 или более позднюю версию с Servlet 3.0? У вас есть код для остальных трех методов? - person Silver; 22.11.2015
comment
Остальные 3 метода просто вызывают getParameterMap () и получают необходимое значение. - person 30thh; 23.11.2015
comment
Так что я столкнулся с некоторыми проблемами и с paramMap. См. Мой ответ и ПРИЛОЖЕНИЕ от января 2021 г. там есть волшебная строка (которая пришла из еще одного ответа на этот вопрос) .. которая может быть триггером для того, чтобы не сохранять вручную paramertMaps. Магическая часть кода super.getParameterMap (); // инициализируем кеш в ContentCachingRequestWrapper. ЭТО ВАЖНЫЙ ВЫЗОВ, так что параметры @RequestParam Map ‹String, String› заполняются в контроллере REST. - person granadaCoder; 28.01.2021

Если у вас есть контроль над запросом, вы можете установить тип контента на двоичный / октетный поток. Это позволяет запрашивать параметры без использования входного потока.

Однако это может быть специфическим для некоторых серверов приложений. Я тестировал только tomcat, причал, похоже, ведет себя одинаково в соответствии с https://stackoverflow.com/a/11434646/957103.

person Olivier.Roger    schedule 27.08.2014

Метод getContentAsByteArray () класса Spring ContentCachingRequestWrapper читает тело несколько раз, но методы getInputStream () и getReader () того же класса не читают тело несколько раз:

"Этот класс кэширует тело запроса, потребляя InputStream. Если мы читаем InputStream в одном из фильтров, то другие последующие фильтры в цепочке фильтров больше не могут его читать. Из-за этого ограничения этот класс не подходит для всех ситуации ".

В моем случае более общее решение, которое решило эту проблему, заключалось в добавлении следующих трех классов в мой проект загрузки Spring (и необходимых зависимостей для файла pom):

CachedBodyHttpServletRequest.java:

public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper {

    private byte[] cachedBody;

    public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException {
        super(request);
        InputStream requestInputStream = request.getInputStream();
        this.cachedBody = StreamUtils.copyToByteArray(requestInputStream);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        return new CachedBodyServletInputStream(this.cachedBody);
    }

    @Override
    public BufferedReader getReader() throws IOException {
        // Create a reader from cachedContent
        // and return it
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.cachedBody);
        return new BufferedReader(new InputStreamReader(byteArrayInputStream));
    }
}

CachedBodyServletInputStream.java:

public class CachedBodyServletInputStream extends ServletInputStream {

    private InputStream cachedBodyInputStream;

    public CachedBodyServletInputStream(byte[] cachedBody) {
        this.cachedBodyInputStream = new ByteArrayInputStream(cachedBody);
    }

    @Override
    public boolean isFinished() {
        try {
            return cachedBodyInputStream.available() == 0;
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return false;
    }

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

    @Override
    public void setReadListener(ReadListener readListener) {
        throw new UnsupportedOperationException();
    }

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

ContentCachingFilter.java:

@Order(value = Ordered.HIGHEST_PRECEDENCE)
@Component
@WebFilter(filterName = "ContentCachingFilter", urlPatterns = "/*")
public class ContentCachingFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        System.out.println("IN  ContentCachingFilter ");
        CachedBodyHttpServletRequest cachedBodyHttpServletRequest = new CachedBodyHttpServletRequest(httpServletRequest);
        filterChain.doFilter(cachedBodyHttpServletRequest, httpServletResponse);
    }
}

Я также добавил в pom следующие зависимости:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.10.0</version>
</dependency>

Учебный и полный исходный код находится здесь: https://www.baeldung.com/spring-reading-httpservletrequest-multiple-times

person Veli-Matti Sorvala    schedule 20.12.2019

вы можете использовать цепочку фильтров сервлетов, но вместо этого использовать исходную, вы можете создать свой собственный запрос. yourownrequests extends HttpServletRequestWrapper.

person Zhengwei Zhan    schedule 30.06.2013
comment
Похоже, что ссылка на учебник теперь содержит вирус. - person fasth; 19.07.2016

Прежде всего, мы не должны читать параметры в фильтре. Обычно заголовки читаются в фильтре для выполнения нескольких задач аутентификации. Сказав, что можно полностью прочитать тело HttpRequest в фильтре или перехватчике, используя CharStreams:

String body = com.google.common.io.CharStreams.toString(request.getReader());

Это никак не влияет на последующие чтения.

person ashoka    schedule 23.12.2016
comment
Да. Если вы сделаете это один раз, request.getReader() вернет читатель, который будет содержать только пустую строку при последующих чтениях. - person christophetd; 04.03.2017
comment
Я бы работал в случае перезаписи методов getReader () и getInputStream (), чтобы использовать это новое тело в качестве источника. - person Rodrigo Borba; 23.07.2018