1. 程式人生 > >SpringMVC/SpringBoot下restful風格URL,以及Http method的選擇、傳值問題

SpringMVC/SpringBoot下restful風格URL,以及Http method的選擇、傳值問題

一、Http 請求方法

HTTP/1.1 協議規定的 HTTP 請求方法有 OPTIONS、GET、HEAD、POST、PUT、DELETE、TRACE、CONNECT 8種,常用的有GET DELETE POST PUT四種。這些方法最大的區別是語義的區別,分別代表獲取、刪除、增加、修改操作,引導對資源的操作使用對應的方法。用法上也略有區別,常用的四種方法中又以GET、POST最為常用。

1.1 DELETE 和PUT不常用的原因

  • 歷史原因

     HTTP1.0只定義了三種請求方法: GET、POST 和 HEAD方法,沒有DELETE和PUT,導致很多舊的瀏覽器、WEB中介軟體不支援DELETE和PUT,我經過測試發現,新版本的瀏覽器、WEB伺服器早就支援了。

  • 企業的安全策略

     有些企業為了安全,採用了些安全措施,比如安裝了防火牆,阻止DELETE、PUT請求。我們知道其實安不安全跟這個method關係不大。

  • 其他,可能還有很多原因

    restful風格的URL建議我們根據操作意圖使用對應的method, 但是為了有更好的相容性,我們使用GET和POST兩種方式

1.2 GET和POST的區別

  • GET傳值只能通過URL,POST方式URL可以帶引數,請求體中也可以帶引數。
  • GET傳值,URL長度有限制,POST請求體幾乎無限制。
  • GET適合傳少量值,POST適合傳遞大量值,由於帶請求體,請求頭必須帶有contentType,GET請求頭沒有Content-Type。axiosGET方式,即便手動設定Content-Type,也會被忽略(不確定不是瀏覽器忽略的)。
  • 注意,有些說法說GET不安全、POST安全純屬以訛傳訛,安不安全跟用什麼method沒有關係,想要安全請使用HTTPS。

二、Content-Type

是什麼?Content-Type是http請求的頭資訊,但不是所有的方法都有。比如GET就沒有,POST有。它用來告訴伺服器本次請求體的資料格式是什麼。

服務端是根據請求頭(headers)中的 Content-Type 欄位來獲知請求中的訊息主體是用何種格式,再對主體進行解析,SpringMVC是由類HttpMessageConverter完成解析的,想要徹底理解可以看原始碼,或者參考這篇文章。這裡只介紹幾種常見的型別。

  • application/x-www-form-urlencoded

瀏覽器的原生 form 表單,如果不設定 enctype 屬性,那麼最終就會以 application/x-www-form-urlencoded 方式提交資料。請求類似於下面這樣(無關的請求頭在本文中都省略掉了):

POST http://www.example.com HTTP/1.1      // 這裡是請求行
Content-Type: application/x-www-form-urlencoded;charset=utf-8  //這裡是請求頭
title=test&myword=%E6%89%8B%E6%9C%BA   //這裡是請求

首先,Content-Type 被指定為 application/x-www-form-urlencoded;其次,提交的資料按照 key1=val1&key2=val2 的方式進行編碼,key 和 val 都進行了 URL 轉碼。

  • multipart/form-data

這又是一個常見的 POST 資料提交的方式。我們使用表單上傳檔案時,必須讓 form 的 enctyped 等於這個值。它會將表單的資料處理為一條訊息,以標籤為單元,用分隔符分開。既可以上傳鍵值對,也可以上傳檔案。當上傳的欄位是檔案時,會有Content-Type來表名檔案型別;content-disposition,用來說明欄位的一些資訊; 參考這篇文章

  • application/json

application/json 這個 Content-Type 作為響應頭大家肯定不陌生。實際上,現在越來越多的人把它作為請求頭,用來告訴服務端訊息主體是序列化後的 JSON 字串。

json的好處體積小,比XML小太多、與JS互動方便,解析難度小(幾乎為0),最重要的是靈活,特別是在傳輸層次結構比較複雜的資料時,正成為一代資料傳輸利器。

  • text/xml

