Получить тело запроса 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 "%r" [%{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;
}
}