HTTP請求中韭山,如果是get請求,那么表單參數(shù)以name=value&name1=value1的形式附到url的后面冷溃,如果是post請求钱磅,那么表單參數(shù)是在請求體中,也是以name=value&name1=value1的形式在請求體中似枕。通過chrome的開發(fā)者工具可以看到如下(這里是可讀的形式盖淡,不是真正的HTTP請求協(xié)議的請求格式):
get請求:
[plain]view plaincopy
RequestURL:http://127.0.0.1:8080/test/test.do?name=mikan&address=street
Request?Method:GET
Status?Code:200?OK
Request?Headers
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip,deflate,sdch
Accept-Language:zh-CN,zh;q=0.8,en;q=0.6
AlexaToolbar-ALX_NS_PH:AlexaToolbar/alxg-3.2
Connection:keep-alive
Cookie:JSESSIONID=74AC93F9F572980B6FC10474CD8EDD8D
Host:127.0.0.1:8080
Referer:http://127.0.0.1:8080/test/index.jsp
User-Agent:Mozilla/5.0?(Windows?NT?6.1)AppleWebKit/537.36?(KHTML,?like?Gecko)?Chrome/33.0.1750.149?Safari/537.36
Query?String?Parameters
name:mikan
address:street
Response?Headers
Content-Length:2
Date:Sun,?11?May?2014?10:42:38?GMT
Server:Apache-Coyote/1.1
Post請求:
[plain]view plaincopy
RequestURL:http://127.0.0.1:8080/test/test.do
Request?Method:POST
Status?Code:200?OK
Request?Headers
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding:gzip,deflate,sdch
Accept-Language:zh-CN,zh;q=0.8,en;q=0.6
AlexaToolbar-ALX_NS_PH:AlexaToolbar/alxg-3.2
Cache-Control:max-age=0
Connection:keep-alive
Content-Length:25
Content-Type:application/x-www-form-urlencoded
Cookie:JSESSIONID=74AC93F9F572980B6FC10474CD8EDD8D
Host:127.0.0.1:8080
Origin:http://127.0.0.1:8080
Referer:http://127.0.0.1:8080/test/index.jsp
User-Agent:Mozilla/5.0?(Windows?NT?6.1)AppleWebKit/537.36?(KHTML,?like?Gecko)?Chrome/33.0.1750.149?Safari/537.36
Form?Data
name:mikan
address:street
Response?Headers
Content-Length:2
Date:Sun,?11?May?2014?11:05:33?GMT
Server:Apache-Coyote/1.1
這里要注意post請求的Content-Type為application/x-www-form-urlencoded,參數(shù)是在請求體中凿歼,即上面請求中的Form Data褪迟。
在servlet中,可以通過request.getParameter(name)的形式來獲取表單參數(shù)答憔。
而如果使用原生AJAX POST請求的話:
[javascript]view plaincopy
functiongetXMLHttpRequest()?{
varxhr;
if(window.ActiveXObject)?{
xhr=newActiveXObject("Microsoft.XMLHTTP");
}elseif(window.XMLHttpRequest)?{
xhr=newXMLHttpRequest();
}else{
xhr=null;
}
returnxhr;
}
functionsave()?{
varxhr?=?getXMLHttpRequest();
xhr.open("post","http://127.0.0.1:8080/test/test.do");
vardata?="name=mikan&address=street...";
xhr.send(data);
xhr.onreadystatechange=function()?{
if(xhr.readyState?==?4?&&?xhr.status?==?200)?{
alert("returned:"+?xhr.responseText);
}
};
}
通過chrome的開發(fā)者工具看到請求頭如下:
[plain]view plaincopy
RequestURL:http://127.0.0.1:8080/test/test.do
Request?Method:POST
Status?Code:200?OK
Request?Headers
Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:zh-CN,zh;q=0.8,en;q=0.6
AlexaToolbar-ALX_NS_PH:AlexaToolbar/alxg-3.2
Connection:keep-alive
Content-Length:28
Content-Type:text/plain;charset=UTF-8
Cookie:JSESSIONID=C40C7823648E952E7C6F7D2E687A0A89
Host:127.0.0.1:8080
Origin:http://127.0.0.1:8080
Referer:http://127.0.0.1:8080/test/index.jsp
User-Agent:Mozilla/5.0?(Windows?NT?6.1)AppleWebKit/537.36?(KHTML,?like?Gecko)?Chrome/33.0.1750.149?Safari/537.36
Request?Payload
name=mikan&address=street
Response?Headers
Content-Length:2
Date:Sun,?11?May?2014?11:49:23?GMT
Server:Apache-Coyote/1.1
注意請求的Content-Type為text/plain;charset=UTF-8味赃,而請求表單參數(shù)在RequestPayload中。
那么servlet中通過request.getParameter(name)卻是空攀唯。為什么呢洁桌?而這樣的參數(shù)又該怎么樣獲取呢?
為了搞明白這個問題侯嘀,查了些資料另凌,也看了Tomcat7.0.53關于請求參數(shù)處理的源碼,終于搞明白了是怎么回事戒幔。
HTTP POST表單請求提交時吠谢,使用的Content-Type是application/x-www-form-urlencoded,而使用原生AJAX的POST請求如果不指定請求頭RequestHeader诗茎,默認使用的Content-Type是text/plain;charset=UTF-8工坊。
由于Tomcat對于Content-Type multipart/form-data(文件上傳)和application/x-www-form-urlencoded(POST請求)做了“特殊處理”。下面來看看相關的處理代碼敢订。
Tomcat的HttpServletRequest類的實現(xiàn)類為org.apache.catalina.connector.Request(實際上是org.apache.coyote.Request)王污,而它對處理請求參數(shù)的方法為protected void parseParameters(),這個方法中對Content-Type multipart/form-data(文件上傳)和application/x-www-form-urlencoded(POST請求)的處理代碼如下:
[java]view plaincopy
protectedvoid?parseParameters()?{
//省略部分代碼......
parameters.handleQueryParameters();//?這里是處理url中的參數(shù)
//省略部分代碼......
if("multipart/form-data".equals(contentType))?{//?這里是處理文件上傳請求
parseParts();
success?=true;
return;
}
if(!("application/x-www-form-urlencoded".equals(contentType)))?{//?這里如果是非POST請求直接返回楚午,不再進行處理
success?=true;
return;
}
//下面的代碼才是處理POST請求參數(shù)
//省略部分代碼......
try{
if(readPostBody(formData,?len)!=?len)?{//?讀取請求體數(shù)據(jù)
return;
}
}catch(IOException?e)?{
//?Client?disconnect
if(context.getLogger().isDebugEnabled())?{
context.getLogger().debug(
sm.getString("coyoteRequest.parseParameters"),e);
}
return;
}
parameters.processParameters(formData,0,?len);//?處理POST請求參數(shù)昭齐,把它放到requestparameter?map中(即request.getParameterMap獲取到的Map,request.getParameter(name)也是從這個Map中獲取的)
//?省略部分代碼......
}
protectedintreadPostBody(bytebody[],intlen)
throwsIOException?{
intoffset?=0;
do{
intinputLen?=?getStream().read(body,?offset,?len?-?offset);
if(inputLen?<=0)?{
returnoffset;
}
offset?+=?inputLen;
}while((len?-?offset)?>0);
returnlen;
}
從上面代碼可以看出矾柜,Content-Type不是application/x-www-form-urlencoded的POST請求是不會讀取請求體數(shù)據(jù)和進行相應的參數(shù)處理的阱驾,即不會解析表單數(shù)據(jù)來放到request parameter map中就谜。所以通過request.getParameter(name)是獲取不到的。
那么這樣提交的參數(shù)我們該怎么獲取呢里覆?
當然是使用最原始的方式丧荐,讀取輸入流來獲取了,如下所示:
[java]view plaincopy
privateString?getRequestPayload(HttpServletRequest?req)?{
StringBuildersb?=newStringBuilder();
try(BufferedReaderreader?=?req.getReader();)?{
char[]buff?=newchar[1024];
intlen;
while((len?=?reader.read(buff))?!=?-1)?{
sb.append(buff,0,?len);
}
}catch(IOException?e)?{
e.printStackTrace();
}
returnsb.toString();
}
當然喧枷,設置了application/x-www-form-urlencoded的POST請求也可以通過這種方式來獲取虹统。
所以,在使用原生AJAX POST請求時割去,需要明確設置Request Header窟却,即:
[javascript]view plaincopy
xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
另外,如果使用jQuery呻逆,我使用1.11.0這個版本來測試,$.ajax post請求是不需要明確設置這個請求頭的菩帝,其他版本的本人沒有親自測試過咖城。相信在1.11.0之后的版本也是不需要設置的。不過之前有的就不一定了呼奢。這個沒有測試過宜雀。
2015-04-17后記:
最近在看書時才真正搞明白,服務器為什么會對表單提交和文件上傳做特殊處理握础,因為表單提交數(shù)據(jù)是名值對的方式辐董,且Content-Type為application/x-www-form-urlencoded,而文件上傳服務器需要特殊處理禀综,普通的post請求(Content-Type不是application/x-www-form-urlencoded)數(shù)據(jù)格式不固定简烘,不一定是名值對的方式,所以服務器無法知道具體的處理方式定枷,所以只能通過獲取原始數(shù)據(jù)流的方式來進行解析孤澎。
jquery在執(zhí)行post請求時,會設置Content-Type為application/x-www-form-urlencoded欠窒,所以服務器能夠正確解析覆旭,而使用原生ajax請求時,如果不顯示的設置Content-Type岖妄,那么默認是text/plain型将,這時服務器就不知道怎么解析數(shù)據(jù)了,所以才只能通過獲取原始數(shù)據(jù)流的方式來進行解析請求數(shù)據(jù)荐虐。