本章是我們的API之旅的轉(zhuǎn)折點(diǎn)狸演。我們已經(jīng)結(jié)束了基礎(chǔ)部分的瀏覽,準(zhǔn)備來看看之前的概念如何與構(gòu)建API結(jié)合起來趁蕊。在這一章槐壳,我們通過設(shè)計(jì)一個API來討論API的各個組件然低。
組織數(shù)據(jù)
國家地理雜志2011年估計(jì),美國人拍攝了800億張照片。這么多的照片雳攘,你可以想象人們會使用各種不同的方法來組織他們電腦上的照片带兜。一些人會選擇全部丟在一個文件夾里。另一些人會細(xì)心的將照片放到不同的文件夾吨灭,這些文件夾根據(jù)年刚照,月和事件的層次關(guān)系進(jìn)行組織。
公司在構(gòu)建API的時候也會進(jìn)行類似的考慮沃于。就像我們在第一章提到的涩咖,API的目的是讓電腦方便的處理公司的數(shù)據(jù)。處于易用性的考慮繁莹,公司可能會決定對所有的數(shù)據(jù)使用同一個URL檩互,然后讓它可以進(jìn)行搜索(類似于將所有照片放在同一個文件夾里)。另一些公司可能決定數(shù)據(jù)的每一部分有自己的URL咨演,組織成一個層次關(guān)系(就像使用目錄和子目錄來管理照片)闸昨。每一個公司在業(yè)界已有的最佳實(shí)踐的指導(dǎo)下,根據(jù)自己的情況選擇最合適的方式來組織自己的API薄风。
從架構(gòu)風(fēng)格開始
當(dāng)討論API的時候饵较,你可能會聽到他們談?wù)摗皊oap”和“rest”,或許你會奇怪這些軟件開發(fā)者是在工作還是在計(jì)劃度假遭赂。事實(shí)上這是兩個基于web的API的最常用的架構(gòu)的名字循诉。SOAP(首字母的縮寫)是一個基于XML的設(shè)計(jì),它的請求和響應(yīng)有標(biāo)準(zhǔn)的格式撇他。REST代表表述性狀態(tài)傳遞茄猫,是一種更開放的方式,它提供了很多約定困肩,但也給API的設(shè)計(jì)者留了很多自己做決定的空間划纽。
縱觀本課程,你可能已經(jīng)注意到我們更傾向于REST API锌畸。這種偏好很大程度上是由于REST那驚人的占有率勇劣。這并不是說SOAP不好,它也有自己的優(yōu)勢潭枣。然而比默,我們討論的焦點(diǎn)仍會保持在REST上,因?yàn)槟阌龅降腁PI很可能是REST類型的卸耘。在接下來的部分退敦,我們將談?wù)摌?gòu)成REST API的組件。
我們的第一個資源
回到第二章蚣抗,我們談?wù)撨^一點(diǎn)資源侈百∥拖拢回憶一下,資源就是API中的名詞(顧客和披薩)钝域。這些是我們希望可以通過我們的API進(jìn)行處理的東西讽坏。
為了感受公司如何設(shè)計(jì)一個API,我們從披薩店開始試試手例证。我們將從增加訂披薩的功能開始路呜。
為了讓客戶端可以和我們討論披薩,我們需要做這些事情:
- 確定哪些資源是可訪問的
- 為這些資源分配URL
- 確定允許客戶端對這些資源進(jìn)行哪些操作
- 估計(jì)每個動作需要數(shù)據(jù)的哪些部分织咧,這些數(shù)據(jù)應(yīng)該采用什么格式
第一個任務(wù)挑選資源就有些難度胀葱。解決這個問題的一個方法是逐步深入了解一個典型的交互需要涉及哪些資源。對于披薩店來說笙蒙,我們可能會有一個菜單抵屿。菜單上是各種披薩。當(dāng)顧客希望我們?yōu)樗麄冏鲆粋€披薩的時候捅位,他們下一個訂單轧葛。在這個背景下,菜單艇搀、披薩尿扯、顧客和訂單聽起來都是備選的資源。讓我們從訂單開始焰雕。
下一個步驟是為資源分配URL衷笋。可選的方案有很多矩屁,幸運(yùn)的是REST的規(guī)定可以提供一些指導(dǎo)右莱。在一個典型的REST API中,一個資源會有兩個分配給它的URL模式档插。第一個是資源名字的復(fù)數(shù)形式,比如 /orders亚再。第二個是資源名字的復(fù)數(shù)加上一個唯一的標(biāo)識符來指定一個資源郭膛,比如/orders/<order_id>,其中<order_id>是某一個訂單的唯一標(biāo)識符氛悬。這兩種URL模式構(gòu)成了我們的API將會支持的第一個終端则剃。叫它們終端僅僅是因?yàn)樗麄兲幵谝粋€URL的末尾,比如在 http://example.com/<endpoint_goes_here> 如捅。
既然我們挑選了資源棍现,也為它們分配了URL,我們就需要決定客戶端可以執(zhí)行哪些動作镜遣。根據(jù)REST的規(guī)則己肮,我們說復(fù)數(shù)終端(/orders)是為了列出所有存在的訂單和創(chuàng)建新的訂單。帶有唯一標(biāo)識符的復(fù)數(shù)終端(/orders/<order_id>),是為了獲取谎僻、更新或者取消某個特定的訂單娄柳。客戶端通過請求中適當(dāng)?shù)腍TTP動詞 (GET, POST, PUT 或者DELETE)告訴服務(wù)器要執(zhí)行哪個動作艘绍。
總體來說赤拒,現(xiàn)在我們的API看起來是這樣的:
HTTP動詞 | 終端 | 動作 |
---|---|---|
GET | /orders | 列出已有的訂單 |
POST | /orders | 添加一個新的訂單 |
GET | /orders/1 | 獲取訂單 #1的詳情 |
GET | /orders/2 | 獲取訂單 #2的詳情 |
PUT | /orders/1 | 更新訂單 #1 |
DELETE | /orders/1 | 取消訂單 #1 |
有了對于訂單終端的詳實(shí)的動作,最后要做的就是決定客戶端和服務(wù)器之間要交換哪些數(shù)據(jù)诱鞠。借用第3章披薩店的例子挎挖,我們可以說一個訂單需要外殼類型和配料。我們還需要選擇一個客戶端和服務(wù)器之間傳遞信息的數(shù)據(jù)格式航夺。XML和JSON都是很好的選擇蕉朵,但是對于可讀性的考慮,我們會選擇JSON敷存。
到這個時候墓造,你可以松口氣了,我們已經(jīng)設(shè)計(jì)了一個實(shí)用的API锚烦。下面是客戶端和服務(wù)器使用這個API進(jìn)行交互的例子:
把資源聯(lián)系在一起
我們的披薩店的API看起來不錯觅闽,訂單前所未有的多。事實(shí)上涮俄,生意確實(shí)不錯蛉拙,我們決定開始追蹤顧客的訂單來估計(jì)顧客的忠誠度。一個簡單的方式是增加一個新的顧客資源彻亲。
和訂單資源一樣孕锄,我們的顧客資源需要一些終端。根據(jù)規(guī)定苞尝, /customers和/customers/<id>非常合適畸肆。我們將會跳過細(xì)節(jié),但是我們要說我們決定了對于每個終端來說哪些動作是有意義的宙址,以及哪些數(shù)據(jù)代表了一個顧客轴脐。假設(shè)我們已經(jīng)完成了這些,我們就遇到了一個有趣的問題:我們怎么把顧客和訂單聯(lián)系起來呢抡砂?
REST的實(shí)踐者們在如何解決聯(lián)系資源的問題上發(fā)生了分歧大咱。一些人認(rèn)為這種層次結(jié)構(gòu)需要增長,增加終端注益,比如/customers/5/orders作為訂單的所有顧客#5碴巾,/customers/5/orders/3作為顧客#5的第三個訂單。另一些人認(rèn)為應(yīng)該在資源的數(shù)據(jù)中增加聯(lián)系的細(xì)節(jié)來保持結(jié)構(gòu)的扁平丑搔。在這種模型下厦瓢,創(chuàng)建一個訂單需要跟訂單細(xì)節(jié)一個傳遞一個customer_id字段提揍。這兩種方案都在使用,所以都需要了解旷痕。
由于系統(tǒng)中數(shù)據(jù)的增長碳锈,列舉所有訂單的終端就變得不切實(shí)際了。假設(shè)我們的披薩店有三百萬已完成的訂單欺抗,你想找出有多少用意大利辣香腸作為配料售碳。向/orders發(fā)送一個 GET 請求,并且獲取全部的三百萬訂單就不太合適了绞呈。幸好贸人,REST有一個實(shí)用的方法在數(shù)據(jù)中進(jìn)行搜索。
URL還有另一個我們還沒有提到過的組成部分佃声,查詢字符串艺智。查詢就是搜索,字符串就是文本圾亏。查詢字符串是URL末尾傳遞東西到API的少量文本十拣。比如這個URL中問號后面的所有東西都是查詢字符串 http://example.com/orders?key=value 。
REST API使用查詢字符串來定義查詢的細(xì)節(jié)志鹃。這些細(xì)節(jié)被稱為查詢參數(shù)。API決定它接受哪些參數(shù)曹铃,以及為了使查詢有效應(yīng)該為這些參數(shù)使用的準(zhǔn)確的名字缰趋。我們的披薩店API允許客戶端使用這個URL來按照配料進(jìn)行查詢: http://example.com/orders?topping=pepperoni ∩录客戶端可以包含多個查詢參數(shù)秘血,只要將這些參數(shù)一個接一個的列出來,中間用and符號(“&”)分隔评甜。比如 http://example.com/orders?topping=pepperoni&crust=thin 灰粮。
查詢字符串的另一個用途是限制每個請求返回的數(shù)據(jù)的數(shù)量。通常忍坷,API會將結(jié)果分組(每組100或500條記錄)谋竖,每次返回一組。這種分割數(shù)據(jù)的處理就是我們知道的分頁(類似于書的分頁)承匣。為了允許客戶端訪問數(shù)據(jù)的任一頁,API會支持一個查詢參數(shù)锤悄,這個參數(shù)允許客戶端指定它想獲取數(shù)據(jù)的哪一頁韧骗。在披薩店API中,我們通過允許客戶端指定兩個參數(shù):page和size來支持分頁零聚。如果客戶端發(fā)送這樣的請求 GET /orders?page=2&size=200 我們就知道他們需要結(jié)果的第二頁袍暴,煤業(yè)包含200條記錄些侍,也就是訂單201-400。
譯自