如何从 Java 中的 HttpServletRequest 检索原始帖子数据

Posted

技术标签:

【中文标题】如何从 Java 中的 HttpServletRequest 检索原始帖子数据【英文标题】:How to retrieve raw post data from HttpServletRequest in java 【发布时间】:2011-06-28 18:22:50 【问题描述】:

我正在尝试用 Java 获取发布数据。似乎它应该是最简单的事情之一,对吗?我的意思是, HttpServletRequest.getParameter 必须做对吗?那么如何获取原始帖子数据呢?

我找到HttpServletRequest get JSON POST data 并使用Kdeveloper 的代码从请求中提取帖子数据。它有效,但有一个问题:我只能获得该帖子数据一次

这是我用 Kdeveloper 的代码制作的方法:

public static String getPostData(HttpServletRequest req) 
    StringBuilder sb = new StringBuilder();
    try 
        BufferedReader reader = req.getReader();
        reader.mark(10000);

        String line;
        do 
            line = reader.readLine();
            sb.append(line).append("\n");
         while (line != null);
        reader.reset();
        // do NOT close the reader here, or you won't be able to get the post data twice
     catch(IOException e) 
        logger.warn("getPostData couldn't.. get the post data", e);  // This has happened if the request's reader is closed    
    

    return sb.toString();

以前我在这个方法结束时关闭了阅读器,但是当方法在同一个请求上运行多次时会导致异常。不关闭它,不会发生异常,但该方法返回一个空字符串。

老实说,应该只公开一个 req.getPostData() 方法 - 没有人认为这会有用吗?

那么我怎样才能编写这个方法,让它总是返回正确的帖子数据呢?

【问题讨论】:

【参考方案1】:

HttpServletRequest#getInputStream() 可以以字节流的形式获取请求正文:

InputStream body = request.getInputStream();
// ...

作为HttpServletRequest#getReader()的字符流:

Reader body = request.getReader();
// ...

请注意,您只能阅读一次。客户端不会多次重新发送相同的请求。调用getParameter() 等也会隐式读取它。如果您以后需要分解参数,则必须将主体存储在某个地方并自己处理。

【讨论】:

所以你的回答是没有没有的方法来做我想做的事?它不是关于客户多次发送它。 HttpServletRequest 清楚地将发布数据存储在内部某处(因为您总是可以多次获取发布参数)。感谢您的回答,我只是想完全理解您是在说“不可能”,还是只是在重申我已经发现的内容。 在第一次调用getParameter() 时,HttpServletRequest 将在内部使用getInputStream() 来读取和解析请求正文(它是来自网络连接的字节流)并将其存储在您可以使用的映射中可以通过getParameterMap()获得。在此之后,您将无法再读取getInputStream()/getReader() 的请求正文,因为它已经被读取了。如果您更清楚地阐明此需求背后的功能需求,那么我们或许可以向您建议更好的方法来实现它。 好吧,您可能想要创建一个HttpServletRequestWrapper,它在ByteArrayInputStream 中保存请求正文的副本。 这是您正在寻找的请求包装器的示例:***.com/questions/1046721/… 感谢您的回答。 “只读一次”...不得不说我对这个设计决定感到非常惊讶。【参考方案2】:

我们遇到过 IE 强制我们以 text/plain 形式发布的情况,因此我们不得不使用 getReader 手动解析参数。 servlet 被用于长轮询,因此当 AsyncContext::dispatch 在延迟后执行时,它实际上是空手重新发布请求。

所以我只是在请求第一次出现时使用 HttpServletRequest::setAttribute 将帖子存储在请求中。 getReader 方法清空缓冲区,其中 getParameter 也清空缓冲区,但自动存储参数。

    String input = null;

    // we have to store the string, which can only be read one time, because when the
    // servlet awakens an AsyncContext, it reposts the request and returns here empty handed
    if ((input = (String) request.getAttribute("com.xp.input")) == null) 
        StringBuilder buffer = new StringBuilder();
        BufferedReader reader = request.getReader();

        String line;
        while((line = reader.readLine()) != null)
            buffer.append(line);
        
        // reqBytes = buffer.toString().getBytes();

        input = buffer.toString();
        request.setAttribute("com.xp.input", input);
    

    if (input == null) 
        response.setContentType("text/plain");
        PrintWriter out = response.getWriter();
        out.print("\"act\":\"fail\",\"msg\":\"invalid\"");
           

【讨论】:

【参考方案3】:

这对我有用:(注意需要 java 8)

String requestData = request.getReader().lines().collect(Collectors.joining());
UserJsonParser u = gson.fromJson(requestData, UserJsonParser.class);

UserJsonParse 是一个展示 gson 如何解析 json 共振峰的类。

类是这样的:

public class UserJsonParser 

    private String username;
    private String name;
    private String lastname;
    private String mail;
    private String pass1;
//then put setters and getters

解析出来的json字符串是这样的:

$jsonData:     "username": "testuser",    "pass1": "clave1234" 

其余值(邮件、姓氏、姓名)设置为空

【讨论】:

我认为这个答案与提出的问题无关

以上是关于如何从 Java 中的 HttpServletRequest 检索原始帖子数据的主要内容,如果未能解决你的问题,请参考以下文章

自己动手用写简单的验证码(JAVA)

java与javascript对cookie操作的工具类

通过 HTML5 视频元素的 Java 多媒体流

#yyds干货盘点#☕并发技术系列「Web请求读取系列」如何构建一个可重复读取的Request的流机制

JSP学习JSP中的九个内置对象

如何从 java 中的组件获取 BufferedImage?