SpringMVC——HTTP請求專案實踐整理總結

TheOctopus發表於2020-12-06

(一)MIME介紹

(1)介紹

語法結構

type/subtype         型別/獨立型別

媒體型別(通常稱為 Multipurpose Internet Mail Extensions 或 MIME 型別 )是一種標準,用來表示文件、檔案或位元組流的性質和格式。它在IETF RFC 6838中進行了定義和標準化。

重要:瀏覽器通常使用MIME型別(而不是副檔名)來確定如何處理URL,因此Web伺服器在響應頭中新增正確的MIME型別非常重要。如果配置不正確,瀏覽器可能會曲解檔案內容,網站將無法正常工作,並且下載的檔案也會被錯誤處理。

MIME的組成結構非常簡單;由型別與子型別兩個字串中間用’/'分隔而組成。不允許空格存在。type 表示可以被分多個子類的獨立類別。subtype 表示細分後的每個型別。

MIME型別對大小寫不敏感,但是傳統寫法都是小寫。

(2)獨立型別
text/plain
text/html
image/jpeg
image/png
audio/mpeg
audio/ogg
audio/*
video/mp4
application/*
application/json
application/javascript
application/ecmascript
application/octet-stream

在這裡插入圖片描述

對於text檔案型別若沒有特定的subtype,就使用 text/plain。類似的,二進位制檔案沒有特定或已知的 subtype,即使用 application/octet-stream。

(二)四種常見的 POST 提交資料方式

協議規定 POST 提交的資料必須放在訊息主體(entity-body)中,但協議並沒有規定資料必須使用什麼編碼方式。實際上,開發者完全可以自己決定訊息主體的格式,只要最後傳送的 HTTP 請求滿足上面的格式就可以。

但是,資料傳送出去,還要服務端解析成功才有意義。一般服務端語言如 php、python 等,以及它們的 framework,都內建了自動解析常見資料格式的功能。服務端通常是根據請求頭(headers)中的 Content-Type 欄位來獲知請求中的訊息主體是用何種方式編碼,再對主體進行解析。所以說到 POST 提交資料方案,包含了 Content-Type 和訊息主體編碼方式兩部分。下面就正式開始介紹它們。

【application/x-www-form-urlencoded】
這應該是最常見的 POST 提交資料的方式了。瀏覽器的原生 表單,如果不設定 enctype 屬性,那麼最終就會以

POST http://www.example.com HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8

title=test&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3

首先,Content-Type 被指定為 application/x-www-form-urlencoded;其次,提交的資料按照 key1=val1&key2=val2 的方式進行編碼,key 和 val 都進行了 URL 轉碼。大部分服務端語言都對這種方式有很好的支援。例如 PHP 中, P O S T [ ′ t i t l e ′ ] 可 以 獲 取 到 t i t l e 的 值 , _POST['title'] 可以獲取到 title 的值, POST[title]title_POST[‘sub’] 可以得到 sub 陣列。

很多時候,我們用 Ajax 提交資料時,也是使用這種方式。例如 JQuery 的 Ajax,Content-Type 預設值都是
「application/x-www-form-urlencoded;charset=utf-8」

【multipart/form-data】
這又是一個常見的 POST 資料提交的方式。我們使用表單上傳檔案時,必須讓 表單的 enctype 等於 multipart/form-data。
【application/json】

application/json 這個 Content-Type 作為響應頭大家肯定不陌生。實際上,現在越來越多的人把它作為請求頭,用來告訴服務端訊息主體是序列化後的 JSON 字串。由於 JSON 規範的流行,除了低版本 IE 之外的各大瀏覽器都原生支援 JSON.stringify,服務端語言也都有處理 JSON 的函式,使用 JSON 不會遇上什麼麻煩。

JSON 格式支援比鍵值對複雜得多的結構化資料,這一點也很有用。

【text/xml】
它是一種使用 HTTP 作為傳輸協議,XML 作為編碼方式的遠端呼叫規範。典型的 XML-RPC 請求是這樣的:

POST http://www.example.com HTTP/1.1 
Content-Type: text/xml

<?xml version="1.0"?>
<methodCall>
    <methodName>examples.getStateName</methodName>
    <params>
        <param>
            <value><i4>41</i4></value>
        </param>
    </params>
</methodCall>

XML-RPC 協議簡單、功能夠用,各種語言的實現都有。它的使用也很廣泛,如 WordPress 的 XML-RPC Api,搜尋引擎的 ping 服務等等。JavaScript 中,也有現成的庫支援以這種方式進行資料互動,能很好的支援已有的 XML-RPC 服務。不過,我個人覺得 XML 結構還是過於臃腫,一般場景用 JSON 會更靈活方便。

(三)SpringMVC接收POST請求的方式

(1)預設Content-Type

Content-Type以application/x-www-form-urlencoded進行傳輸,預設形式
此種傳輸方式是構造form表單的普通post請求的方式進行傳輸
controller中需要有兩個引數,同時需要注意這個不能加@RequestBody否則丟擲405異常,引數會以字串和陣列的形式填充到引數裡面

application/x-www-form-urlencoded接收方式:

1:@RequestParam(“name”)

2:使用相同的鍵名稱字串接收 String name ,名稱需要匹配

3:使用實體類接收,匹配的屬性會注入值,需要有setgeter方法,名稱不一致無法注入

4:使用request.getParameter(“name”);接收

(2)application/json

Content-Type以application/json的方式進行傳輸
此種方式需要傳輸一個json格式的字串
controller中需要以一個字串來進行接受這個json字串
如果用註解的話需要新增@RequestBody
如果不用註解,需要從request域裡面把body內容讀取出來,然後將此字串進行json反序列化成物件,如果想讓他自動轉化成物件,需要在SpringMVC的配置裡面配置json序列化工具。

參考方式如下:

1:使用@RequestBody String 接收,直接給個String無法接收,這種方式接收到是一個JSON字串,需要自己通過工具轉換

2:@RequestBody 加POJO接收或者字串

3:@RequestBody Map<String,Object> json,使用Map接收也比較方便,不需要構建VO物件了

4:手動從流中獲取getReader()或者getInputStream(),注意前面方式底層也通過這種方式接收的,不能重複操作流

接收引數:多個單一屬性的一個物件

(四)getParameter() getInputStream()和getReader() 區別

request.getParameter()
request.getInputStream()
request.getReader()
這三個方法都是從request物件中得到提交的資料,但是用途不同。

要根據表單提交資料的編碼方式選擇不同的方法。

HTML中的form表單的一個關鍵屬性 enctype:

(1)enctype=application/x- www-form-urlencoded
這種編碼方式是預設的編碼方式。
編碼後的結果通常是field1=value2&field2=value2&… 的形式,如 name=aaaa&password=bbbb。
通常使用的表單也是採用這種方式編碼的,Servlet 的 API 提供了對這種 編碼方式解碼的支援,只需要呼叫 ServletRequest 類中的 getParameter()方法就可以得到使用者表單中的欄位和資料。
不足:
這種編碼方式( application/x-www-form-urlencoded )雖然簡單,但對於傳輸大塊的二進位制資料顯得力不從心。

(2)multipart/form-data
對於傳輸大塊的二進位制數這類資料,瀏覽器採用了另一種編碼方式,即 “multipart/form-data” 的編碼方式:

瀏覽器可以很容易將表單內的資料和檔案放在一起傳送。這種編碼方式先定義好一個不可能在資料中出現的字串作為 分界符,然後用它將各個資料段分開,而對於每個資料段都對應著 HTML 頁面表單中的一個 Input 區,包括一個 content-disposition 屬性,說明了這個資料段的一些資訊,如果這個資料段的內容是一個檔案,還會有 Content-Type 屬性,然後就是資料本身,如果以這種方式提交資料就要用request.getInputStream()或request.getReader()得到 提交的資料,用 request.getParameter()是得不到提交的資料的。

request.getParameter()、 request.getInputStream()、request.getReader()這三種方法是有衝突的,因為流只能被讀一次。
比如:
當form表單內容採用 enctype=application/x-www-form-urlencoded編碼時,先通過呼叫request.getParameter() 方法得到引數後,再呼叫request.getInputStream()或request.getReader()已經得不到流中的內容,因為在呼叫 request.getParameter()時系統可能對錶單中提交的資料以流的形式讀了一次,反之亦然。

當form表單內容採用enctype=multipart/form-data編碼時,即使先呼叫request.getParameter()也得不到資料,所以這時呼叫request.getParameter()方法對 request.getInputStream()或request.getReader()沒有衝突,即使已經呼叫了 request.getParameter()方法也可以通過呼叫request.getInputStream()或request.getReader()得 到表單中的資料,而request.getInputStream()和request.getReader()在同一個響應中是不能混合使用的,如果混合使用就會拋異常。

在http請求中,有Header和Body之分,讀取header使用request.getHeader("…");讀取Body使用request.getReader(),但getReader獲取的是BufferedReader,需要把它轉換成字串

 private static String getPostData(HttpServletRequest request) throws IOException {
     StringBuilder data = new StringBuilder();
     String line;
     BufferedReader reader;
     try {
         reader = request.getReader();
         while (null != (line = reader.readLine())) {
             data.append(line);
         }
     } catch (IOException ignored) {
         throw new IOException();
     }
     return data.toString();
 }

參考

https://imququ.com/post/four-ways-to-post-data-in-http.html

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/MIME_types

相關文章