XML-RPC(XML Remote Procedure Call)。它是一種使用 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>
         
    </params>
</methodcall>

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

XML是體積太過龐大,個人認為正在被JSON取代,不予考慮。

2.1 HTTP介面入參型別選擇

POST請求(非檔案上傳)使用JOSN作為資料傳輸格式,參考各大網際網路公司,在微服務的時代,他們對外提供的HTTP介面都是HTTP+JSON。使用JOSN還有一個好處,它的結構非常好,能精確的描述介面協議,XML在這方面則更勝一籌,傳統的xform最差。

2.2 HTTP介面返回值型別選擇

排除XML、HTML文字、只剩下json和自定義格式的文字,採用前後端分離技術,HTML文字也被排除,json為不二之選。

拓展:一個返回json中,除了正常的業務資料,還應該包含狀態碼、以及對應的Msg,參考dubbo的最佳實踐。呼叫方在獲取到返回值後首先應該判斷狀態碼。因此後臺設計api時,返回值應該使用一個DTO定義,此DTO繼承一個父類,父類包含狀態、Msg。方法使用@ResponseBody註解,SpringMVC會自動轉換為json輸出。

同理,入參也應該使用DTO定義,使用@RequestBody註解,只要前臺傳遞的json各個欄位跟入參bean的欄位對應,SpringMVC會自動轉化賦值。

三、springmvc常用傳值方式

  • GET請求,URL傳參,比例xxx/yyy?id=001,後臺使用@RequestParam接收引數。
  • GET請求,URI傳參,比如xxx/yyy/id,後臺@RequestMapping配置value=xxx/yyy/{id},形參使用註解@PathVariable接收引數
  • POST請求,請求體為JOSN,後臺形參使用註解@RequestBody接收引數
  • POST請求,請求體為FROM,後臺形參使用註解@ModelAttribute接收引數

四、restful風格的URL

restful只是url的一種風格,最佳實踐可以參考這裡。

風格精髓:url代表資源(貌似url的定義本就是如此),method代表操作意圖,url簡短、優雅。

那麼在SpringMVC下,如果要使用restful的URL,具體該怎麼做?需要綜合考慮http method、contentType、傳值方式。

我們看到最佳實踐中有些url是相同的,只是method不同,這在後臺不是很好處理。參考微信公眾號的api,我們總結出自己的實踐如下,使得URL接近restful,以user管理為例。

場景 http method URL contentType 傳值方式 說明
獲取一個資源 get user/(get|info)/id -- url路徑傳值
獲取資源列表 get user/(list) -- url傳值 適合無參獲取列表。引數較少時也可使用user/list?形式
獲取資源列表 post user/(list) json post請求體 適合引數較多(比如分頁查詢,不僅要傳分頁資訊,還要穿搜尋條件),這是一個妥協方式。
新增一個資源 post user/(add|create) json post請求體
修改一個資源 post user/(update/modify)/id json post請求體
刪除一個資源 get user/(delete|remove)/id -- url路徑傳值

也就是說,  獲取資料(語義上就是get)和傳值較少(delete)使用get方式,優先使用url路徑傳值,而後考慮url引數傳值。

傳值較多(新增、修改,分頁查詢)使用post,資料格式為json,其他格式不允許。

檔案操作另行設計。

五、後臺相關(Controller)

  • restful風格的url沒有.字尾,如果springmvc DispatcherServlet配置攔截了帶有後綴的部分需要去掉,改為<url-pattern>/</url-pattern>。(可以通過配置不攔截的靜態資源型別達到只攔截獲取資料URL的目的)
  • 不管get和post方法,@RequestMapping 除了value和method外,必須配置produces = "application/json;charset=UTF-8",用來告知客戶端返回值為json,雖然有些客戶端api有設定返回值型別。這裡可以設定charset用來防止中文亂碼,否則可能會出現客戶端能解析但是中文亂碼。
  • consumes標示本介面希望接收的引數的格式,對應http請求頭的content-type。springmvc在解析資料時會做對比校驗。post方法一定要寫用來明確表示。get方法一定不要寫,因為get沒有content-type,寫了就會校驗報錯,錯誤為 不支援' ',這個空字串就是因為沒有獲取到content-type。