从 HttpServletRequest 获取 POST 请求主体

我尝试从 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 上添加任何内容。

432803 次浏览

如果请求体是空的,那么它仅仅意味着它已经被预先使用了。例如,通过 request.getParameter()getParameterValues()getParameterMap()调用。只需从代码中删除执行这些调用的行。

请注意,您的代码相当嘈杂。 我知道这个帖子很老了,但是很多人还是会看的。 您可以使用 番石榴库做同样的事情:

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

提供者: https://stackoverflow.com/a/5445161/1389219

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

在 Java8中,你可以用一种更简单和干净的方式来实现:

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

这将适用于所有 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;
}
}

我用这种方式解决了问题。我创建了一个 util 方法,它使用 ObjectMapper 的 readValue 方法返回从请求体中提取的对象,该方法能够接收 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;
}

我个人使用这段代码(在开发服务器上,而不是在生产环境中)。看起来有用。主要的困难在于,一旦读取了请求主体,它就会丢失,而不会传输到应用程序中。所以你必须先“缓存”它。

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

我能想到的最简单的方法:

request.getReader().lines().reduce("",String::concat)
  • 但是,这将是一个需要解析的长字符串。如果您发送的用户名为 tim和密码为 12345。上面代码的输出如下:
{    "username":"tim",    "password": "12345"}

请注意

  • 请注意,使用 reduce ()方法,我们将执行一个 变异性减少,它执行大量的字符串复制,并且运行时为 O (N ^ 2) ,N 为字符数。如果需要更高性能的结果,请检查 变异性减少文档。