Obtenga el cuerpo de la solicitud POST de HttpServletRequest

115

Estoy tratando de obtener todo el cuerpo del objeto HttpServletRequest.

El código que estoy siguiendo se ve así:

if ( request.getMethod().equals("POST") )
{
    StringBuffer sb = new StringBuffer();
    BufferedReader bufferedReader = null;
    String content = "";

    try {
        //InputStream inputStream = request.getInputStream();
        //inputStream.available();
        //if (inputStream != null) {
        bufferedReader =  request.getReader() ; //new BufferedReader(new InputStreamReader(inputStream));
        char[] charBuffer = new char[128];
        int bytesRead;
        while ( (bytesRead = bufferedReader.read(charBuffer)) != -1 ) {
            sb.append(charBuffer, 0, bytesRead);
        }
        //} else {
        //        sb.append("");
        //}

    } catch (IOException ex) {
        throw ex;
    } finally {
        if (bufferedReader != null) {
            try {
                bufferedReader.close();
            } catch (IOException ex) {
                throw ex;
            }
        }
    }

    test = sb.toString();
}

y estoy probando la funcionalidad con curl y wget de la siguiente manera:

curl --header "MD5: abcd" -F "[email protected] http://localhost:8080/abcd.html"

wget --header="MD5: abcd" --post-data='{"imei":"351553012623446","hni":"310150","wdp":false}' http://localhost:8080/abcd.html"

Pero while ( (bytesRead = bufferedReader.read(charBuffer)) != -1 )no devuelve nada, por lo que no obtengo nada agregado en StringBuffer.

patel bhavin
fuente

Respuestas:

192

En Java 8, puedes hacerlo de una manera más simple y limpia:

if ("POST".equalsIgnoreCase(request.getMethod())) 
{
   test = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
}
abhi
fuente
8
Prefiero esta solución debido a que es un Java puro sin ninguna dependencia de terceros.
lu_ko
49
Aunque tenga en cuenta que no podemos volver a leer el cuerpo de la solicitud como getReaderya se ha llamado.
Nikhil Sahu
1
¿Cómo podemos volver a configurar los datos HTTP POST recién formateados en la solicitud?
Pra_A
1
Probé esta solución pero introdujo un problema muy grave, utilicé este código para registrar información de solicitud dentro de un filtro oncePerRequest, y cuando lo usé, todos mis enlaces @modelAttribute en todos mis métodos de publicación dieron nulo en todos los campos de un objeto. No recomiendo usar este enfoque.
Mohammed Fathi
¿No deberíamos cerrar al lector?
aristo_sh
46

Manera fácil con commons-io.

IOUtils.toString(request.getReader());

https://commons.apache.org/proper/commons-io/javadocs/api-2.5/org/apache/commons/io/IOUtils.html

Dani
fuente
Ayudaría si pudiera dar un ejemplo de cómo se vería la salida del lector (es decir, mostrar claves o no)
ArthurG
Esto funcionó para mí. Puedo confirmar que esta solución también evita un escenario común donde bufferreader.readLine () "cuelga" sin razón aparente
Naren
Hola @DavidDomingo, funciona al 100%, puedo leer que estás comentando lo mismo en la respuesta anterior a esta (que también funciona). Verifique que en algún lugar de su código (filtros probablemente), o tal vez porque alguien de Spring, el método getReader () no se llama antes, porque si lo llama dos o más veces, solo devuelve la carga útil la primera.
Dani
Hola @Dani, por eso no funciona. El lector está vacío. Creo que RestController lo lee antes de que pueda hacerlo en cualquier punto final. La forma más sencilla de obtener el cuerpo es mediante HttpEntity.
David Domingo
31

Tenga en cuenta que su código es bastante ruidoso. Sé que el hilo es antiguo, pero mucha gente lo leerá de todos modos. Puedes hacer lo mismo usando la biblioteca guava con:

    if ("POST".equalsIgnoreCase(request.getMethod())) {
        test = CharStreams.toString(request.getReader());
    }
