圖8-1 REST成熟度模型

◆?第0級:使用HTTP作為傳輸方式

在第0級中,Web服務只是使用HTTP作為傳輸方式,實際上只是遠程方法調用(RPC)的一種具體形式。SOAP和XML-RPC都屬于此級別。比如,在一個醫院掛號系統中,醫院會通過某個URI來暴露出該掛號服務端點(Service Endpoint)。然后患者會向該URI發送一個文檔作為請求,文檔中包含了請求的所有細節。

POST /appointmentService HTTP/1.1
[省略了其他頭的信息……]
<openSlotRequest date = "2010-01-04" doctor = "mjones"/>

然后服務器會傳回一個包含了所需信息的文檔。

HTTP/1.1 200 OK
[省略了其他頭的信息……]
<openSlotList>
<slot start = "1400" end = "1450">
<doctor id = "mjones"/>
</slot>
<slot start = "1600" end = "1650">
<doctor id = "mjones"/>
</slot>
</openSlotList>

在這個例子中我們使用了XML,但是內容實際上可以是任何格式,比如JSON、YAML、鍵值對等,或者其他自定義的格式。

有了這些信息,下一步就是創建一個預約。這同樣可以通過向某個端點發送一個文檔來完成。

POST /appointmentService HTTP/1.1
[省略了其他頭的信息……]
<appointmentRequest>
<slot doctor = "mjones" start = "1400" end = "1450"/>
<patient id = "jsmith"/>
</appointmentRequest>

如果一切正常,那開發者能夠收到一個預約成功的響應。

HTTP/1.1 200 OK
[省略了其他頭的信息……]<appointment>
<slot doctor = "mjones" start = "1400" end = "1450"/>
<patient id = "jsmith"/>
</appointment>

如果發生了問題,比如有人在我前面預約上了,那我會在響應體中收到某條錯誤信息。

HTTP/1.1 200 OK
[省略了其他頭的信息……]
<appointmentRequestFailure>
<slot doctor = "mjones" start = "1400" end = "1450"/>
<patient id = "jsmith"/>
<reason>Slot not available</reason>
</appointmentRequestFailure>

到目前為止,這都是非常直觀的基于RPC風格的系統。它是簡單的,因為只有Plain Old XML(POX)在這個過程中被傳輸。如果你使用SOAP或者XML-RPC,原理也是基本相同的,唯一的不同是——你將XML消息包含在了某種特定的格式中。

◆?第1級:引入了資源的概念

在第1級中,Web服務引入了“資源”的概念,每個資源有對應的標識符和表達。所以,不是將所有的請求發送到單個服務端點,而是和單獨的資源進行交互。

因此在我們的首個請求中,對指定醫生會有一個對應的資源。

POST /doctors/mjones HTTP/1.1
[省略了其他頭的信息……]
<openSlotRequest date = "2010-01-04"/>

響應會包含一些基本信息,包含了各個時間段的就診時間信息,這些就診時間可以被作為資源單獨處理。

HTTP/1.1 200 OK[省略了其他頭的信息……]
<openSlotList>
<slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
<slot id = "5678" doctor = "mjones" start = "1600" end = "1650"/>
</openSlotList>

有了這些資源后,創建一個預約就是向某個特定的就診時間發送請求。

POST /slots/1234 HTTP/1.1
[省略了其他頭的信息……]
<appointmentRequest>
<patient id = "jsmith"/>
</appointmentRequest>

如果一切順利,則會收到和前面類似的響應:

HTTP/1.1 200 OK
[省略了其他頭的信息……]
<appointment>
<slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
<patient id = "jsmith"/>
</appointment>

◆?第2級:根據語義使用HTTP動詞

在第2級中,Web服務使用不同的HTTP方法來進行不同的操作,并且使用HTTP狀態碼來表示不同的結果。例如,GET方法用來獲取資源,DELETE方法用來刪除資源。

在醫院掛號系統中,獲取醫生的就診時間信息需要使用GET。

GET /doctors/mjones/slots?date=20100104&status=open HTTP/1.1
Host: royalhope.nhs.uk

響應和之前使用POST發送請求時一致。

