Estoy tratando de acceder a dos de solicitud http de parámetros en Java Servlet filtro, no hay nada nuevo aquí, pero se sorprendió al encontrar que los parámetros hayan sido ya consumidos! Debido a esto, ya no está disponible en la cadena de filtros.

Parece que esto sólo ocurre cuando los parámetros que se trata de una solicitud POST del cuerpo (presentar un formulario, por ejemplo).

Hay una manera de leer los parámetros y NO los consuma?

Hasta ahora he encontrado sólo esta referencia: Filtro de servlets mediante solicitud.getParameter pierde los datos del Formulario.

Gracias!

  • tal vez mostrar un fragmento de código de cómo lo estás haciendo?
  • ¿Te getInputStream() o getReader()? Parece que ellos son los que van a interferir con la ejecución de getParameter()
InformationsquelleAutor amuniz | 2012-04-18

12 Comentarios

  1. 89

    Como un aparte, una forma alternativa de resolver este problema es no utilizar el filtro de la cadena y en lugar de construir su propio interceptor componente, tal vez el uso de aspectos, que pueden operar en el se analiza la solicitud de cuerpo. También es probable que sea más eficiente, ya que sólo la conversión de la solicitud InputStream en su propio modelo de objetos de una vez.

    Sin embargo, creo que es razonable querer leer el cuerpo de solicitud más de una vez, especialmente cuando la solicitud se mueve a través de la cadena de filtros. Yo normalmente uso el filtro de cadenas para ciertas operaciones que quiero mantener en la capa HTTP, desacoplado de los componentes del servicio.

    Según lo sugerido por Se Hartung he logrado esto mediante la ampliación de HttpServletRequestWrapper, el consumo de la solicitud InputStream y esencialmente de almacenamiento en caché de los bytes.

    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();
        }
      }
    }

    Ahora el cuerpo de solicitud puede ser leído más de una vez por envolver el original de la solicitud antes de pasar por el filtro de la cadena:

    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);
      }
    }

    Esta solución también se puede leer el cuerpo de solicitud varias veces a través de la getParameterXXX métodos, debido a la llamada subyacente es getInputStream(), que, por supuesto, leer la caché de solicitud de InputStream.

    Editar

    Para la versión más reciente de ServletInputStream de la interfaz. Usted necesidad de proporcionar la aplicación de algunos métodos más como isReady, setReadListener etc. Consulte este pregunta como siempre en los comentarios de abajo.

    • Es eso cierto? La llamada subyacente es getInputStream() en la petición original, de la que ya te has leído los bytes. El subyacente de la solicitud no se tiene conocimiento de su contenedor, entonces, ¿cómo saben llamar la envoltura del getInputStream()?
    • Para ser precisos getInputStream se llama en la contenedor ya que este es el ServletRequest ejemplo que me paso en la cadena de filtros. Si usted todavía está en duda, leer el código fuente para ServletRequestWrapper y la ServletRequest de la interfaz.
    • Si yo pudiera hacer esto +100, yo lo haría. He estado tratando de conseguir que esto funcione bien durante 3-4 horas. Gracias por tu ejemplo claro y explicación! Me alegro de haber encontrado este post!!!
    • Alguna sugerencia de cómo hacer este trabajo con Servlet api 3.0+ ? El ServletInputStream ahora ha abstracto isReady(). isFinished() y setReadListener() tratar con la falta de bloqueo de IO que debe ser implementado. Estoy pensando en la ReadListener podría ser dejado en blanco, pero no está seguro de qué hacer al respecto isFinished() y/o isReady().
    • Funciona en cualquier entorno? En el caso de Tomcat 7 el getParameter() métodos no llame getInputStream(). Al menos no sucede en mi entorno…
    • El código funciona para servlet api 2.5, me temo que voy a tener que probar el 3.0 y Tomcat 7 (que también utiliza 3.0).
    • El CachedServletInputStream la ampliación de ServletInputStream no parece aplicar los otros métodos como por las últimas specificiation. podría publicar, ¿cómo serían esos métodos deben ser implementadas para hacer este contenedor de trabajo
    • Creo que también debe sobrescribir getStream, ya que este método está siendo utilizado por readChunkedPostBody utilizado por parseParameters que rellena los parámetros de atributo de el coyote.Objeto de solicitud. El problema es que getStream no lanzar una IOException pero IOUtils.copia no. Así que usted necesita para ponerse allí, pero no es sensato hacer con él.
    • Estoy trabajando en una versión de Tomcat 8. Usted tiene que sobreescribir readPostBody y readChunkedPostBody y uso getStream así las banderas «usingInputStream’ y ‘usingReader’ no son ya que interfieran con la Petición de#parseParameter(). También puede sobrescribir Solicitud#parseParameter (), pero si usted no necesita utilizar el lector overcomplicates cosas. Voy a tratar de formular una respuesta completa cuando mi código funciona.
    • El problema con el contenedor de la solución es que la modificación de la conducta de getStream, readPostBody o readChunkedPostBody no tiene ningún efecto cuando se llama a estos métodos por parseParameter desde parseParameter se ejecuta en la Solicitud y no la WrappedRequest. No es herencia… me di cuenta de esto cuando mi aplicación no funciona y los métodos que yo había modificado nunca fueron llamados. Creo que esto debe ser tratado internamente en Tomcat. Pero, probablemente, la especificación servlet tiene que cambiar primero.
    • preguntando tienes una solución para el Servlet api 3.0+ en la final? He encontrado muchos ejemplos para el contenedor, pero todos están bajo la edad de la api a saber, la no aplicación de isReady(). isFinished() y setReadListener()
    • Yo, sinceramente, no recuerdo lo que hice. Y por desgracia, ni siquiera me acuerdo de lo que este proyecto era para entonces no puedo ir a desenterrar viejos código fuente.
    • gracias de todos modos. Después encontré la solución para la nueva api de la interfaz, acaba de pegar aquí en caso de que alguien esté interesado. stackoverflow.com/questions/29208456/…
    • ¿tiene una solución para el tomcat Solicitud parseParameters problema? He pasado varias horas en tratar de resolver esta (ver stackoverflow.com/questions/36521793/…)
    • Muchas gracias @pestrella para la solución, trabajó para mí. Por favor puede también ayudar con MultiReadHttpServletResponse ? He intentado similar a MultiReadHttpServletRequest enfoque, pero por alguna razón no se caché.

  2. 30

    Sé que llego tarde, pero esta cuestión todavía era relevante para mí y ese post fue uno de los grandes éxitos de Google. Yo voy por delante y post mi solución en la esperanza de que alguien más podría salvar a la par de horas.

    En mi caso necesitaba registro de todas las solicitudes y respuestas con sus cuerpos. El uso de Spring Framework, la respuesta es en realidad bastante simple, sólo tiene que utilizar ContentCachingRequestWrapper y 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
    
            }
    
        }
    
    }
    • Esto no funciona para mí. Ambos requestBody y responseBody fueron las cadenas vacías
    • Mi error. Yo estaba haciendo un chain.doFilter(request, response); en lugar de un chain.doFilter(requestWrapper, responseWrapper);
    • El ContentCaching*Wrapper clases tienen el caro precio de consumo de la secuencia de entrada para el «caché» se realiza a través del método de getContentAsByteArray pero esta clase no es la caché de la secuencia de entrada que podrían ser necesarios otros filtros en la cadena de filtros (que es mi caso de uso). En mi humilde opinión, esto no es un comportamiento esperado de un almacenamiento en caché del contenido de la clase, por lo tanto me planteó esta mejora en la primavera de equipo de jira.de la primavera.io/browse/SPR-16028
    • Usted puede utilizar AbstractRequestLoggingFilter a partir de la Primavera, donde la mayor parte del trabajo ya está hecho por la Primavera y usted sólo necesita reemplazar 1 o 2 métodos simples.
    • Esto no funciona para mí como de spring-web-4.3.12.RELEASE. Como he comprobado la fuente que he encontrado la variable cachedContent se utiliza para almacenar diversos contenidos, tales como los parámetros de solicitud y de solicitud de inputStream. Es vacío si se llama getContentAsByteArray() únicamente. Para obtener la solicitud de cuerpo tienes para llamar getInputStream(). Pero de nuevo, esto hará que el inputStream disponible a otros filtros y el controlador.
  3. 4

    La única manera sería que te consume toda la secuencia de entrada a ti mismo en el filtro, toma lo que quieras de ella, y, a continuación, crear una nueva InputStream para el contenido que usted de leer, y puesto que InputStream en un ServletRequestWrapper (o HttpServletRequestWrapper).

    La desventaja es que usted tendrá que analizar la carga de sí mismo, la norma no hace que la capacidad disponible para usted.

    Anexos —

    Como ya he dicho, tienes que mirar en HttpServletRequestWrapper.

    En un filtro, que continúe a lo largo llamando cadena de filtros.doFilter(request, response).

    Trivial de los filtros, la solicitud y la respuesta son las mismas que las que se pasa para el filtro. Que no tiene que ser el caso. Usted puede reemplazar a los que tienen sus propias solicitudes y/o respuestas.

    HttpServletRequestWrapper está diseñado específicamente para facilitar esto. Le pasamos el original de la solicitud y, a continuación, usted puede interceptar todas las llamadas. Usted puede crear su propia subclase de esto, y reemplazar el getInputStream método con uno de sus propios. Usted no puede cambiar la secuencia de entrada de la solicitud original, así que en lugar de tener este contenedor y volver a su propio flujo de entrada.

    El caso más simple es consumir los originales de las solicitudes de flujo de entrada en un byte del búfer, hacer lo que la magia que quieras en él, a continuación, crear una nueva ByteArrayInputStream de ese buffer. Esto es lo que se devuelve en su contenedor, que se pasa a la cadena de filtros.método doFilter.

    Necesitará subclase ServletInputStream y hacer otro contenedor para su ByteArrayInputStream, pero eso no es gran cosa tampoco.

    • No he podido leer el InputStream y restaurar después, no hay métodos get/set para acceder de forma directa a la corriente. Su propuesta parece buena, pero no veo cómo ponerlo en práctica.
  4. 4

    Las respuestas anteriores fueron muy útiles, pero todavía había algunos problemas en mi experiencia. En tomcat 7 servlet 3.0, la getParamter y getParamterValues también tuvo que ser sobrescrito. La solución que aquí se incluye tanto get-los parámetros de la consulta y el post-cuerpo. Permite obtener primas de cadena sencilla.

    Como las otras soluciones que se utiliza Apache commons-io y Gafas de Guayaba.

    En esta solución el getParameter* métodos no tirar IOException pero el uso de super.getInputStream() (para obtener el cuerpo) que puede lanzar IOException. Yo cogerlo y tirar runtimeException. No es tan agradable.

    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();
    }
    }
    • Esto es genial! He estado tratando de averiguar esto por días, y esto funciona con servlet 3.1. Una pregunta: ¿por qué haces decode(getPostBodyAsString(), result); en getParameterMap()? Que crea un parámetro con la tecla = cuerpo de solicitud y el valor = null, que es bastante raro.
    • En lugar de ir a través de toda la cadena de análisis, ¿por qué no llamas a super.getParameterMap() en su getParameterMap? Que le dará un mapa de <String, String[]> de todos modos.
  5. 3

    Yo también tuve el mismo problema y creo que el siguiente código es más simple y se está trabajando para mí,

    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()));
    }
    }

    en el filtro de clase de java,

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

    Por favor, hágamelo saber si usted tiene alguna pregunta

  6. 2

    Así que esto es básicamente Lathy la respuesta, PERO actualizado para los nuevos requisitos para ServletInputStream.

    A saber (por ServletInputStream), uno tiene que implementar:

    public abstract boolean isFinished();
    public abstract boolean isReady();
    public abstract void setReadListener(ReadListener var1);

    Este es el editado Lathy del objeto

    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()));
    }
    }

    y en algún lugar (??) He encontrado este (que es de primera clase de una clase que se ocupa de la «extra» de los métodos.

    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;
    }
    }
    };

    En última instancia, sólo estaba tratando de registro de las solicitudes. Y por encima de la frankensteined juntos piezas me ayudó a crear a continuación.

    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;
    }
    }

    Por favor, tome este código con un grano de sal.

    El MÁS importante «prueba» es que si un POST funciona con una capacidad de carga. Esto es lo que va a exponer «doble lectura» de los problemas.

    pseudo código de ejemplo

    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;
    }
    }

    Se puede sustituir «MyCustomObject» con ole llano «Objeto» si lo que desea es probar.

    Esta respuesta es frankensteined de diferentes SOF puestos y ejemplos..pero tomó un tiempo para juntar todo así que espero que ayude un futuro lector.

    Por favor upvote Lathy la respuesta antes que la mía. Yo podría no han llegado hasta este punto sin ella.

    A continuación es de una o algunas de las excepciones que obtuve mientras trabajando en ello.

    getReader() ya ha sido llamado para esta solicitud

    Se parece a algunos de los lugares que más me «prestada» de está aquí:

    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

  7. 2

    La primavera ha construido en el apoyo a este con un 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;
    }

    Por desgracia, todavía no será capaz de leer la carga directamente la solicitud, pero el mensaje de Cadena parámetro se incluyen la carga de modo que usted puede tomar desde allí de la siguiente manera:

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

    • Tenía la esperanza de utilizar la solución para generar un registro de auditoría, pero tengo que registrar si la solicitud se ha realizado correctamente, puede que me enganche en la respuesta http y obtener el código de esta clase.
  8. 1

    Sólo la sobreescritura de getInputStream() no funcionó en mi caso. Mi servidor parece que la aplicación de análisis de los parámetros sin necesidad de llamar a este método. No encontré otra manera, pero volver a implementar el todos los cuatro getParameter* métodos así. Aquí está el código de getParameterMap (Apache Http de Cliente de Google y de la Guayaba la biblioteca):

    @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;
    }
  9. 0

    Si usted tiene el control sobre la solicitud, no se pudo establecer el tipo de contenido a binario/octet-stream. Esto permite la consulta de parámetros sin consumir la secuencia de entrada.

    Sin embargo, esto podría ser específicas para algunos de los servidores de aplicaciones. Yo sólo he probado tomcat, jetty parece comportarse de la misma manera de acuerdo a https://stackoverflow.com/a/11434646/957103.

  10. -1

    Primero de todo, no debemos leer los parámetros dentro del filtro. Generalmente los encabezados de leer en el filtro para hacer algunas tareas de autenticación. Habiendo dicho que uno puede leer el HttpRequest cuerpo completamente en el Filtro o Interceptor mediante el CharStreams:

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

    Esto no afectará la lee en absoluto.

    • sí, así es. Si haces esto una vez, request.getReader() devolverá un lector que sólo contiene una cadena vacía en las lecturas posteriores.
    • Me gustaría trabajar en el caso de sobrescribir getReader() y getInputStream() los métodos para el uso de este nuevo cuerpo como fuente.
  11. -1

    puede utilizar servlet cadena de filtros, pero en lugar de utilizar el original, usted puede crear su propia petición yourownrequests se extiende HttpServletRequestWrapper.

    • Parece que el enlace a un tutorial contiene un virus ahora.

Dejar respuesta

Please enter your comment!
Please enter your name here