表單提交
表單有兩種提交方式,POST和GET。通常我們會使用POST方式,一是因為形式上的安全 ;二是可以上傳文件。
我之前經常忽略掉表單的編碼類型,覺得它特別長比較難記,而且不設置也似乎不影響什么。表單的編碼類型,用來控制表單中的數據的編碼格式。POST 提交方式 默認 enctype=“application/x-www-form-urlencoded”,數據以鍵值對的方式傳送到服務器,這種方式適合於大多數場景。GET 提交方式,默認none。
表單(POST請求)支持下面兩種編碼:
enctype
application/x-www-form-urlencoded 不指定時默認方式, key1=value1&key2=value2
multipart/form-data 一般用於表單需要文件上傳
表單(GET請求)支持下面兩種編碼:
enctype
application/x-www-form-urlencoded key1=value1&key2=value2
none 不指定時默認方式,getContentType() 返回null
ServletRequest中獲取參數常用方法:
Map<String,String[]> maps= request.getParameterMap(); //獲取所有的鍵值對 Enumeration<String> names= request.getParameterNames();//獲取所有的參數名 String[] values = request.getParameterValues(parameterName); //獲取某個參數下的所有值,適合多選組件 String value = request.getParameter(parameterName); //獲取某個參數對應的值,適合文本組件,單選等 ServletInputStream stream=request.getInputStream();//需要注意的是,該方法只能被調用一次,再次調用返回結果為空 BufferedReader reader=request.getReader();//該方法也是只能調用一次 Collection<Part> parts = request.getParts();// 獲取 multipart/form-data 編碼方式的數據
需要額外注意的是:getInputStream 方法和getReader方法互斥。最多只能調用其中的一個,如果這兩個方法都被調用則會拋出異常。
request.getInputStream();//需要注意的是,該方法只能被調用一次,再次調用返回結果為空 request.getReader();//該方法也是只能調用一次
1.application/x-www-form-urlencoded
- GET方式,會將表單中的數據(鍵值對)經過urlencode編碼后追加到url中。
- POST方式,會將表單中的數據經過urlencode編碼后放在request body 中。
<form action="/xxxx" method="post" enctype="application/x-www-form-urlencoded"> name: <input type="text" name="name"><br> password: <input type="text" name="password"><br> <input type="submit" value="提交"> </form>
POST 提交方式: name 傳遞 “測試人”,password 傳遞“test”.(postman 工具測試結果)
Content-Type: application/x-www-form-urlencoded name:%E6%B5%8B%E8%AF%95%E4%BA%BA password:test
該編碼方式,會將表單數據中的非西歐字符轉化為十六進制數字的形式。utf8字符集中,每個漢字占3個字節,每個漢字會轉化成3個十六進制的數 %XX%XX%XX 的形式。
在java 代碼中經常會處理下載文件名中文亂碼的問題,對於IE,谷歌等瀏覽器用到的就是編碼方式。
try { System.out.println(URLEncoder.encode("測試人","UTF8")); //%E6%B5%8B%E8%AF%95%E4%BA%BA } catch (UnsupportedEncodingException e) { e.printStackTrace(); }
當然瀏覽器不同使用的處理編碼也不盡相同。在使用Chrome的話顯示如下(gb2312編碼),這個可能和當前使用的語言環境有關:
name: %B2%E2%CA%D4%C8%CB password: test
接下來就是獲取值
Map<String,String[]> maps= request.getParameterMap(); //獲取所有的鍵值對 Enumeration<String> names= request.request.getParameterNames();//獲取所有的參數名 String[] values = request.getParameterValues(parameterName); //獲取某個參數下的所有值,適合多選組件 String value = request.getParameter(parameterName); //獲取某個參數對應的值,適合文本組件,單選等
如果需要在表單中上傳文件則不能使用該方式
2.multipart/form-data
當需要在表單內上傳文件時(二進制流數據)時,就需要使用 multipart/form-data。
<form action="/xxxx" method="POST" enctype="multipart/form-data"> <label>手機號</label><input type="text" name="phone"><br> <label>郵箱</label> <input type="text" name="mail"><br> <label>頭像</label><input type="file" name="portrait"><br/> <input type="submit" value="提交"> </form>
請求行的部分內容
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
請求體的內容
------WebKitFormBoundary7MA4YWxkTrZu0gW Content-Disposition: form-data; name=“phone” 16666666666 ------WebKitFormBoundary7MA4YWxkTrZu0gW Content-Disposition: form-data; name=“mail” test@163.com ------WebKitFormBoundary7MA4YWxkTrZu0gW Content-Disposition: form-data; name="portrait "; filename=“portrait.png” Content-Type: image/png ------WebKitFormBoundary7MA4YWxkTrZu0gW–
multipart,顧名思義 多部分。使用該編碼方式會將表單進行分割成每個控件(以----boundary為分隔符,將分割控件數據分隔開,在最后以—boundary—結尾)。每個部分必須加上Content-Disposition(form-data) ,對於上傳文件還會設置Content-Type。
對於上傳的不同類型的文件,會自動識別文件類型,如果識別不了則設置為application/octet-stream。
上傳jpeg格式圖片
Content-Type: image/jpeg
上傳pdf文件
Content-Type: application/pdf
上傳MP3
Content-Type: audio/mp3
對於使用該方式提交的表單,在服務器端的參數獲取通常有以下兩種:
單獨獲取 鍵值對數據。 使用getParameterMap() ,getParameter()等常規方法獲取數據
獲取全部數據(鍵值對和二進制流) getParts()。
使用getParts()方法,要求Content-Type:multipart/form-data,否則拋出異常。
通過Part 獲取上傳的文本內容
System.out.println(request.getParameter("phone")); System.out.println(request.getParameter("mail")); System.out.println(request.getParameter("portrait"));//二進制數據返回null System.out.println("----------------getParts獲取參數-------------------------"); //如果是multipart/form-data 提交方式,則此時 contentType:multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW String contentType=request.getContentType(); if(contentType !=null && contentType.contains(MediaType.MULTIPART_FORM_DATA_VALUE)) {//判斷是否是multipart/form-data 提交方式 Collection<Part> parts = request.getParts(); //獲取所有的部分 for(Part part :parts) { System.out.println(part.getContentType()); InputStream inPart = part.getInputStream(); String name=part.getName();//請求名稱 StringBuilder buffer=new StringBuilder(); //保存請求值 byte[] buff =new byte[1024]; int length=0; while((length=inPart.read(buff))!=-1) { buffer.append(new String(buff,0,length,"utf8")); } System.out.println("name = "+name+"\tvalue="+buffer); } }
輸出結果
16666666666 test@163.com null ----------------getParts獲取參數------------------------- null name = phone value=16666666666 null name = mail value=test@163.com image/png name = portrait value=�PNG
圖片比較大,所以只截取了一點。
注意:
在判斷contentType時,multipart/form-data提交方式的contentType不單純是multipart/form-data,還會包含 boundary=----WebKitFxxxx
小結
使用不同的編碼方式,服務端獲取參數的方式也不盡相同。
application/x-www-form-urlencoded。 該方式就是常規的方法, getParameterMap(),getParameter()等方式獲取參數
multipart/form-data
號 | 鍵值對 | 二進制數據 |
---|---|---|
1 | getParameter(),getParameterMap | getParts(); |
2 | getParts() |
注意: 使用 getParts() 方法 必須保證 ContentType 包含 ”multipart/form-data“ ,否則拋出ServletException,如果上傳文件特別大則會拋出IllegalStateException
重復說明:
getReader()和getInputSteam()互斥
getReader()和getInputStream() 只能調用一次
補充
在使用elasticsearch 的時候,發現 GET請求可以有請求體。當時就很懵逼,查閱資料后發現http標准中 GET請求確實可以有請求體。之前使用POSTMAN的時候都沒有注意過,也是心大了。
請求體的編碼方式和POST請求一致,但是GET方式請求體可以傳輸的數據要遠小於POST 請求的。
enctype | GET 請求獲取數據 | POST 請求獲取數據 | |
application/x-www-form-urlencoded | getInputStream() | getParameter() | |
multipart/form-data getInputStream() | getParameter() | getParameter(), getParts() | |
raw | 比如常用的 ajax請求 application/json | getInputStream() | getInputStream() |
binary | 文件類型 | getInputStream() | getInputStream() |
注意:
1. getInputStream() 不限 getInputStream方式,還包括 getReader()
2. getParameter() 方式還包括 getParameterMap(),getParameterValues(),getParameterNames()
3. multipart/form-data 類型的請求體, GET請求的數據不能通過getParts()獲取
測試發現,雖然GET請求可以使用上面的幾種方式。但是請求體中的內容只能通過 getInputStram()等流式方式獲取。
文本
可以指定的文本內容格式有多種,application/json是最常用的
text/plain: 純文本
text/xml :傳遞xml語法格式的字符串
text/html: 傳遞html字符串
application/json : 傳遞序列化后的 JSON 字符串。
獲取數據
ServletInputStream in = request.getInputStream();//該方法也是只能調用一次 BufferedReader reader=request.getReader();//該方法也是只能調用一次
文本數據格式,會使用流的方式傳遞數據。在springmvc中使用@RequestBody注解對應的注解解釋類應該就是通過InputSteam獲取數據並轉化為json對象。