Obtenha o corpo da solicitação POST de HttpServletRequest

114

Estou tentando obter o corpo inteiro do objeto HttpServletRequest.

O código que estou seguindo é assim:

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

e estou testando a funcionalidade com curl e wget da seguinte maneira:

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"

Mas o while ( (bytesRead = bufferedReader.read(charBuffer)) != -1 )não retorna nada e, portanto, não recebo nada anexado a StringBuffer.

Patel Bhavin
fonte

Respostas:

191

No Java 8, você pode fazer isso de uma maneira mais simples e limpa:

if ("POST".equalsIgnoreCase(request.getMethod())) 
{
   test = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
}
abhi
fonte
7
Prefiro essa solução por ser um Java puro sem nenhuma dependência de terceiros.
lu_ko
49
Porém, tenha em mente que não podemos ler o corpo da solicitação novamente, pois getReaderjá foi chamado.
Nikhil Sahu
1
Como podemos restaurar os dados HTTP POST recém-formatados de volta à solicitação?
Pra_A
1
Eu tentei essa solução, mas ela apresentou um problema muito sério, usei esse código para registrar informações de solicitação dentro de um filtro oncePerRequest e, quando o usei, todas as minhas ligações @modelAttribute em todos os meus métodos de postagem deram null em todos os campos de um objeto .Eu não recomendo usar esta abordagem.
Mohammed Fathi
Não deveríamos fechar o leitor?
aristo_sh
46

Maneira fácil com o commons-io.

IOUtils.toString(request.getReader());

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

Dani
fonte
Eu ajudaria se você pudesse dar um exemplo de como seria a saída do leitor (ou seja, mostrar as chaves ou não)
ArthurG
Isso funcionou para mim. Posso confirmar que essa solução também evita um cenário comum em que bufferedreader.readLine () "trava" sem motivo aparente
Naren
Olá @DavidDomingo, funciona 100%, posso ler que você está comentando o mesmo na resposta acima (que também funciona). Verifique se em algum lugar do seu código (provavelmente os filtros), ou talvez porque alguém do Spring, o método getReader () não foi chamado antes, porque se você chamá-lo duas ou mais vezes, ele retornará a carga útil apenas na primeira.
Dani
Olá @Dani, é por isso que não funciona. O leitor está vazio. Acho que o RestController lê antes que você seja capaz de fazer em qualquer endpoint. A maneira mais fácil de obter o corpo é usando HttpEntity.
David Domingo
31

Esteja ciente de que seu código é muito barulhento. Eu sei que o tópico é antigo, mas muitas pessoas irão ler de qualquer maneira. Você poderia fazer a mesma coisa usando a biblioteca de goiaba com:

    if ("POST".equalsIgnoreCase(request.getMethod())) {
        test = CharStreams.toString(request.getReader());
    }
afx
fonte
3
Talvez considereif(RequestMethod.POST.name().equalsIgnoreCase(...)) { ... }
Madbreaks
Eu recebo o java.lang.IllegalStateException: getReader () já foi chamado para esta solicitação
Pra_A
18

Se tudo o que você deseja é o corpo da solicitação POST, você pode usar um 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 para: https://stackoverflow.com/a/5445161/1389219

vegemite4me
fonte
1
Leve em consideração que request.getInputStream()isso não respeita a codificação de caracteres da solicitação como request.getReader()faz. 1 para o link embora.
Vadzim
qual deve ser o equivalente para o método PUT?
devanathan
10

Isso funciona para GET e 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
fonte
9

Se o corpo da solicitação estiver vazio, isso significa simplesmente que já foi consumido de antemão. Por exemplo, por um request.getParameter(), getParameterValues()ou getParameterMap()chamada. Apenas remova as linhas que fazem essas chamadas de seu código.

BalusC
fonte
Isso só funciona se não for um upload de arquivo, como é o curlexemplo, não?
Dave Newton
1
Eu tentei isso. Mas ainda não estou recebendo o corpo do POST. Eu devo estar esquecendo alguma coisa. Só para acrescentar: estou usando Tapestry para o desenvolvimento.
patel bhavin
3

Isso funcionará para todos os 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
fonte
0

Resolvi essa situação desta forma. Criei um método utilitário que retorna um objeto extraído do corpo da solicitação, usando o método readValue do ObjectMapper que é capaz de receber um 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
fonte
1
o que é PortalUtil?
Ferry Kranenburg
Aposto que é do Liferay, API específica do Liferay
mvmn