servlet 获取 post body 体 (用流读取为空的问题)



  目前基于rest风格的很多API开始使用通过body data来传输来代替之前的key-value传输方式。在java servlet或者springmvc中可以通过如下代码来获取并图片通过流方式传输的数据:

 

1 InputStream is= null; String contentStr=""
2 try {
3      is = request.getInputStream();
4           contentStr= IOUtils.toString(is, "utf-8");
5    catch (IOException e) {
6       e.printStackTrace(); 
7   }


 

 

单纯的如上代码可以获取通过POST或PUT等方法传输的数据,但有时候也有例,如果你现在有如下请求的时候:

 

POST http://127.0.0.1:8085/test?id=12564564654

POST data:

01001512a101a203a303a

 

可以看到这里不但会通过post data流的方式发送数据,同时也通过key-value发送了数据,那现在的代码是什么样的呢?如下:

01 @RequestMapping(value = "posttest", method = RequestMethod.POST) 
02 @ResponseBody 
03 public String test(HttpServletRequest request,@RequestParam(value = "id") String id ) {
04      InputStream is= null;     
05      String contentStr="";     
06      try {         
07          is = request.getInputStream();         
08          contentStr= IOUtils.toString(is, "utf-8");     
09          catch (IOException e) { 
10                  e.printStackTrace();     
11          }     
12          return contentStr; 
13 }


 

通过测试你会发现contentStr中并未获取到任何值,同时也不会报任务错误,这是怎么回事呢?会不会是springmvc的问题?答案是否定的。这个时候获取不到postdata的数据与springmvc没有任务关系,同样的把上面的代码进行修改,如下:


01 @RequestMapping(value = "posttest", method = RequestMethod.POST) 
02 @ResponseBody
03 public String test(HttpServletRequest request) {
04      String id=request.getParameter("id");     
05      InputStream is= null;     
06      String contentStr="";     
07      try 
08              is = request.getInputStream();         
09              contentStr= IOUtils.toString(is, "utf-8");     
10          catch (IOException e) {
11                   e.printStackTrace();     
12          }     
13          return contentStr; 
14 }


 

经过测试getInputStream仍然没有数据。

 

那问题出在哪呢?经过查找终于找到问了问答的原因,答案在servlet规范中:

3.1.1 When Parameters Are Available
The following are the conditions that mustbe met before post form data will be 
populated to the parameter set:
1. The request is an HTTP or HTTPS request.
2.  The HTTP method is POST.
3.  The content type is application/x-www-form-urlencoded.
4.  The servlet has made an initial call of any of the getParameterfamily of methods 
on the request object.
If the conditions are not met and the post form data is not included in the parameter 
set, the post data must still be available to the servlet via the request object’s input 
stream. If the conditions are met, post form data will no longer be available for 
reading directly from the request object’s input stream.

 

经过翻译servlet上面一段规范如下:

根据Servlet规范,如果同时满足下列条件,则请求体(Entity)中的表单数据,将被填充到request的parameter集合中(request.getParameter系列方法可以读取相关数据):
1 这是一个HTTP/HTTPS请求
2 请求方法是POST(querystring无论是否POST都将被设置到parameter中)
3 请求的类型(Content-Type头)是application/x-www-form-urlencoded
4 Servlet调用了getParameter系列方法


如果上述条件没有同时满足,则相关的表单数据不会被设置进request的parameter集合中,相关的数据可以通过request.getInputStream()来访问。反之,如果上述条件均满足,相关的表单数据将不能再通过request.getInputStream()来读取。

 

根据这个规范的说明,当我们在调用request.getParameter(“id”)的方法时,通过post data交的数据(请求体)被填充到了parameter集合中了,所以后面的通过request.getInputStream获取数据流时就为空。通过测试打印断点我们可以看到如下图:

原本我们只提交了一个参数id,但是在执行过request.getParameter(“id”)的方法后,我们的parameterNames里就有了两个参数,其中第二个参数的key就是我们通过body请求体传输的数据,值也是body请求体的post data数据。

 

最终这个问题的解决方案是把string id= request.getParameter(“id”);放到request.getInputStream之后,这样就回避了servlet的规范,代码调整后为:


01 @RequestMapping(value = "posttest", method = RequestMethod.POST) 
02 @ResponseBody
03 public String test(HttpServletRequest request) {
04      InputStream is= null;     
05      String contentStr="";     
06      try {         
07          is = request.getInputStream();         
08          contentStr= IOUtils.toString(is, "utf-8");     
09          catch (IOException e) {
10                   e.printStackTrace();     
11              }     
12        String id=request.getParameter("id");     
13   return contentStr; 
14 }


 

在servlet规范中已经说明是post请求时才会有这个获取post提交数据的顺序问题,而在其它的如put中不会出现这种顺序问题。

  • 6
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 可以使用以下Java代码从HttpServletRequest对象中获取POST请求: ``` // 获取请求长度 int contentLength = request.getContentLength(); // 创建字节数组来保存请求数据 byte[] requestBodyData = new byte[contentLength]; // 读取请求数据到字节数组 ServletInputStream inputStream = request.getInputStream(); int totalBytesRead = 0; while(totalBytesRead < contentLength) { int bytesRead = inputStream.read(requestBodyData, totalBytesRead, contentLength - totalBytesRead); if(bytesRead == -1) { break; } totalBytesRead += bytesRead; } // 将字节数组转换成字符串 String requestBody = new String(requestBodyData, "UTF-8"); ``` 在这个示例代码中,我们首先从HttpServletRequest对象中获取请求长度,然后创建一个字节数组来保存请求数据。接着,我们使用ServletInputStream对象从请求中读取数据,并将其存储在字节数组中。最后,我们将字节数组转换成字符串,以便在后续代码中进行处理。请注意,在这个示例代码中,我们假设请求使用UTF-8编码。如果请求使用其他编码,则需要相应地更改代码。 ### 回答2: 从HttpServletRequest对象获取POST请求可以通过以下步骤完成: 1. 首先,确保添加了javax.servlet-api依赖包到项目中,以便使用HttpServletRequest类。 2. 在Servlet的doPost或doPut方法中,接收HttpServletRequest对象作为方法参数。 3. 使用HttpServletRequest对象的getInputStream方法,获取来自客户端的请求输入流。代码示例如下: ``` InputStream inputStream = request.getInputStream(); ``` 4. 在获取到请求输入流后,我们可以使用IO流的方式来读取POST请求的内容。例如,使用BufferedReader读取输入流内容。代码示例如下: ``` BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); String line; StringBuilder stringBuilder = new StringBuilder(); while ((line = reader.readLine()) != null) { stringBuilder.append(line); } String requestBody = stringBuilder.toString(); ``` 5. 最后,你可以通过requestBody变量来获取POST请求的内容。你可以根据实际情况对请求进行解析、处理或存储。 需要注意的是,如果请求较大或需要多次读取请求内容,可以使用缓冲流、字符流等方式进行读取和处理。 希望以上回答能够帮助到你,如果还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值