HTTP/1.1 200 OK
[省略了其他頭的信息……]
<openSlotList><slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
<slot id = "5678" doctor = "mjones" start = "1600" end = "1650"/>
</openSlotList>

像上面那樣使用GET來發送一個請求是至關重要的。HTTP將GET定義為一個安全的操作,它并不會對任何事物的狀態造成影響。這也就允許我們可以用不同的順序若干次調用GET請求而每次還能夠獲取到相同的結果。一個重要的結論就是,GET允許參與到路由中的參與者使用緩存機制,該機制是讓目前的Web運轉良好的關鍵因素之一。HTTP包含許多方法來支持緩存,這些方法可以在通信過程中被所有的參與者使用。通過遵守HTTP的規則,我們可以很好地利用該緩存。

為了創建一個預約,我們需要使用一個能夠改變狀態的請求方式。

這里使用和前面相同的一個POST請求。

POST /slots/1234 HTTP/1.1
[省略了其他頭的信息……]
<appointmentRequest>
<patient id = "jsmith"/>
</appointmentRequest>

如果一切順利,則服務會返回一個201響應來表明新增了一個資源。這是與第1級的POST響應完全不同的。第2級中的操作響應都有統一的返回狀態碼。

HTTP/1.1 201 Created
Location: slots/1234/appointment
[省略了其他頭的信息……]
<appointment>
<slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
<patient id = "jsmith"/>
</appointment>

201響應包含了一個Location屬性,它是一個URI。將來客戶端可以通過GET請求獲得該資源的狀態。以上的響應還包含該資源的信息,從而省去了一個獲取該資源的請求。當出現問題時,第2級和第1級還有一個不同之處。比如某人預約了該時段:

HTTP/1.1 409 Conflict
[various headers]
<openSlotList>
<slot id = "5678" doctor = "mjones" start = "1600" end = "1650"/>
</openSlotList>

在上例中,409表明該資源已經被更新了。與使用200作為響應碼再附帶一個錯誤信息相比,在第2級中我們會明確響應碼的含義,以及其所對應的響應信息。

◆?第3級:使用HATEOAS

在第3級中,Web服務使用HATEOAS。HATEOAS是Hypertext AsThe Engine Of Application State的縮寫,是指在資源的表達中包含了鏈接信息,客戶端可以根據鏈接來發現可以執行的動作。

從上述REST成熟度模型中可以看到,使用HATEOAS的REST服務是成熟度最高的,也是Roy Fielding所推薦的“超文本驅動”的做法。對于不使用HATEOAS的REST服務,客戶端和服務器的實現之間是緊密耦合的。客戶端需要根據服務器提供的相關文檔來了解所暴露的資源和對應的操作。當服務器發生變化(如修改了資源的URI)時,客戶端也需要進行相應的修改。而在使用HATEOAS的REST服務中,客戶端可以通過服務器提供的資源的表達來智能地發現可以執行的操作。當服務器發生了變化時,客戶端并不需要做出修改,因為資源的URI和其他信息都是被動態發現的。下面是一個HATEOAS的例子。

{
"id": 711,
"manufacturer": "bmw",
"model": "X5",
"seats": 5,"drivers": [
{
"id": "23",
"name": "Way Lau",
"links": [
{
"rel": "self",
"href": "/api/v1/drivers/23"
}
]
}
]
}

回到我們的醫院掛號系統案例中,還是使用在第2級中使用過的GET作為首個請求。

GET /doctors/mjones/slots?date=20100104&status=open HTTP/1.1
Host: royalhope.nhs.uk

但是響應中添加了一個新元素。

HTTP/1.1 200 OK
[省略了其他頭的信息……]
<openSlotList>
<slot id = "1234" doctor = "mjones" start = "1400" end = "1450">
<link rel = "/linkrels/slot/book"
uri = "/slots/1234"/>
</slot>
<slot id = "5678" doctor = "mjones" start = "1600" end = "1650">
<link rel = "/linkrels/slot/book"
uri = "/slots/5678"/>
</slot>
</openSlotList>

