Pobierz treść żądania POST z HttpServletRequest

115

Próbuję uzyskać całe ciało z obiektu HttpServletRequest.

Poniższy kod wygląda następująco:

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

i testuję funkcjonalność z curl i wget w następujący sposób:

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"

Ale while ( (bytesRead = bufferedReader.read(charBuffer)) != -1 )nic nie zwraca, więc nic nie dostaję na StringBuffer.

patel bhavin
źródło

Odpowiedzi:

192

W Javie 8 możesz to zrobić w prostszy i bardziej przejrzysty sposób:

if ("POST".equalsIgnoreCase(request.getMethod())) 
{
   test = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
}
abhi
źródło
8
Wolę to rozwiązanie, ponieważ jest to czysta Java bez żadnych zależności od stron trzecich.
lu_ko,
49
Pamiętaj jednak, że nie możemy ponownie odczytać treści prośby, jak getReaderjuż zostało wezwane.
Nikhil Sahu
1
Jak możemy ponownie ustawić nowo sformatowane dane HTTP POST z powrotem na żądanie?
Pra_A
1
Wypróbowałem to rozwiązanie, ale wprowadziło to bardzo poważny preambuł, użyłem tego kodu do zalogowania informacji o żądaniu w filtrze OncePerRequest, a kiedy go użyłem, wszystkie moje powiązania @modelAttribute we wszystkich moich metodach postu dały null we wszystkich polach obiekt. Nie polecam stosowania tego podejścia.
Mohammed Fathi
Czy nie powinniśmy zamykać czytnika?
aristo_sh
46

Łatwy sposób z commons-io.

IOUtils.toString(request.getReader());

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

Dani
źródło
Pomogłbym, gdybyś mógł podać przykład, jak wyglądałoby wyjście czytnika (tj. Pokaż klucze lub nie)
ArthurG,
To zadziałało dla mnie. Mogę potwierdzić, że to rozwiązanie pozwala również uniknąć typowego scenariusza, w którym bufferedreader.readLine () „zawiesza się” bez wyraźnego powodu
Naren,
Cześć @DavidDomingo, działa w 100%, czytam, że komentujesz to samo w odpowiedzi powyżej tej (która również działa). Sprawdź, czy gdzieś w swoim kodzie (prawdopodobnie filtruje), a może dlatego, że ktoś przez Spring nie wywołuje wcześniej metody getReader (), ponieważ jeśli wywołasz ją dwa razy lub więcej razy, zwróci ona tylko pierwszy ładunek.
Dani,
Cześć @Dani, dlatego to nie działa. Czytnik jest pusty. Myślę, że RestController czyta go, zanim będziesz mógł to zrobić w dowolnym punkcie końcowym. Najłatwiejszym sposobem uzyskania treści jest użycie HttpEntity.
David Domingo
31

Pamiętaj, że Twój kod jest dość hałaśliwy. Wiem, że wątek jest stary, ale wiele osób i tak go przeczyta. Możesz zrobić to samo używając biblioteki guawy z:

    if ("POST".equalsIgnoreCase(request.getMethod())) {
        test = CharStreams.toString(request.getReader());
    }
afx
źródło
3
Może rozważif(RequestMethod.POST.name().equalsIgnoreCase(...)) { ... }
Madbreaks
Otrzymuję wyjątek java.lang.IllegalStateException: getReader () został już wywołany dla tego żądania
Pra_A
18

Jeśli wszystko, czego chcesz, to treść żądania POST, możesz użyć takiej metody:

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

Kredyt na: https://stackoverflow.com/a/5445161/1389219

vegemite4me
źródło
1
Weź pod uwagę, że request.getInputStream()nie honoruje to tak jak kodowanie znaków żądania request.getReader(). Jednak +1 dla linku.
Vadzim
jaki powinien być odpowiednik metody PUT?
devanathan
10

Działa to zarówno w przypadku GET, jak i 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
źródło
9

Jeśli treść żądania jest pusta, oznacza to po prostu, że została już wcześniej zużyta. Na przykład, przez request.getParameter(), getParameterValues()lub getParameterMap()połączenia. Po prostu usuń wiersze wywołujące te wywołania z kodu.

BalusC
źródło
Działa to tylko wtedy, gdy nie jest to przesyłanie pliku, jak w curlprzykładzie, nie?
Dave Newton,
1
Próbowałem tego. Ale nadal nie otrzymuję treści POST. Muszę czegoś przegapić. Jeszcze tylko: używam Tapestry do tworzenia.
patel bhavin
3

To zadziała dla wszystkich metod 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
źródło
0

W ten sposób rozwiązałem tę sytuację. Stworzyłem metodę util, która zwraca obiekt wyodrębniony z treści żądania, używając metody readValue ObjectMapper, która może odbierać czytnik.

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
źródło
1
co to jest PortalUtil?
Prom Kranenburg
Założę się, że pochodzi z Liferay, API specyficznego dla Liferay
mvmn