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 "fileupload=@filename.txt 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.

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

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


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

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


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

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.


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

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

1
¿Qué es PortalUtil?
Ferry Kranenburg

Apuesto a que esto es de Liferay, API específica de Liferay
mvmn
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.