每個就診時間信息現在都包含一個URI,用來告訴我們如何創建一個預約。

超媒體控制(Hypermedia Control)的關鍵在于:它告訴我們下一步能夠做什么,以及相應資源的URI。比如,我們事先就可以知道去哪個地址發送預約請求,因為響應中的超媒體控制直接在響應體中告訴了我們該如何做。

預約的POST請求與第2級中類似。

POST /slots/1234 HTTP/1.1
[省略了其他頭的信息……]
<appointmentRequest>
<patient id = "jsmith"/>
</appointmentRequest>

響應包含了一系列的超媒體控制,用來告訴我們后面可以進行什么操作。

HTTP/1.1 201 Created
Location: http://royalhope.nhs.uk/slots/1234/appointment
[省略了其他頭的信息……]
<appointment>
<slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
<patient id = "jsmith"/>
<link rel = "/linkrels/appointment/cancel"
uri = "/slots/1234/appointment"/>
<link rel = "/linkrels/appointment/addTest"
uri = "/slots/1234/appointment/tests"/>
<link rel = "self"
uri = "/slots/1234/appointment"/>
<link rel = "/linkrels/appointment/changeTime"
uri = "/doctors/mjones/slots?date=20100104@status=open"/>
<link rel = "/linkrels/appointment/updateContactInfo"
uri = "/patients/jsmith/contactInfo"/>
<link rel = "/linkrels/help"
uri = "/help/appointment"/>
</appointment>

超媒體控制的一個顯著優點是:它能夠在保證客戶端不受影響的條件下,改變服務器返回的URI方案。只要客戶端查詢“addTest”這個URI,后臺開發團隊就可以根據需要隨意修改與之對應的URI(只有最初的入口URI不能被修改)。

超媒體控制的另一個顯著優點是:它能夠幫助客戶端開發人員進行探索。其中的鏈接告訴了客戶端開發人員下面可能需要執行的操作。它并不會告訴所有的信息,但是至少它提供了一個思考的起點,引導開發人員在協議文檔中查看相應的URI

同樣地,它也讓服務器端的團隊可以通過向響應中添加新的鏈接來增加功能。比如,如果客戶端開發人員發現了一個之前未知的鏈接,那他們就會知道這個鏈接是服務器端提供的新的功能。

◆?REST API管理

下面介紹幾種簡潔的REST API設計的最佳實踐,可以作為真假REST的一個判別依據。

1.使用的是名詞而不是動詞

使用名詞來定義接口。

/resources
/resources/1024
不應該使用動詞:
/getAllResources
/createNewResource
/deleteAllResources

2.GET方法和查詢參數不能改變資源狀態

如果要改變資源的狀態,要使用PUT、POST和DELETE。下面使用GET方法來修改user的狀態是錯誤的:

GET /users/711?activate

GET /users/711/activate

3.使用名詞復數

不要混淆名詞的單復數。保持簡單,只用復數名詞來定義所有資源。

/cars 代替 /car
/users 代替 /user
/products 代替 /product/settings 代替 /setting

4.使用子資源來表達資源間的關系

GET /cars/711/drivers/ 返回 711 號 car 的所有 driver 列表

GET /cars/711/drivers/4 返回 711 號 car 的 4 號 driver

5.使用HTTP header來序列化格式

客戶端、服務器都需要知道相互之間的通信格式。這些格式可以定義在HTTP header里面。

·Content-Type定義了請求格式。

·Accept定義了接收相應的格式列表。

6.使用HATEOAS約束

HATEOAS是REST架構風格中最復雜的約束,也是構建成熟REST服務的核心。它的重要性在于打破了客戶端和服務器之間嚴格的合約,使得客戶端可以更加智能和自適應,而REST服務本身的演化和更新也變得更加容易。

下面是一個HATEOAS的例子。

{
"id": 711,
"manufacturer": "bmw",
"model": "X5",
"seats": 5,
"drivers": [
{
"id": "23",
"name": "Stefan Jauker",
"links": [
{
"rel": "self",
"href": "/api/v1/drivers/23"
}
]
}
]}

7.提供過濾、排序、字段選擇、分頁

過濾:

