在Java servlet中获取POST请求的请求负载

134

我有一个JavaScript库,它正在向我的Java servlet发送POST请求,但在doPost方法中,我似乎无法获取请求负载的内容。在Chrome开发者工具中,所有内容都在标题选项卡的请求负载部分中,内容都在那里,我知道POST已被doPost方法接收,但它只是空白。

对于HttpServletRequest对象,我该如何获取请求负载中的数据?

public class TestFilter implements Filter {


你需要指定哪个参数,例如如果你在正文中有关键词,请使用String keyword = request.getParameter("keyword"); - justMe
看到 JavaScript 代码发送请求会很有趣。显然,它以错误的方式组合了请求参数。 - BalusC
@Razh 嗯,我知道,我只是在说明我正在尝试哪些方法。 BalusC 我正在使用resumable.js库来处理分割文件上传。 - Fasih Awan
4
如果我没有弄错的话,重要的是在从输入流中读取之前不要使用request.getParameter(),否则将没有可用数据(已经被读取)。 - Jeach
9个回答

128

简单回答:
使用getReader()来读取请求体

更多信息:
有两种方法可以读取请求体:

  1. getReader() 返回一个BufferedReader,它允许您读取请求体。

  2. getInputStream()如果需要读取二进制数据则返回一个ServletInputStream

文档中的注意事项:"[两种方法] 都可以用来读取请求体,但不要同时使用。"


4
没问题,只是有点隐蔽。 - davidfrancis
30
如果您在过滤器中使用了此内容,然后发现其他程序无法再次读取正文,则本帖子可能会有所帮助!http://natch3z.blogspot.co.uk/2009/01/read-request-body-in-filter.html - JonnyRaa
3
@SgtPooki,请随意为您的评论添加一些理由,先生! - davidfrancis
@SgtPooki 正确,因为这些东西都不需要。我认为所需方法的名称有些晦涩,因此方法名称就是这个问题的答案。就是这么简单,我想。如果您认为更长的答案会有用,请随意提交。 - davidfrancis
@davidfrancis,你没有理解问题的关键。实际问题是“如何从请求中获取数据”。现在请尝试使用您目前的答案编写req.getReader()来获取数据。这样做会将请求有效负载中的数据提供给您吗?还是它只是提供了一个读取器,以便获取请求正文的内容? - SgtPooki
显示剩余3条评论

85
String payloadRequest = getBody(request);

使用此方法

public static String getBody(HttpServletRequest request) throws IOException {

    String body = null;
    StringBuilder stringBuilder = new StringBuilder();
    BufferedReader bufferedReader = null;

    try {
        InputStream inputStream = request.getInputStream();
        if (inputStream != null) {
            bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            char[] charBuffer = new char[128];
            int bytesRead = -1;
            while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                stringBuilder.append(charBuffer, 0, bytesRead);
            }
        } else {
            stringBuilder.append("");
        }
    } catch (IOException ex) {
        throw ex;
    } finally {
        if (bufferedReader != null) {
            try {
                bufferedReader.close();
            } catch (IOException ex) {
                throw ex;
            }
        }
    }

    body = stringBuilder.toString();
    return body;
}

7
请注意,request.getInputStream() 不像 request.getReader() 一样遵循请求字符编码。因此,此示例使用默认系统字符集。 - Vadzim
14
new BufferedReader(new InputStreamReader(request.getInputStream())) 可以简化为 request.getReader(),后者已经缓冲且保留了请求编码。 - Vadzim
3
当我调用getReader()时,由于读取器已经打开,导致抛出javax.servlet.ServletException: java.lang.IllegalStateException: getInputStream() has already been called for this request异常。我发现这个解决方案很有帮助。 - Benjamin Slabbert
1
为什么要将空字符串附加到SB? - Li3ro
公共静态字符串 getBody(HttpServletRequest request){ 返回新的字符串(request.getInputStream().readAllBytes()); } - Ilya
显示剩余2条评论

58
你可以使用 request 的 Buffer Reader 来读取数据。
    // Read from request
    StringBuilder buffer = new StringBuilder();
    BufferedReader reader = request.getReader();
    String line;
    while ((line = reader.readLine()) != null) {
        buffer.append(line);
        buffer.append(System.lineSeparator());
    }
    String data = buffer.toString()

44

Java 8流

String body = request.getReader().lines()
    .reduce("", (accumulator, actual) -> accumulator + actual);

这很有趣,但在字符串连接方面看起来相当低效!有什么方法可以改进吗? - davidfrancis
13
String body = request.getReader().lines().collect(Collectors.joining()); 这也可以工作。Collectors.joining在底层使用StringBuilder。 - Oliver Kohll
3
我认为应该使用request.getReader().lines().collect(joining("\n"))来保留换行符。 - Michael Böckling
在Java 8中,流将被并行处理,因此需要添加sequential方法,否则它将合并错误的数据部分 - String body = request.getReader().lines().sequential().reduce(System.lineSeparator(),(accumulator, actual) -> accumulator + actual) - Jatin Bodarya
2
你可以用String::concat替换(accumulator, actual) -> accumulator + actual - shmosel

38

使用Apache Commons IO,您可以在一行代码中实现此操作。

IOUtils.toString(request.getReader())

24

如果Java 8中请求正文内容是字符串,可以这样操作:

String body = request.getReader().lines().collect(Collectors.joining());


7
如果您能以JSON格式发送有效载荷,这是阅读有效载荷的最方便的方法:
示例数据类:
public class Person {
    String firstName;
    String lastName;
    // Getters and setters ...
}

示例负载(请求正文):

{ "firstName" : "John", "lastName" : "Doe" }

读取Servlet中的有效载荷代码(需要com.google.gson.*):

Person person = new Gson().fromJson(request.getReader(), Person.class);

就是这样。简单易懂且干净利落。不要忘记将 content-type 标头设置为 application/json。


这在我实现JWT身份验证的情况下起作用。我发送了带有用户名和密码的JSON有效载荷,然后将其映射到我的自定义Auth Token类。 - KnockingHeads

2

使用Java 8的资源管理器:

    StringBuilder stringBuilder = new StringBuilder();
    try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(request.getInputStream()))) {
        char[] charBuffer = new char[1024];
        int bytesRead;
        while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
            stringBuilder.append(charBuffer, 0, bytesRead);
        }
    }

1
您只需要

request.getParameterMap()

用于获取POST和GET参数。

该方法返回一个Map<String,String[]>

您可以通过此Map读取参数。

Map<String, String[]> map = request.getParameterMap();
//Reading the Map
//Works for GET && POST Method
for(String paramName:map.keySet()) {
    String[] paramValues = map.get(paramName);

    //Get Values of Param Name
    for(String valueOfParam:paramValues) {
        //Output the Values
        System.out.println("Value of Param with Name "+paramName+": "+valueOfParam);
    }
}

5
注意:这些参数只有在使用编码为 application/x-www-form-urlencoded 时才可用;如果使用 multipart/form-data,则需要通过 request.getReader() 访问请求正文并手动解析它。 - twonkeys

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接