天天看點

關于application/x-www-form-urlencoded編碼

轉http://hongjiang.info/http-application-x-www-form-urlencoded/

同僚遇到在servlet端通過request對象getInputStream讀取POST過來的資料,卻讀不到的問題,懷疑是tomcat的問題。查了一下

Content-type

application/x-www-form-urlencoded

,估計是被解析成了

parameters

,果然在他擷取流之前,有過

request.getParameter

的操作。

熟悉servlet的話,這個問題應該算常識了。它其實跟容器無關,所有的servlet容器都是這樣的行為。幾年前在實作一個網關代理的時候就遇到過這個問題,當時使用的是jetty,發現POST過來的資料讀不到,也是

application/x-www-form-urlencoded

編碼,斷點跟蹤發現是在擷取流之前有過

request.getParameter

,資料會被解析,并且後續資料流不可再被讀取。

在servlet規範3.1.1節裡,對POST資料何時會被當做parameters有描述:

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 getParameter family of methods on the request object.

If the conditions are met, post form data will no longer be available for reading directly from the request object’s input stream.
           

規範裡已經明确的聲明當請求滿足: 1) http/https, 2) POST, 3) Content-type 是

application/x-www-form-urlencoded

, 4) 調用過getParameter方法;則資料會被當做請求的paramaters,而不能再通過 request 的 inputstream 直接讀取。

是以不論tomcat、jetty還是其他servlet容器都遵循這個方式。不過話說回來,為什麼

application/x-www-form-urlencoded

編碼的資料會被當做parameter來解析呢?

使用http上傳資料可以用GET或POST,使用GET的話,隻能通過uri的queryString形式,這會遇到長度的問題,各個浏覽器或server可能對長度支援的不同,是以到要送出的資料如果太長并不适合使用GET送出。

采用POST的話,既可以在uri中帶有queryString也可以将資料放在body中。body内容可以有多種編碼形式,其中

application/x-www-form-urlencoded

編碼其實是基于uri的

percent-encoding

編碼的,是以采用

application/x-www-form-urlencoded

的POST資料和queryString隻是形式不同,本質都是傳遞參數。

在tomcat的Request.parseParameters方法裡,對于

application/x-www-form-urlencoded

是有做判斷的,對這種編碼會去解析body裡的資料,填充到parameters裡,是以後續想再通過流的方式讀取body是讀不到的(除非你沒有觸發過getParameter相關的方法)。

在HTML4之前,表單資料的編碼方式隻有

application/x-www-form-urlencoded

這一種(現在預設也是這種方式),因為早期的時候,web上送出過來的資料也是非常簡單的,基本上以key-value形式為主,是以表單采用

application/x-www-form-urlencoded

這種編碼形式也沒什麼問題。

在HTML4裡又引入了

multipart/form-data

編碼,對于這兩種編碼如何選擇,請參考這裡。