Регистрация параметров HttpRequest и тела запроса

Я пытаюсь создать журнал запросов для своего веб-приложения. Я использую Spring 3.0.

Я реализовал класс, расширяющий HandlerInterceptorAdapter, и использовал preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) для перехвата запроса.

В методе я хочу иметь возможность регистрировать тело запроса (мои параметры - это объекты в XML, которые записываются непосредственно в тело запроса), и для этого я использую request.getReader();

Проблема в том, что позже я получу IllegalStateException, когда контроллер Spring попытается прочитать запрос.

Есть ли способ сделать то, что я намереваюсь?


person Noam Nevo    schedule 12.06.2011    source источник
comment
Есть пара проблем. В ServletRequest вы можете вызывать только getReader() или getInputStream(), но не оба. Если вы вызываете оба, вы получаете исключение IllegalStateException. Вы можете попробовать вызвать getInputStream, но вы все равно можете получить сообщение об ошибке, если прочитаете входной поток. Spring может не увидеть его. (ServletInputStream может поддерживать сброс, но я так не думаю). Лучше всего регистрировать параметры либо во время процесса десериализации XML (вы должны выяснить, какой класс это делает), либо сразу после него.   -  person Pace    schedule 12.06.2011
comment
Вы рассматривали возможность настройки своего http-сервера для регистрации заголовков запросов или использования фильтров сервлетов для ведения журнала?   -  person happymeal    schedule 12.06.2011
comment
@happymeal Мне не нужны только заголовки, мне нужно и тело.   -  person Noam Nevo    schedule 12.06.2011
comment
любое решение будет работать. в подходе с фильтром сервлета вам просто нужно прочитать из входного потока и распечатать все; это также будет включать тело запроса. в подходе с http-сервером вы должны обратиться к своей документации, поскольку конфигурация отличается от поставщика к поставщику.   -  person happymeal    schedule 12.06.2011


Ответы (3)


Это можно сделать с помощью фильтра. Параметры запроса просты в обработке. Однако работа с телом запроса будет намного сложнее и потребует обертывания запроса сервлета, см.: HttpServletRequest.

Вам нужно будет посмотреть, насколько велик входящий запрос, и решить, хотите ли вы сохранить тело запроса в виде файла tmp или строки.

Вам нужно будет переопределить ServetRequest.getInputStream() вашим файлом или сохраненной строкой, которая использовалась для ведения журнала.

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

public class LogRequest extends HttpServletRequestWrapper {

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

    @Override
    public ServletInputStream getInputStream() throws IOException {
        //read from tmp file or string.
    }

    @Override
    public BufferedReader getReader() throws IOException {
        //read from tmp file or string
    }

}
person Adam Gent    schedule 12.06.2011
comment
Обтекание запроса по-прежнему не позволяет мне дважды вызывать getReader(). Я все еще получаю нелегальное исключение StateException - person Noam Nevo; 12.06.2011
comment
Ноам Нево, вы собираетесь вызывать методы ввода (reader/inputstream) в исходном запросе, а затем обернуть исходный запрос оболочкой запроса с переопределением getReader() и друзей. - person Adam Gent; 12.06.2011

У Spring есть готовый фильтр, который сделает это за вас — см. использование AbstractRequestLoggingFilter и его подклассов, описанных в этот ответ.

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

person Adam Michalik    schedule 27.11.2014
comment
AbstractRequestLoggingFilter не позволит вам регистрировать запрос до его обработки. Вы не можете получить полезную нагрузку в AbstractRequestLoggingFilter#beforeRequest. Если вас это устраивает, вы можете переопределить AbstractRequestLoggingFilter#afterRequest, который будет регистрировать все, включая полезную нагрузку. - person Eugene To; 06.02.2017

Простая реализация для небольших запросов. Не используйте его для составного запроса.

package ru.rbs.logger.web;

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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

class CachedRequestWrapper extends HttpServletRequestWrapper {
    private final byte[] cachedBody;

    CachedRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        IOUtils.copy(request.getInputStream(), bos);
        cachedBody = bos.toByteArray();
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        return new CachedServletInputStream();
    }

    byte[] toByteArray(){
        return cachedBody;
    }

    private class CachedServletInputStream extends ServletInputStream {
        private InputStream baseInputStream;

        CachedServletInputStream() throws IOException {
            baseInputStream = new ByteArrayInputStream(cachedBody);
        }

        @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 baseInputStream.read();
        }
    }
}
person Eugene To    schedule 06.02.2017