servlet 獲取 post body 體用流讀取為空的問題【轉】


 引用自: http://www.zicheng.net/article/982028.htm

目前基於rest風格的很多API開始使用通過body data來傳輸來代替之前的key-value傳輸方式。在Java servlet或者springmvc中可以通過如下代碼來獲取並圖片通過流方式傳輸的數據:

InputStream is= null; 
String contentStr=""; try {   is = request.getInputStream();   contentStr= IOUtils.toString(is, "utf-8"); } catch (IOException e) {   e.printStackTrace(); }

 



單純的如上代碼可以獲取通過POST或PUT等方法傳輸的數據,但有時候也有例外,如你現在有如下請求的時候:

POST http://127.0.0.1:8085/test?id=12564564654
POST data: 01001512a101a203a303a

 



可以看到這里不但會通過post data流的方式發送數據,同時也通過key-value發送了數據,那現在的代碼是什么樣的呢?如下:

@RequestMapping(value = "posttest", method = RequestMethod.POST)
    @ResponseBody
    public String test(HttpServletRequest request, @RequestParam(value = "id") String id) {
        InputStream is = null;
        String contentStr = "";
        try {
            is = request.getInputStream();
            contentStr = IOUtils.toString(is, "utf-8");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return contentStr;
    }

 


通過測試你會發現contentStr中並未獲取到任何值,同時也不會報任務錯誤,這是怎么回事呢?會不會是springmvc的問題?答案是否定的。

這個時候獲取不到postdata的數據與springmvc沒有任何關系,同樣的把上面的代碼進行修改,如下:

 

@RequestMapping(value = "posttest", method = RequestMethod.POST)
    @ResponseBody
    public String test(HttpServletRequest request) {
        String id = request.getParameter("id");
        InputStream is = null;
        String contentStr = "";
        try {
            is = request.getInputStream();
            contentStr = IOUtils.toString(is, "utf-8");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return contentStr;
    }

 


經過測試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數據。

最終這個問題的解決方案是把request.getInputStream()放到string id= request.getParameter("id");之前,這樣就回避了servlet的規范,代碼調整后為:

 

@RequestMapping(value = "posttest", method = RequestMethod.POST)
    @ResponseBody
    public String test(HttpServletRequest request) {
        InputStream is = null;
        String contentStr = "";
        try {
            is = request.getInputStream();
            contentStr = IOUtils.toString(is, "utf-8");
        } catch (IOException e) {
            e.printStackTrace();
        }
        String id = request.getParameter("id");
        return contentStr;
    }

 


在servlet規范中已經說明是post請求時才會有這個獲取post提交數據的順序問題,而在其它的如put中不會出現這種順序問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM