Отримайте тіло запиту POST від HttpServletRequest


115

Я намагаюся отримати все тіло від об’єкта HttpServletRequest.

Код, який я дотримуюся, виглядає приблизно так:

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

і я тестую функціональність за допомогою curl і wget так:

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"

Але while ( (bytesRead = bufferedReader.read(charBuffer)) != -1 )нічого не повертає, і тому я нічого не додаю на StringBuffer.

Відповіді:


192

У Java 8 це можна зробити більш простим та чистим способом:

if ("POST".equalsIgnoreCase(request.getMethod())) 
{
   test = request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
}

8
Я вважаю за краще це рішення через те, що це чиста Java без сторонніх залежностей.
lu_ko

49
Хоча майте на увазі, що ми не можемо знову прочитати орган запиту, як getReaderуже було викликано.
Нікхіл Саху

1
Як ми можемо знову повернути щойно відформатовані дані HTTP POST на запит?
Pra_A

1
Я спробував це рішення, але він представив дуже серйозну проблему. Я використовував цей код для внесення інформації про запит всередині фільтра OncePerRequest, і коли я використовував його, усі мої прив'язки @modelAttribute у всіх моїх публікаційних методах давали нуль у всіх полях об'єкта. Я не рекомендую використовувати цей підхід.
Мухаммед Фаті

Чи не слід закривати читача?
aristo_sh

46

Простий спосіб за допомогою commons-io.

IOUtils.toString(request.getReader());

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


Я б допоміг, якщо ви можете навести приклад того, як виглядатиме висновок читача (тобто показати клавіші чи ні)
ArthurG

Це працювало для мене. Я можу підтвердити, що це рішення також уникає поширеного сценарію, коли bufferedreader.readLine () "зависає" без видимих ​​причин
Нарен

Привіт @DavidDomingo, це працює на 100%, я можу прочитати, що ви коментуєте те саме у відповіді вище цієї (яка також працює). Переконайтеся, що десь у вашому коді (фільтрується ймовірно), а може тому, що хтось за Spring, метод getReader () не викликається раніше, тому що якщо ви дзвоните йому двічі і більше разів, він повертає лише корисний навантаження першого.
Дані

Привіт @Dani, тому це не працює. Читач порожній. Я думаю, що RestController читає його, перш ніж ви зможете це зробити в будь-якій кінцевій точці. Найпростіший спосіб отримати тіло - за допомогою HttpEntity.
Девід Домінго

31

Будьте в курсі, що ваш код досить галасливий. Я знаю, що нитка стара, але багато людей її все одно прочитають. Можна зробити те ж саме, використовуючи бібліотеку guava з:

    if ("POST".equalsIgnoreCase(request.getMethod())) {
        test = CharStreams.toString(request.getReader());
    }

3
Можливо, врахуйтеif(RequestMethod.POST.name().equalsIgnoreCase(...)) { ... }
Madbreaks

Я отримую java.lang.IllegalStateException: getReader () вже закликав цей запит
Pra_A

18

Якщо все, що вам потрібно, це орган запиту POST, ви можете використовувати такий метод:

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

Кредит: https://stackoverflow.com/a/5445161/1389219


1
Враховуйте те, що request.getInputStream()кодування символів не вимагає кодування запиту request.getReader(). Хоча +1 для посилання.
Вадим,

що має бути еквівалентом методу PUT?
деванафан

10

Це працює як для GET, так і для 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

Якщо тіло запиту порожнє, це просто означає, що воно вже було спожито заздалегідь. Наприклад, з допомогою request.getParameter(), getParameterValues()або getParameterMap()виклику. Просто видаліть з коду лінії, які здійснюють ці дзвінки.


Це працює лише в тому випадку, якщо це не завантаження файлу, як, curlнаприклад, ні?
Дейв Ньютон

1
Я спробував цю річ. Але все одно я не отримую корпус POST. Я, мабуть, чогось не вистачає. Просто додати: Я використовую Гобелен для розробки.
patel bhavin

3

Це буде працювати для всіх методів 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

Я вирішив цю ситуацію таким чином. Я створив метод util, який повертає об'єкт, вилучений з тіла запиту, використовуючи метод readValue ObjectMapper, здатний приймати 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
що таке PortalUtil?
Пором Краненбург

Б'юсь об заклад, що це від Liferay, специфічного для
Liferay
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.