GET /cars?color=red
GET /cars?seats<=2

排序:

GET /cars?sort=-manufactorer,+model

字段選擇:

GET /cars?fields=manufacturer,model,id,color

分頁:

GET /cars?offset=10&limit=5

8.API版本化

版本號使用簡單的序號,并避免點號,如2.5等。正確用法如下。

/blog/api/v1

9.充分使用HTTP狀態碼來處理錯誤

HTTP狀態碼(HTTP Status Code)是用來表示網頁服務器HTTP響應狀態的3位數字代碼。它由RFC 2616規范定義,并得到RFC 2518、RFC 2817、RFC 2295、RFC 2774、RFC 4918等規范的擴展。

在設計API處理錯誤時,應該充分使用HTTP狀態碼,而不是簡單地拋出一個“500-Internal Server Error(內部服務器錯誤)”。所有的異常都應該有一個錯誤的payload作為映射,下面是一個例子。

{
"errors": [
{
"userMessage": "Sorry, the requested resource does not exist",
"internalMessage": "No car found in the database",
"code": 34,
"more info": "http://dev.mwaysolutions.com/blog/api/v1/errors/12345"
}
]
}

文章轉自微信公眾號@IT大咖說

熱門推薦
一個賬號試用1000+ API
助力AI無縫鏈接物理世界 · 無需多次注冊
3000+提示詞助力AI大模型
和專業工程師共享工作效率翻倍的秘密
返回頂部
上一篇
API的性能約定
下一篇
15 個REST API 設計的基本技巧
国内精品久久久久影院日本,日本中文字幕视频,99久久精品99999久久,又粗又大又黄又硬又爽毛片
欧美色偷偷大香| 亚洲另类春色国产| 国内外精品视频| 日韩三级电影网址| av高清不卡在线| 欧美日韩一区高清| 国产亚洲精品久| 欧美这里有精品| 亚洲精选免费视频| 亚洲精品少妇30p| 国产成人精品www牛牛影视| 日韩欧美的一区| 国产精品自拍一区| 欧美大度的电影原声| 国产精品大尺度| 亚洲裸体在线观看| 欧美精品丝袜中出| 亚洲欧美激情小说另类| 国产欧美一区二区三区沐欲| 成人精品电影在线观看| 亚洲素人一区二区| 色综合天天综合色综合av| 精品美女在线观看| 久久久蜜臀国产一区二区| 亚洲成人久久影院| 欧美成人激情免费网| 成人ar影院免费观看视频| 欧美色倩网站大全免费| 国产精品欧美一级免费| 欧美日韩小视频| 国产麻豆一精品一av一免费 | 亚洲欧美综合色| 久久 天天综合| 精品国产一区二区亚洲人成毛片| 97久久超碰精品国产| 欧美激情中文字幕| 色综合天天综合网国产成人综合天 | 国产精品每日更新| 有码一区二区三区| 日韩激情中文字幕| 91精品啪在线观看国产60岁| 成人av资源下载| 午夜欧美大尺度福利影院在线看| 欧美高清性hdvideosex| 亚洲人成人一区二区在线观看| 国产蜜臀97一区二区三区| 国产亲近乱来精品视频| 久久久亚洲高清| 一本色道亚洲精品aⅴ| 日韩和欧美一区二区| 欧美色窝79yyyycom| 五月天精品一区二区三区| 欧美色中文字幕| 成人激情校园春色| 99国产精品久久久| 欧美日韩国产一级片| 色又黄又爽网站www久久| 亚洲一区二区影院| 亚洲色图制服诱惑 | 蜜臀91精品一区二区三区| 日韩午夜av电影| 欧美性极品少妇| 91美女视频网站| 91成人网在线| 青草国产精品久久久久久| 亚洲欧美自拍偷拍| 中文字幕+乱码+中文字幕一区| 久久青草欧美一区二区三区| 国产大陆a不卡| 亚洲高清视频的网址| 天天综合日日夜夜精品| 国产一二精品视频| 国产精品一级片| 成人午夜又粗又硬又大| 97精品电影院| 精品一区二区影视| 99久久夜色精品国产网站| 精品一区二区三区在线播放视频| 毛片不卡一区二区| 从欧美一区二区三区| 国产真实精品久久二三区| 国模套图日韩精品一区二区| 亚洲福利视频导航| 日韩1区2区3区| 国产99一区视频免费| 国产精品传媒视频| 亚洲狠狠丁香婷婷综合久久久| 欧美一卡二卡在线| 日韩三级伦理片妻子的秘密按摩| 欧美变态凌虐bdsm| 亚洲欧洲99久久| 日韩不卡手机在线v区| 亚洲综合色在线| 日韩成人免费看| 国产一区二区福利| 欧美日韩国产影片| 国产欧美精品一区| 国产精品不卡一区| 婷婷中文字幕综合| 秋霞午夜av一区二区三区| av在线一区二区三区| 日韩一区二区视频在线观看| 亚洲自拍与偷拍| 欧美色网站导航| 亚洲精品一线二线三线无人区| 久久一留热品黄| 国产在线精品免费av| 欧美一级专区免费大片| 麻豆一区二区在线| 欧美性生活一区| 久久久久久久久久美女| 五月激情六月综合| 91视视频在线观看入口直接观看www| 久久综合狠狠综合| 亚洲福中文字幕伊人影院| 欧美午夜理伦三级在线观看| 久久久久97国产精华液好用吗| 久久精品国产亚洲aⅴ| 色婷婷综合中文久久一本| 中文字幕在线不卡| 一区二区三区色| 欧美性极品少妇| 捆绑紧缚一区二区三区视频 | 一区二区三区小说| 国产精品1区2区3区在线观看| 日本一区二区三区在线不卡| 激情综合色播五月| 中文字幕av资源一区| 色一区在线观看| 开心九九激情九九欧美日韩精美视频电影 | 久久久精品国产99久久精品芒果| 中文字幕不卡一区| 懂色av噜噜一区二区三区av| 中文乱码免费一区二区| 色综合av在线| 大白屁股一区二区视频| 国产精品乱人伦一区二区| 成人av资源站| 亚洲图片有声小说| 欧美一级黄色大片| 91丨porny丨在线| 97久久久精品综合88久久| 午夜亚洲国产au精品一区二区| 成人国产一区二区三区精品| 亚洲欧洲精品天堂一级| 久久这里都是精品| 久久精品一区二区三区不卡| 欧美一区二区三区喷汁尤物| 色综合久久中文字幕| 蜜桃一区二区三区在线观看| 丝袜a∨在线一区二区三区不卡| 亚洲一区二区美女| 337p粉嫩大胆噜噜噜噜噜91av| 日本韩国一区二区| 99综合电影在线视频| 91麻豆精品在线观看| 91麻豆精品视频| 91小视频免费观看| 国产一区视频导航| 久久国产视频网| 91亚洲精品一区二区乱码| 欧美日韩卡一卡二| 欧美精品久久一区| 久久伊99综合婷婷久久伊| 欧美在线一二三四区| 国产一区二区三区综合| 国产一区二三区| 成人午夜视频免费看| 99国产精品久久久久久久久久| 精品亚洲国内自在自线福利| 美女免费视频一区| 五月婷婷综合网| 免费一区二区视频| 毛片一区二区三区| 99久久婷婷国产| 欧美一级理论性理论a| 亚洲制服丝袜在线| 国产成a人亚洲精品| 99麻豆久久久国产精品免费优播| 美国精品在线观看| 成人一区二区三区视频在线观看| 欧美一级片在线观看| 亚洲黄色性网站| 九九国产精品视频| 欧美日韩国产大片| 欧美一区二区福利视频| 欧美久久久久久蜜桃| 欧美国产成人精品| 日精品一区二区| 欧美精品tushy高清| 亚洲精品亚洲人成人网| 视频一区二区欧美| 欧美日韩一卡二卡三卡| 欧美一区二区三级| 一区二区三区不卡在线观看| 国产精品69毛片高清亚洲| 色婷婷综合久久久久中文一区二区| 成人免费黄色在线| 国产精品成人午夜| 波多野结衣中文字幕一区|