afx
fuente
3
Quizás considereif(RequestMethod.POST.name().equalsIgnoreCase(...)) { ... }
Madbreaks
Recibo la excepción java.lang.IllegalStateException: getReader () ya ha sido llamado para esta solicitud
Pra_A
18

Si todo lo que desea es el cuerpo de la solicitud POST, puede usar un método como este:

static String extractPostRequestBody(HttpServletRequest request) throws IOException {
    if ("POST".equalsIgnoreCase(request.getMethod())) {
        Scanner s = new Scanner(request.getInputStream(), "UTF-8").useDelimiter("\\A");
        return s.hasNext() ? s.next() : "";
    }
    return "";
}

Crédito a: https://stackoverflow.com/a/5445161/1389219

vegemite4me
fuente
1
Tenga en cuenta que request.getInputStream()no respeta la codificación de caracteres de solicitud como lo request.getReader()hace. Sin embargo, +1 para el enlace.
Vadzim
¿Cuál debería ser el equivalente del método PUT?
devanathan
10

Esto funciona tanto para GET como para POST:

@Context
private HttpServletRequest httpRequest;


private void printRequest(HttpServletRequest httpRequest) {
    System.out.println(" \n\n Headers");

    Enumeration headerNames = httpRequest.getHeaderNames();
    while(headerNames.hasMoreElements()) {
        String headerName = (String)headerNames.nextElement();
        System.out.println(headerName + " = " + httpRequest.getHeader(headerName));
    }

    System.out.println("\n\nParameters");

    Enumeration params = httpRequest.getParameterNames();
    while(params.hasMoreElements()){
        String paramName = (String)params.nextElement();
        System.out.println(paramName + " = " + httpRequest.getParameter(paramName));
    }

    System.out.println("\n\n Row data");
    System.out.println(extractPostRequestBody(httpRequest));
}

static String extractPostRequestBody(HttpServletRequest request) {
    if ("POST".equalsIgnoreCase(request.getMethod())) {
        Scanner s = null;
        try {
            s = new Scanner(request.getInputStream(), "UTF-8").useDelimiter("\\A");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return s.hasNext() ? s.next() : "";
    }
    return "";
}
Bruno Lee
fuente
9

Si el cuerpo de la solicitud está vacío, simplemente significa que ya se ha consumido de antemano. Por ejemplo, mediante a request.getParameter(), getParameterValues()o getParameterMap()llamar. Simplemente elimine las líneas que realizan esas llamadas de su código.

BalusC
fuente
Eso solo funciona si no es una carga de archivo, como es el curlejemplo, ¿no?
Dave Newton
1
Probé esta cosa. Pero todavía no obtengo el cuerpo POST. Debo estar perdiendo algo. Solo para agregar: estoy usando Tapestry para el desarrollo.
patel bhavin
3

Esto funcionará para todos los métodos HTTP.

public class HttpRequestWrapper extends HttpServletRequestWrapper {
    private final String body;

    public HttpRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        body = IOUtils.toString(request.getReader());
    }

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

            @Override
            public boolean isFinished() {
                return false;
            }

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

            @Override
            public void setReadListener(ReadListener listener) {
            }

        };
        return servletInputStream;
    }

    public String getBody() {
        return this.body;
    }
}
Bhawna Joshi
fuente
0

Resolví esa situación de esta manera. Creé un método util que devuelve un objeto extraído del cuerpo de la solicitud, usando el método readValue de ObjectMapper que es capaz de recibir un Reader.

public static <T> T getBody(ResourceRequest request, Class<T> class) {
    T objectFromBody = null;
    try {
        ObjectMapper objectMapper = new ObjectMapper();
        HttpServletRequest httpServletRequest = PortalUtil.getHttpServletRequest(request);
        objectFromBody = objectMapper.readValue(httpServletRequest.getReader(), class);
    } catch (IOException ex) {
        log.error("Error message", ex);
    }
    return objectFromBody;
}
Francisco Disalvo
fuente
1
¿Qué es PortalUtil?
Ferry Kranenburg
Apuesto a que esto es de Liferay, API específica de Liferay
mvmn