1.起因
在網上找不到能夠將application/x-www-form-urlencoded與multipart/form-data與application/json三者區別完全解釋清楚的文章,真是令人失望,特發此帖詳細解說。
前端數據傳遞至后台時,需要對其進行編碼,其中,編碼格式可分為四種:application/x-www-form-urlencoded,multipart/form-data,application/json,text/plain。
text/plain是純文本數據,這里不做解釋,平常也沒人會使用。
前后台完成數據交互的方式只有兩種:一是form表單提交,二是ajax提交。
form表單可通過enctype屬性設置編碼類型,默認值為:application/x-www-form-urlencoded;ajax可通過contentType屬性設置編碼類型,默認值也是:application/x-www-form-urlencoded;
2.application/x-www-form-urlencoded
后台如何接收?使用request.getParameter("work");來獲取參數名和參數值。
這種編碼格式,是我們最常見的一種方式,將數據封裝成一個字符串,參數名和參數值使用"="拼接,參數之間使用"&"拼接,最終傳遞至后台的數據格式形如:key1=value1&key2=value2&...;
另外,key和value都會分別使用encodeURI()對其進行編碼,也就是你所要傳遞的數據,實際上已經進行了一次編碼,形如:name=Marydon&work=%E7%A8%8B%E5%BA%8F%E5%91%98,服務器接收到后干的第一件事就是使用URLDdecoder.decode()對name和value進行一次解碼。
不管是form表單請求還是ajax請求,都是這樣進行數據組裝的。
get請求與post請求的基本區別:
這是get請求
下圖是post請求
區別在於:
get請求:沒有請求體;會直接將form數據拼接到url中,用?隔開,數據對外可見;
post請求:將數據放到請求體中;無法直觀看到要傳輸的數據,安全性相對高一點。
PS:想要比較兩種請求的區別,在IE瀏覽器下調試最直觀,chrome不行。
這是為我們大眾所熟知的兩個區別,那么問題來了,為何get請求提交的數據攜帶中文時,后台接收到的中文數據會亂碼,而post請求就不會亂碼呢?
再來看一下這兩個請求:
get請求
post請求
后台將請求字符集打印出來
// 該請求的字符集是 System.out.println(request.getCharacterEncoding());
通過前后端測試,這次我們可以看出來get請求與post請求的另一個區別:
當form表單數據編碼類型設置為application/x-www-form-urlencoded時(也就是默認值),瀏覽器向后台發送請求時,分為兩種情況:
當請求方式為get時,請求頭部信息沒有Content-Type屬性,也沒有指定數據的字符集;
當請求方式為post時,請求頭部信息有Content-Type屬性,並指定數據的字符集,即:application/x-www-form-urlencoded; charset=UTF-8;
那么get請求時,所采用的字符集到底是什么?通過后台測試得出結果:ISO-8859-1
// get請求默認字符集 String work = new String(work.getBytes("iso-8859-1"), "utf-8");
那么既然如此,在ajax請求中,我們是不是可以顯式聲明contentType屬性,解決get請求亂碼問題呢?
type : 'get',// 請求方式 contentType : 'application/x-www-form-urlencoded; charset=UTF-8',// 顯式聲明
網頁已經生效
結果卻令人失望:
還是亂碼,還得重新編碼。
Content-Type屬性,告訴服務器提交的數據的字符集是utf-8,並讓服務器以utf-8格式解析數據,由此可見:
問題不在於服務器,而在於瀏覽器:當瀏覽器檢測到編碼格式為application/x-www-form-urlencoded並且為get請求時,瀏覽器會先用ISO-8859-1對form數據進行編碼,然后再用encodeURI()對其進行編碼。
所以,get請求導致后台接收到的中文出現亂碼的根本原因是:頁面字符集是utf-8,瀏覽器卻按iso-8859-1進行重新編碼,后台接收后以utf-8進行解碼當然會出現亂碼。
小結:
當表單數據的編碼格式為:application/x-www-form-urlencoded時:
get請求,瀏覽器會將數據按照iso-8859-1進行重新編碼,導致后台接收到中文時必然亂碼;
post請求,瀏覽器會按照utf-8對要提交的數據進行編碼,由於后台本身就是使用utf-8對其進行解析,所以才不會出現亂碼的情況。
至於get請求解決亂碼問題主要有兩種方式,具體見另一篇文章。
3.multipart/form-data
主要用於傳輸文件,將文件轉換成二進制數據進行傳輸,不涉及轉碼問題。
后台如何接收?使用request.getInputStream();取值。
IE瀏覽器和chrome瀏覽器下,傳輸的form數據是不一樣的;
IE瀏覽器
chrome瀏覽器
共同點是:Content-Type的值為multipart/form-data; boundary=--...,貌似沒有其它的固定格式,不再考慮,后台只要能接收到就行。
后台接收到的數據,形如:
結合代碼進行實現:
前端發送數據
<input type="file" id="file" onchange="upload('getParams')" style="display: none;"> <input type="button" value="上傳" onclick='javascript:$("#file").click()'>
function upload(url) { // js 獲取文件對象 var fileObj = document.getElementById("file").files[0]; if (null == fileObj) { alert("圖像上傳失敗,請重試!"); return; } // 創建form表單 var formFile = new FormData(); // 加入文件對象 formFile.append("file", fileObj); // 創建XMLHttpRequest對象 var xhr = new XMLHttpRequest(); // post方式,url為服務器請求地址,true 該參數規定請求是否異步處理。 xhr.open("post", url, true); //請求完成 xhr.onload = function () { // 將返回數據轉換成JSON對象 var resData = JSON.parse(this.responseText); document.getElementById("file").value = ""; }; // 請求失敗 xhr.onerror = null; // 上傳進度調用方法(可實現上傳進度條) xhr.upload.onprogress = null; // //開始上傳,發送form數據(以二進制數據傳遞給后台) xhr.send(formFile); }
后台接收
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { InputStream is = request.getInputStream(); int i = 0; while ((i = is.read()) != -1) { System.out.print((char) i); } }
4.application/json
后台如何接收?使用request.getReader()取值,也可以使用request.getInputStream()獲取。
錯誤示例:
$.ajax({ type : 'post',// 請求方式 url : 'getParams',// 請求地址 contentType : 'application/json; charset=UTF-8', data :{'name' : 'Marydon','work' : '程序員'}, success : function(result){// 請求成功 alert(result.work); } });
結果提交的數據還是form表單形式,根本不是json
以json格式進行數據的傳輸,從前端發送json形式的方式不常見,常見的是從服務器對服務器,即一台服務器發送json數據,另一台服務器負責接收。
言歸正傳,怎樣才能通過ajax向后台傳輸json數據呢?
首先需要明白的是,前端向后台傳輸數據有且只有兩種方式,一種是常見的字符串格式,另一種就是二進制數據,也就是不管你向后台傳輸什么類型的數據都會被轉換成字符串或者二進制。
這樣,我們就明白了,向后台傳輸,並不是傳遞一個json對象,而是應該傳一個json字符串。
與普通的Ajax調用的區別,僅僅在於:參數名,也就是name需要加上單引號,{}兩邊要加上雙引號,這樣就不需要使用反斜杠\來轉義啦。
代碼實現
$.ajax({ type : 'post',// 請求方式 url : 'getParams',// 請求地址 contentType : 'application/json; charset=UTF-8',// 告知服務器,傳遞的是json數據(可以省略) data :"{'name' : 'Marydon','work' : '程序員'}",// json字符串(必要條件) success : function(result){// 請求成功 alert(result.work); } });
后台接收:有兩種方式
方式一:使用字符流取值-request.getReader()
StringBuffer buffer = new StringBuffer(); BufferedReader reader = request.getReader(); String s = ""; while ((s = reader.readLine()) != null) { buffer.append(s); } System.out.println(JSONObject.fromObject(buffer.toString())); System.out.println(buffer.toString());
方式二:使用字節流取值-request.getInputStream()
int i = 0; StringBuffer buffer = new StringBuffer(); InputStream stream = request.getInputStream(); while ((i = stream.read()) != -1) { buffer.append((char)i); } System.out.println(JSONObject.fromObject(buffer.toString())); System.out.println(buffer.toString());
打印結果
經測試發現,即使Ajax沒有聲明contentType,也就是采用的默認值:application/x-www-form-urlencoded,當你實際傳輸的是json字符串時,后台照樣能夠正常接收。如下圖所示:
雖然后台可以正常接收,但是,為了規范行事,還是聲明上吧。
另外,如你所見,當想后台傳遞json字符串時,就會犯get請求一樣的毛病:當參數值為中文時,后台接收到的將是亂碼數據,需要手動進行轉碼。
也就是,當ajax向后台發送json格式的數據時,即使是post請求,也會造成亂碼問題。
亂碼解決有兩種方式,這里不再贅述,下面文章有詳細解說。
不管是form表單提交還是ajax提交,最常用的編碼格式還是application/x-www-form-urlencoded。