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

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

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

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


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

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


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

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.


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

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

1
co to jest PortalUtil?
Prom Kranenburg

Założę się, że pochodzi z Liferay, API specyficznego dla Liferay
mvmn
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.