Получить тело запроса 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уже было вызвано.
Нихилу Sahu
1
Как мы можем снова вернуть заново отформатированные данные HTTP POST в запрос?
Pra_A
1
Я пробовал это решение, но оно создало очень серьезную проблему: я использовал этот код для регистрации информации о запросе внутри фильтра oncePerRequest, и когда я его использовал, вся моя привязка @modelAttribute во всех моих методах публикации давала null во всех полях объект. Я не рекомендую использовать этот подход.
Мохаммед Фатхи
Не закрывать ли читателя?
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 () не вызывается раньше, потому что, если вы вызываете его дважды или более раз, он возвращает только полезную нагрузку первого.
Дэни
Привет @ Дани, вот почему это не работает. Читалка пуста. Я думаю, что RestController читает его, прежде чем вы сможете это сделать в любой конечной точке. Самый простой способ получить тело - использовать HttpEntity.
Дэвид Доминго
31

Имейте в виду, что ваш код довольно шумный. Я знаю, что эта ветка старая, но многие люди все равно ее прочитают. Вы можете сделать то же самое, используя библиотеку guava :

    if ("POST".equalsIgnoreCase(request.getMethod())) {
        test = CharStreams.toString(request.getReader());
    }
afx
источник
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

vegemite4me
источник
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()вызова. Просто удалите из кода строки, выполняющие эти вызовы.

BalusC
источник
Это работает, только если это не загрузка файла, как в curlпримере, нет?
Дэйв Ньютон
1
Я пробовал эту штуку. Но я все еще не получаю тело POST. Я, должно быть, что-то упускаю. Просто добавлю: я использую Tapestry для разработки.
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, API для Liferay
mvmn