Получить тело запроса POST от HttpServletRequest

Я пытаюсь получить все тело из объекта 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.

12 ответов

В Java 8 вы можете сделать это более простым и понятным способом:

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

Простой способ с обыкновенными.

IOUtils.toString(request.getReader());

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

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

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

Если все, что вам нужно, это тело запроса 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 "";
}

Кредит: /questions/13992476/kak-mne-prochitat-preobrazovat-inputstream-v-stroku-v-java/13992501#13992501

Это работает как для 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 "";
}

Если тело запроса пустое, то это просто означает, что оно уже было использовано заранее. Например, по request.getParameter(), getParameterValues() или же getParameterMap() вызов. Просто удалите строки, выполняющие эти вызовы, из вашего кода.

Самый простой способ, о котором я мог думать:

      request.getReader().lines().reduce("",String::concat)
  • Однако это будет одна длинная строка, которую вам придется разобрать. ЕСЛИ вы отправляете имя пользователя timи пароль от 12345. Вывод кода выше будет выглядеть так:
      {    "username":"tim",    "password": "12345"}

Пожалуйста, будьте осторожны

  • Имейте в виду, что с помощью метода reduce () мы выполняем , которая выполняет большое количество операций копирования строк и имеет время выполнения O(N^2), где N — количество символов. Пожалуйста, ознакомьтесь с документацией изменяемую редукциюMutable Reduction , если вам нужен более производительный результат.

Это будет работать для всех методов HTTP.

открытый класс 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;
}

}

I resolved that situation in this way. I created a util method that return a object extracted from request body, using the readValue method of ObjectMapper that is capable of receiving a 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;
}

Если вы пытаетесь выполнить синтаксический анализ и хотите обработать загрузку файлов, используйте Commons FileUpload - он обрабатывает все ваши разборы, как полей, так и данных файла.

FWIW, код работает для меня как есть, используя любую команду.

Каков весь сервлет и его отображение?

Команды вы опубликовали опечатки? Они не будут работать как есть.

Я лично использую этот код (на сервере разработки, а не на производстве). Кажется, работает. Основная сложность заключается в том, что как только вы прочитаете тело запроса, оно будет потеряно и не будет передано в приложение. Так что вы должны сначала "кэшировать" его.

      /* Export this filter as a jar and place it under directory ".../tomcat/lib" on your Tomcat server/
 In the lib directory, also place the dependencies you need
 (ex. org.apache.commons.io => commons-io-2.8.0.jar)
 
 Once this is done, in order to activate the filter, on the Tomcat server: 
 o in .../tomcat/conf/server.xml, add:
  <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log" suffix=".txt" pattern="%h %l %u %t &quot;%r&quot;  [%{postdata}r] %s %b"/>
  => the server will log the "postdata" attribute we generate in the Java code.
 o in .../tomcat/conf/web.xml, add:
  <filter>
  <filter-name>post-data-dumper-filter</filter-name>
  <filter-class>filters.PostDataDumperFilter</filter-class>
  </filter>
  <filter-mapping>
  <filter-name>post-data-dumper-filter</filter-name>
  <url-pattern>/*</url-pattern>
  </filter-mapping>

Once you've done this, restart your tomcat server. You will get extra infos in file "localhost_access_log.<date>.txt"

*/

package filters;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Enumeration;
import java.util.stream.Collectors;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.commons.io.IOUtils;

class MultiReadHttpServletRequest extends HttpServletRequestWrapper {
    private ByteArrayOutputStream cachedBytes;

    public MultiReadHttpServletRequest(HttpServletRequest request) {
        super(request);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (cachedBytes == null)
            cacheInputStream();

        return new CachedServletInputStream();
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    private void cacheInputStream() throws IOException {
        /* Cache the inputstream in order to read it multiple times.
         */
        cachedBytes = new ByteArrayOutputStream();
        IOUtils.copy(super.getInputStream(), cachedBytes);
    }

    /* An input stream which reads the cached request body */
    public class CachedServletInputStream extends ServletInputStream {
        private ByteArrayInputStream input;

        public CachedServletInputStream() {
            /* create a new input stream from the cached request body */
            input = new ByteArrayInputStream(cachedBytes.toByteArray());
        }
        //---------------------
        @Override
        public int read() throws IOException {
            return input.read();
        }

        @Override
        public boolean isFinished() {
            return input.available() == 0;
        }

        @Override
        public boolean isReady() {
            return true;
        }
        //---------------------
        @Override
        public void setReadListener(ReadListener arg0) {
            // TODO Auto-generated method stub
            // Ex. : throw new RuntimeException("Not implemented");
        }
    }
}

public final class PostDataDumperFilter implements Filter {

    private FilterConfig filterConfig = null;


    public void destroy() {
        this.filterConfig = null;
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (filterConfig == null)
            return;

        StringBuffer output = new StringBuffer();
        output.append("PostDataDumperFilter-");

        /* Wrap the request in order to be able to read its body multiple times */
        MultiReadHttpServletRequest multiReadRequest = new MultiReadHttpServletRequest((HttpServletRequest) request);

        // TODO : test the method in order not to log the body when receiving GET/DELETE requests ?
        // I finally leave it "as it", since I've seen GET requests containing bodies (hell...).
        output.append("Content-type=" + multiReadRequest.getContentType());
        output.append(" - HTTP Method=" + multiReadRequest.getMethod());
        output.append(" - REQUEST BODY = " + multiReadRequest.getReader().lines().collect(Collectors.joining(System.lineSeparator())));


        // Log the request parameters:
        Enumeration names = multiReadRequest.getParameterNames();
        if (names.hasMoreElements()) {
            output.append("- REQUEST PARAMS = ");
        }

        while (names.hasMoreElements()) {
            String name = (String) names.nextElement();
            output.append(name + "=");
            String values[] = multiReadRequest.getParameterValues(name);
            for (int i = 0; i < values.length; i++) {
                if (i > 0) output.append("' ");
                output.append(values[i]);
            }
            if (names.hasMoreElements()) output.append("&");
        }

        multiReadRequest.setAttribute("postdata", output);
        chain.doFilter(multiReadRequest, response);
    }

    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
    }
}

используйте (String) request.getAttribute("body")

Другие вопросы по тегам