usar que peticion metodos metodo informatica entre ejemplos ejemplo diferencia cuando java servlets parameters httprequest servlet-filters

java - que - La solicitud de Http Servlet pierde params del cuerpo POST después de leerlo una vez



metodos http ejemplos (10)

Antes que nada, no debemos leer los parámetros dentro del filtro. Por lo general, los encabezados se leen en el filtro para realizar pocas tareas de autenticación. Dicho esto, uno puede leer el cuerpo de HttpRequest por completo en el filtro o el interceptor mediante el uso de CharStreams:

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

Esto no afecta en absoluto las lecturas subsiguientes.

Estoy tratando de acceder a dos parámetros de solicitud HTTP en un filtro Java Servlet, nada nuevo aquí, ¡pero me sorprendí al encontrar que los parámetros ya se habían consumido! Debido a esto, ya no está disponible en la cadena de filtros.

Parece que esto solo ocurre cuando los parámetros vienen en un cuerpo de solicitud POST (un envío de formulario, por ejemplo).

¿Hay alguna forma de leer los parámetros y NO consumirlos?

Hasta ahora, he encontrado solo esta referencia: el filtro de servlets utilizando request.getParameter pierde los datos del formulario .

¡Gracias!


Eche un vistazo (o use) Spring AbstractRequestLoggingFilter


La única forma sería que usted consuma todo el flujo de entrada usted mismo en el filtro, tome lo que quiera de él y luego cree un nuevo InputStream para el contenido que lea y coloque ese InputStream en un ServletRequestWrapper (o HttpServletRequestWrapper).

La desventaja es que tendrá que analizar la carga útil usted mismo, el estándar no pone esa capacidad a su disposición.

Addenda -

Como dije, necesitas mirar HttpServletRequestWrapper.

En un filtro, continúe llamando a FilterChain.doFilter (solicitud, respuesta).

Para los filtros triviales, la solicitud y la respuesta son las mismas que las ingresadas en el filtro. Ese no tiene que ser el caso. Puede reemplazarlos con sus propias solicitudes y / o respuestas.

HttpServletRequestWrapper está específicamente diseñado para facilitar esto. Le pasa la solicitud original y luego puede interceptar todas las llamadas. Usted crea su propia subclase y reemplaza el método getInputStream con uno propio. No puede cambiar la secuencia de entrada de la solicitud original, por lo tanto, tiene esta envoltura y devuelve su propia corriente de entrada.

El caso más simple es consumir el flujo de entrada de las solicitudes originales en un búfer de bytes, hacer cualquier magia que desee, y luego crear un nuevo ByteArrayInputStream a partir de ese búfer. Esto es lo que se devuelve en su envoltorio, que se pasa al método FilterChain.doFilter.

Tendrá que subclasificar ServletInputStream y crear otro contenedor para su ByteArrayInputStream, pero eso tampoco es gran cosa.


Las respuestas anteriores fueron muy útiles, pero aún así tuve algunos problemas en mi experiencia. En Tomcat 7 servlet 3.0, getParamter y getParamterValues ​​también tuvieron que sobrescribirse. La solución aquí incluye ambos parámetros get-query y post-body. Permite obtener cadena sin procesar fácilmente.

Al igual que las otras soluciones, usa Apache commons-io y Googles Guava.

En esta solución, los métodos getParameter * no arrojan IOException pero usan super.getInputStream () (para obtener el cuerpo) que puede lanzar IOException. Lo atrapo y lanzo 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(); } }


Sé que llego tarde, pero esta pregunta todavía era relevante para mí y esta publicación SO fue uno de los principales éxitos en Google. Voy adelante y publico mi solución con la esperanza de que alguien más pueda ahorrar un par de horas.

En mi caso, necesitaba registrar todas las solicitudes y respuestas con sus cuerpos. Usando Spring Framework la respuesta es bastante simple, solo use 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 } } }


Si tiene control sobre la solicitud, puede establecer el tipo de contenido en binary / octet-stream . Esto permite consultar parámetros sin consumir el flujo de entrada.

Sin embargo, esto podría ser específico para algunos servidores de aplicaciones. Solo probé Tomcat, el embarcadero parece comportarse de la misma manera según https://.com/a/11434646/957103 .


Simplemente sobreescribir getInputStream() no funcionó en mi caso. La implementación de mi servidor parece analizar parámetros sin llamar a este método. No encontré ninguna otra forma, pero también reimplecé los cuatro métodos getParameter *. Aquí está el código de getParameterMap (Apache Http Client y Google Guava library used):

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


Yo también tuve el mismo problema y creo que el código a continuación es más simple y está funcionando 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 la clase filtro 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 tiene alguna pregunta


puede usar la cadena de filtros servlet, pero en su lugar use la original, puede crear su propia solicitud yourownrequests extends HttpServletRequestWrapper.


Como un lado, una forma alternativa de resolver este problema es no usar la cadena de filtro y en su lugar construir su propio componente de interceptor, quizás usando aspectos, que pueden operar en el cuerpo de la solicitud analizada. También es probable que sea más eficiente ya que solo está convirtiendo la solicitud InputStream en su propio objeto modelo una vez.

Sin embargo, todavía creo que es razonable querer leer el cuerpo de la solicitud más de una vez, especialmente a medida que la solicitud se mueve a través de la cadena de filtros. Normalmente usaría cadenas de filtros para ciertas operaciones que quiero mantener en la capa HTTP, desacopladas de los componentes del servicio.

Como lo sugirió Will Hartung, logré esto extendiendo HttpServletRequestWrapper , consumiendo la petición InputStream y esencialmente almacenando en caché 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 la solicitud se puede leer más de una vez envolviendo la solicitud original antes de pasarla a través de la cadena de filtro:

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 le permitirá leer el cuerpo de la solicitud varias veces a través de los métodos getParameterXXX porque la llamada subyacente es getInputStream() , que por supuesto leerá la solicitud en caché InputStream .