好RESTful API的設(shè)計(jì)原則

轉(zhuǎn)載自 http://www.cnblogs.com/moonz-wu/p/4211626.html

做出一個(gè)好的API設(shè)計(jì)很難。API表達(dá)的是你的數(shù)據(jù)和你的數(shù)據(jù)使用者之間的契約套鹅。打破這個(gè)契約將會(huì)招致很多憤怒的郵件汰具,和一大堆傷心的用戶-因?yàn)樗麄兪謾C(jī)上的App不工作了留荔。而文檔化只能達(dá)到一半的效果,并且也很難找到一個(gè)愿意寫文檔的程序員杰妓。

你所能做的最重要一件事來(lái)提高服務(wù)的價(jià)值就是創(chuàng)建一個(gè)API碘勉。因?yàn)殡S著其他服務(wù)的成長(zhǎng),有這樣一個(gè)API會(huì)使你的服務(wù)或者核心應(yīng)用將有機(jī)會(huì)變成一個(gè)平臺(tái)倍宾。環(huán)顧一下現(xiàn)有的這些大公司:Facebook高职,Twitter辞州,Google, Github埃元,Amazon牵啦,Netflix等哈雏。如果當(dāng)時(shí)他們沒(méi)有通過(guò)API來(lái)開放數(shù)據(jù)的話,也不可能成長(zhǎng)到如今的規(guī)模土浸。事實(shí)上彭羹,整個(gè)行業(yè)存在的唯一目的就是消費(fèi)所謂平臺(tái)上的數(shù)據(jù)。

你的API越容易使用还最,那么就會(huì)有越多的人去用它

本文提到的這些原則拓轻,如果你的API能嚴(yán)格按照這些原則來(lái)設(shè)計(jì),使用者就可以知道它接下來(lái)要做什么勿锅,并且能減少大量不必要的疑惑或者是憤怒的郵件枣氧。我已經(jīng)把所有內(nèi)容都整理到不同的主題里了,你無(wú)需按順序去閱讀它张弛。

定義

這里有一些非常重要的術(shù)語(yǔ)宗挥,我將在本文里面一直用到它們:

資源:一個(gè)對(duì)象的單獨(dú)實(shí)例丹喻,如一只動(dòng)物

集合:一群同種對(duì)象俊性,如動(dòng)物

HTTP:跨網(wǎng)絡(luò)的通信協(xié)議

客戶端:可以創(chuàng)建HTTP請(qǐng)求的客戶端應(yīng)用程序

第三方開發(fā)者:這個(gè)開發(fā)者不屬于你的項(xiàng)目但是有想使用你的數(shù)據(jù)

服務(wù)器:一個(gè)HTTP服務(wù)器或者應(yīng)用程序,客戶端可以跨網(wǎng)絡(luò)訪問(wèn)它

端點(diǎn):這個(gè)API在服務(wù)器上的URL用于表達(dá)一個(gè)資源或者一個(gè)集合

冪等:無(wú)邊際效應(yīng)踢械,多次操作得到相同的結(jié)果

URL段:在URL里面已斜杠分隔的內(nèi)容

數(shù)據(jù)設(shè)計(jì)與抽象

規(guī)劃好你的API的外觀要先于開發(fā)它實(shí)際的功能内列。首先你要知道數(shù)據(jù)該如何設(shè)計(jì)和核心服務(wù)/應(yīng)用程序會(huì)如何工作。如果你純粹新開發(fā)一個(gè)API嫩与,這樣會(huì)比較容易一些交排。但如果你是往已有的項(xiàng)目中增加API埃篓,你可能需要提供更多的抽象。

有時(shí)候一個(gè)集合可以表達(dá)一個(gè)數(shù)據(jù)庫(kù)表同窘,而一個(gè)資源可以表達(dá)成里面的一行記錄,但是這并不是常態(tài)桨仿。事實(shí)上案狠,你的API應(yīng)該盡可能通過(guò)抽象來(lái)分離數(shù)據(jù)與業(yè)務(wù)邏輯骂铁。這點(diǎn)非常重要罩抗,只有這樣做你才不會(huì)打擊到那些擁有復(fù)雜業(yè)務(wù)的第三方開發(fā)者套蒂,否則他們是不會(huì)使用你的API的。

當(dāng)然你的服務(wù)可能很多部分是不應(yīng)該通過(guò)API暴露出去的烁挟。比較常見的例子就是很多API是不允許第三方來(lái)創(chuàng)建用戶的骨坑。

動(dòng)詞

顯然你了解GET和POST請(qǐng)求。當(dāng)你用瀏覽器去訪問(wèn)不同頁(yè)面的時(shí)候且警,這兩個(gè)是最常見的請(qǐng)求斑芜。POST術(shù)語(yǔ)如此流行以至于開始侵?jǐn)_通俗用語(yǔ)祟霍。即使是那些不知道互聯(lián)網(wǎng)如何工作的人們也能“post”一些東西到朋友的Facebook墻上。

這里至少有四個(gè)半非常重要的HTTP動(dòng)詞需要你知道大州。我之所以說(shuō)“半個(gè)”的意思是PATCH這個(gè)動(dòng)詞非常類似于PUT厦画,并且它們倆也常常被開發(fā)者綁定到同一個(gè)API上。

GET (選擇):從服務(wù)器上獲取一個(gè)具體的資源或者一個(gè)資源列表力试。

POST (創(chuàng)建): 在服務(wù)器上創(chuàng)建一個(gè)新的資源排嫌。

PUT (更新):以整體的方式更新服務(wù)器上的一個(gè)資源淳地。

PATCH (更新):只更新服務(wù)器上一個(gè)資源的一個(gè)屬性。

DELETE (刪除):刪除服務(wù)器上的一個(gè)資源伍伤。

還有兩個(gè)不常用的HTTP動(dòng)詞:

HEAD : 獲取一個(gè)資源的元數(shù)據(jù)遣钳,如數(shù)據(jù)的哈希值或最后的更新時(shí)間蕴茴。

OPTIONS:獲取客戶端能對(duì)資源做什么操作的信息。

一個(gè)好的RESTful API只允許第三方調(diào)用者使用這四個(gè)半HTTP動(dòng)詞進(jìn)行數(shù)據(jù)交互蒋畜,并且在URL段里面不出現(xiàn)任何其他的動(dòng)詞晃听。

一般來(lái)說(shuō)能扒,GET請(qǐng)求可以被瀏覽器緩存(通常也是這樣的)。例如辛润,緩存請(qǐng)求頭用于第二次用戶的POST請(qǐng)求见秤。HEAD請(qǐng)求是基于一個(gè)無(wú)響應(yīng)體的GET請(qǐng)求鹃答,并且也可以被緩存的。

版本化

無(wú)論你正在構(gòu)建什么置济,無(wú)論你在入手前做了多少計(jì)劃,你核心的應(yīng)用總會(huì)發(fā)生變化护盈,數(shù)據(jù)關(guān)系也會(huì)變化羞酗,資源上的屬性也會(huì)被增加或刪除檀轨。只要你的項(xiàng)目還活著,并且有大量的用戶在用撤师,這種情況總是會(huì)發(fā)生拧揽。

請(qǐng)謹(jǐn)記一點(diǎn)淤袜,API是服務(wù)器與客戶端之間的一個(gè)公共契約衰伯。如果你對(duì)服務(wù)器上的API做了一個(gè)更改意鲸,并且這些更改無(wú)法向后兼容,那么你就打破了這個(gè)契約读慎,客戶端又會(huì)要求你重新支持它槐雾。為了避免這樣的事情,你既要確保應(yīng)用程序逐步的演變株灸,又要讓客戶端滿意慌烧。那么你必須在引入新版本API的同時(shí)保持舊版本API仍然可用鸠儿。

注:如果你只是簡(jiǎn)單的增加一個(gè)新的特性到API上,如資源上的一個(gè)新屬性或者增加一個(gè)新的端點(diǎn)淑翼,你不需要增加API的版本玄括。因?yàn)檫@些并不會(huì)造成向后兼容性的問(wèn)題,你只需要修改文檔即可胃惜。

隨著時(shí)間的推移船殉,你可能聲明不再支持某些舊版本的API斯嚎。申明不支持一個(gè)特性并不意味著關(guān)閉或者破壞它堡僻。而是告訴客戶端舊版本的API將在某個(gè)特定的時(shí)間被刪除,并且建議他們使用新版本的API硼讽。

一個(gè)好的RESTful API會(huì)在URL中包含版本信息牲阁。另一種比較常見的方案是在請(qǐng)求頭里面保持版本信息城菊。但是跟很多不同的第三方開發(fā)者一起工作后,我可以很明確的告訴你赚爵,在請(qǐng)求頭里面包含版本信息遠(yuǎn)沒(méi)有放在URL里面來(lái)的容易冀膝。

分析

所謂API分析就是持續(xù)跟蹤那些正為人使用的API的版本和端點(diǎn)信息霎挟。而這可能就跟每次請(qǐng)求都往數(shù)據(jù)庫(kù)增加一個(gè)整數(shù)那樣簡(jiǎn)單。有很多的原因顯示API跟蹤分析是一個(gè)好主意赐纱,例如疙描,對(duì)那些使用最廣泛的API來(lái)說(shuō)效率是最重要的。

第三方開發(fā)者通常會(huì)關(guān)注API的構(gòu)建目的久又,其中最重要的一個(gè)目的是你決定什么時(shí)候不再支持某個(gè)版本地消。你需要明確的告知開發(fā)者他們正在使用那些即將被移除的API特性畏妖。這是一個(gè)很好的方式在你準(zhǔn)備刪除舊的API之前去提醒他們進(jìn)行升級(jí)戒劫。

當(dāng)然第三方開發(fā)者的通知流程可以以某種條件被自動(dòng)觸發(fā),例如每當(dāng)一個(gè)過(guò)時(shí)的特性上發(fā)生10000次請(qǐng)求時(shí)就發(fā)郵件通知開發(fā)者玻熙。

API根URL

無(wú)論你信不信疯攒,API的根地址很重要敬尺。當(dāng)一個(gè)開發(fā)者接手了一個(gè)舊項(xiàng)目(如進(jìn)行代碼考古時(shí))砂吞。而這個(gè)項(xiàng)目正在使用你的API崎溃,同時(shí)開發(fā)者還想構(gòu)建一個(gè)新的特性袁串,但他們完全不知道你的服務(wù)。幸運(yùn)的是他們知道客戶端對(duì)外調(diào)用的那些URL列表赎瑰。讓你的API根入口點(diǎn)保持盡可能的簡(jiǎn)單是很重要的破镰,因?yàn)殚_發(fā)者很可能一看到那些冗長(zhǎng)而又復(fù)雜的URL就轉(zhuǎn)身而走。

這里有兩個(gè)常見的URL根例子:

https://example.org/api/v1/*

https://api.example.com/v1/*

如果你的應(yīng)用很龐大或者你預(yù)期它將會(huì)變的很龐大集惋,那么將API放到子域下通常是一個(gè)好選擇踩娘。這種做法可以保持某些規(guī)陌运牵化上的靈活性。

如果你覺(jué)得你的API不會(huì)變的很龐大习寸,或是你只是想讓應(yīng)用安裝更簡(jiǎn)單些(如你想用相同的框架來(lái)支持站點(diǎn)和API)霞溪,將你的API放到根域名下也是可以的。

讓API根擁有一些內(nèi)容通常也是個(gè)好主意中捆。Github的API根就是一個(gè)典型的例子鸯匹。從個(gè)人角度來(lái)說(shuō)我是一個(gè)通過(guò)根URL發(fā)布信息的粉絲,這對(duì)很多人來(lái)說(shuō)是有用的泄伪,例如如何獲取API相關(guān)的開發(fā)文檔殴蓬。

同樣也請(qǐng)注意HTTPS前綴,一個(gè)好的RESTful API總是基于HTTPS來(lái)發(fā)布的蟋滴。

端點(diǎn)

一個(gè)端點(diǎn)就是指向特定資源或資源集合的URL染厅。

如果你正在構(gòu)建一個(gè)虛構(gòu)的API來(lái)展現(xiàn)幾個(gè)不同的動(dòng)物園津函,每一個(gè)動(dòng)物園又包含很多動(dòng)物肖粮,員工和每個(gè)動(dòng)物的物種,你可能會(huì)有如下的端點(diǎn)信息:

https://api.example.com/v1/zoos

https://api.example.com/v1/animals

https://api.example.com/v1/animal_types

https://api.example.com/v1/employees

針對(duì)每一個(gè)端點(diǎn)來(lái)說(shuō)尔苦,你可能想列出所有可行的HTTP動(dòng)詞和端點(diǎn)的組合涩馆。如下所示,請(qǐng)注意我把HTTP動(dòng)詞都放在了虛構(gòu)的API之前允坚,正如將同樣的注解放在每一個(gè)HTTP請(qǐng)求頭里一樣魂那。

GET /zoos: List all Zoos (ID and Name, not too much detail)

POST /zoos: Create a new Zoo

GET /zoos/ZID: Retrieve an entire Zoo object

PUT /zoos/ZID: Update a Zoo (entire object)

PATCH /zoos/ZID: Update a Zoo (partial object)

DELETE /zoos/ZID: Delete a Zoo

GET /zoos/ZID/animals: Retrieve a listing of Animals (ID and Name).

GET /animals: List all Animals (ID and Name).

POST /animals: Create a new Animal

GET /animals/AID: Retrieve an Animal object

PUT /animals/AID: Update an Animal (entire object)

PATCH /animals/AID: Update an Animal (partial object)

GET /animal_types: Retrieve a listing (ID and Name) of all Animal Types

GET /animal_types/ATID: Retrieve an entire Animal Type object

GET /employees: Retrieve an entire list of Employees

GET /employees/EID: Retreive a specific Employee

GET /zoos/ZID/employees: Retrieve a listing of Employees (ID and Name) who work at this Zoo

POST /employees: Create a new Employee

POST /zoos/ZID/employees: Hire an Employee at a specific Zoo

DELETE /zoos/ZID/employees/EID: Fire an Employee from a specific Zoo

在上面的列表里,ZID表示動(dòng)物園的ID稠项, AID表示動(dòng)物的ID冰寻,EID表示雇員的ID,還有ATID表示物種的ID皿渗。讓文檔里所有的東西都有一個(gè)關(guān)鍵字是一個(gè)好主意斩芭。

為了簡(jiǎn)潔起見轻腺,我已經(jīng)省略了所有API共有的URL前綴。作為溝通方式這沒(méi)什么問(wèn)題划乖,但是如果你真要寫到API文檔中贬养,那就必須包含完整的路徑(如,GET http://api.example.com/v1/animal_type/ATID)琴庵。

請(qǐng)注意如何展示數(shù)據(jù)之間的關(guān)系误算,特別是雇員與動(dòng)物園之間的多對(duì)多關(guān)系。通過(guò)添加一個(gè)額外的URL段就可以實(shí)現(xiàn)更多的交互能力迷殿。當(dāng)然沒(méi)有一個(gè)HTTP動(dòng)詞能表示正在解雇一個(gè)人儿礼,但是你可以使用DELETE一個(gè)動(dòng)物園里的雇員來(lái)達(dá)到相同的效果。

過(guò)濾器

當(dāng)客戶端創(chuàng)建了一個(gè)請(qǐng)求來(lái)獲取一個(gè)對(duì)象列表時(shí)庆寺,很重要一點(diǎn)就是你要返回給他們一個(gè)符合查詢條件的所有對(duì)象的列表蚊夫。這個(gè)列表可能會(huì)很大。但你不能隨意給返回?cái)?shù)據(jù)的數(shù)量做限制懦尝。因?yàn)檫@些無(wú)謂的限制會(huì)導(dǎo)致第三方開發(fā)者不知道發(fā)生了什么知纷。如果他們請(qǐng)求一個(gè)確切的集合并且要遍歷結(jié)果,然而他們發(fā)現(xiàn)只拿到了100條數(shù)據(jù)陵霉。接下來(lái)他們就不得不去查找這個(gè)限制條件的出處琅轧。到底是ORM的bug導(dǎo)致的,還是因?yàn)榫W(wǎng)絡(luò)截?cái)嗔舜髷?shù)據(jù)包踊挠?

盡可能減少那些會(huì)影響到第三方開發(fā)者的無(wú)謂限制

這點(diǎn)很重要乍桂,但你可以讓客戶端自己對(duì)結(jié)果做一些具體的過(guò)濾或限制。這么做最重要的一個(gè)原因是可以最小化網(wǎng)絡(luò)傳輸效床,并讓客戶端盡可能快的得到查詢結(jié)果模蜡。其次是客戶端可能比較懶,如果這時(shí)服務(wù)器能對(duì)結(jié)果做一些過(guò)濾或分頁(yè)扁凛,對(duì)大家都是好事。另外一個(gè)不那么重要的原因是(從客戶端角度來(lái)說(shuō))闯传,對(duì)服務(wù)器來(lái)說(shuō)響應(yīng)請(qǐng)求的負(fù)載越少越好谨朝。

過(guò)濾器是最有效的方式去處理那些獲取資源集合的請(qǐng)求。所以只要出現(xiàn)GET的請(qǐng)求甥绿,就應(yīng)該通過(guò)URL來(lái)過(guò)濾信息字币。以下有一些過(guò)濾器的例子,可能是你想要填加到API中的:

?limit=10: 減少返回給客戶端的結(jié)果數(shù)量(用于分頁(yè))

?offset=10: 發(fā)送一堆信息給客戶端(用于分頁(yè))

?animal_type_id=1: 使用條件匹配來(lái)過(guò)濾記錄

?sortby=name&order=asc: ?對(duì)結(jié)果按特定屬性進(jìn)行排序

有些過(guò)濾器可能會(huì)與端點(diǎn)URL的效果重復(fù)共缕。例如我之前提到的GET /zoo/ZID/animals洗出。它也同樣可以通過(guò)GET /animals?zoo_id=ZID來(lái)實(shí)現(xiàn)。獨(dú)立的端點(diǎn)會(huì)讓客戶端更好過(guò)一些图谷,因?yàn)樗麄兊男枨笸瞿愕念A(yù)期翩活。本文中提到這種冗余差異可能對(duì)第三方開發(fā)者并不可見阱洪。

無(wú)論怎么說(shuō),當(dāng)你準(zhǔn)備過(guò)濾或排序數(shù)據(jù)時(shí)菠镇,你必須明確的將那些客戶端可以過(guò)濾或排序的列放到白名單中冗荸,因?yàn)槲覀儾幌雽⑷魏蔚臄?shù)據(jù)庫(kù)錯(cuò)誤發(fā)送給客戶端。

狀態(tài)碼

對(duì)于一個(gè)RESTful API來(lái)說(shuō)很重要的一點(diǎn)就是要使用HTTP的狀態(tài)碼利耍,因?yàn)樗鼈兪荋TTP的標(biāo)準(zhǔn)蚌本。很多的網(wǎng)絡(luò)設(shè)備都可以識(shí)別這些狀態(tài)碼,例如負(fù)載均衡器可能會(huì)通過(guò)配置來(lái)避免發(fā)送請(qǐng)求到一臺(tái)web服務(wù)器隘梨,如果這臺(tái)服務(wù)器已經(jīng)發(fā)送了很多的50x錯(cuò)誤回來(lái)程癌。這里有大量的HTTP狀態(tài)碼可以選擇,但是下面的列表只給出了一些重要的代碼作為一個(gè)參考:

200 ?OK – [GET] ?客戶端向服務(wù)器請(qǐng)求數(shù)據(jù)轴猎,服務(wù)器成功找到它們

201 ?CREATED – [POST/PUT/PATCH] ?客戶端向服務(wù)器提供數(shù)據(jù)嵌莉,服務(wù)器根據(jù)要求創(chuàng)建了一個(gè)資源

204 ?NO CONTENT – [DELETE] ?客戶端要求服務(wù)器刪除一個(gè)資源,服務(wù)器刪除成功

400 ?INVALID REQUEST – [POST/PUT/PATCH] ?客戶端向服務(wù)器提供了不正確的數(shù)據(jù)税稼,服務(wù)器什么也沒(méi)做

404 ?NOT FOUND – [*] ?客戶端引用了一個(gè)不存在的資源或集合烦秩,服務(wù)器什么也沒(méi)做

500 ?INTERNAL SERVER ERROR – [*] ?服務(wù)器發(fā)生內(nèi)部錯(cuò)誤,客戶端無(wú)法得知結(jié)果郎仆,即便請(qǐng)求已經(jīng)處理成功

狀態(tài)碼范圍

1xx范圍的狀態(tài)碼是保留給底層HTTP功能使用的只祠,并且估計(jì)在你的職業(yè)生涯里面也用不著手動(dòng)發(fā)送這樣一個(gè)狀態(tài)碼出來(lái)。

2xx范圍的狀態(tài)碼是保留給成功消息使用的扰肌,你盡可能的確保服務(wù)器總發(fā)送這些狀態(tài)碼給用戶抛寝。

3xx范圍的狀態(tài)碼是保留給重定向用的。大多數(shù)的API不會(huì)太常使用這類狀態(tài)碼曙旭,但是在新的超媒體樣式的API中會(huì)使用更多一些盗舰。

4xx范圍的狀態(tài)碼是保留給客戶端錯(cuò)誤用的。例如桂躏,客戶端提供了一些錯(cuò)誤的數(shù)據(jù)或請(qǐng)求了不存在的內(nèi)容钻趋。這些請(qǐng)求應(yīng)該是冪等的,不會(huì)改變?nèi)魏畏?wù)器的狀態(tài)剂习。

5xx范圍的狀態(tài)碼是保留給服務(wù)器端錯(cuò)誤用的蛮位。這些錯(cuò)誤常常是從底層的函數(shù)拋出來(lái)的,并且開發(fā)人員也通常沒(méi)法處理鳞绕。發(fā)送這類狀態(tài)碼的目的是確笔剩客戶端能得到一些響應(yīng)。收到5xx響應(yīng)后们何,客戶端沒(méi)辦法知道服務(wù)器端的狀態(tài)萄焦,所以這類狀態(tài)碼是要盡可能的避免。

預(yù)期的返回文檔

當(dāng)使用不同的HTTP動(dòng)詞向服務(wù)器請(qǐng)求時(shí)冤竹,客戶端需要在返回結(jié)果里面拿到一系列的信息拂封。下面的列表是非常經(jīng)典的RESTful API定義:

GET /collection: 返回一系列資源對(duì)象

GET /collection/resource: 返回單獨(dú)的資源對(duì)象

POST /collection: 返回新創(chuàng)建的資源對(duì)象

PUT /collection/resource: 返回完整的資源對(duì)象

PATCH /collection/resource: 返回完整的資源對(duì)象

DELETE /collection/resource: 返回一個(gè)空文檔

請(qǐng)注意當(dāng)一個(gè)客戶端創(chuàng)建一個(gè)資源時(shí)茬射,她們常常不知道新建資源的ID(也許還有其他的屬性,如創(chuàng)建和修改的時(shí)間戳等)烘苹。這些屬性將在隨后的請(qǐng)求中返回躲株,并且作為剛才POST請(qǐng)求的一個(gè)響應(yīng)結(jié)果。

認(rèn)證

服務(wù)器在大多數(shù)情況下是想確切的知道誰(shuí)創(chuàng)建了什么請(qǐng)求镣衡。當(dāng)然霜定,有些API是提供給公共用戶(匿名用戶)的,但是大部分時(shí)間里也是代表某人的利益廊鸥。

OAuth2.0提供了一個(gè)非常好的方法去做這件事望浩。在每一個(gè)請(qǐng)求里,你可以明確知道哪個(gè)客戶端創(chuàng)建了請(qǐng)求惰说,哪個(gè)用戶提交了請(qǐng)求磨德,并且提供了一種標(biāo)準(zhǔn)的訪問(wèn)過(guò)期機(jī)制或允許用戶從客戶端注銷,所有這些都不需要第三方的客戶端知道用戶的登陸認(rèn)證信息吆视。

還有OAuth1.0和xAuth同樣適用這樣的場(chǎng)景典挑。無(wú)論你選擇哪個(gè)方法,請(qǐng)確保它為多種不同語(yǔ)言/平臺(tái)上的庫(kù)提供了一些通用的并且設(shè)計(jì)良好文檔啦吧,因?yàn)槟愕挠脩艨赡軙?huì)使用這些語(yǔ)言和平臺(tái)來(lái)編寫客戶端您觉。

內(nèi)容類型

目前,大多數(shù)“精彩”的API都為RESTful接口提供JSON數(shù)據(jù)授滓。諸如Facebook琳水,Twitter,Github等等你所知的般堆。XML曾經(jīng)也火過(guò)一把(通常在一個(gè)大企業(yè)級(jí)環(huán)境下)在孝。這要感謝SOAP,不過(guò)它已經(jīng)掛了淮摔,并且我們也沒(méi)看到太多的API把HTML作為結(jié)果返回給客戶端(除非你在構(gòu)建一個(gè)爬蟲程序)私沮。

只要你返回給他們有效的數(shù)據(jù)格式,開發(fā)者就可以使用流行的語(yǔ)言和框架進(jìn)行解析和橙。如果你正在構(gòu)建一個(gè)通用的響應(yīng)對(duì)象仔燕,通過(guò)使用一個(gè)不同的序列化器,你也可以很容易的提供之前所提到的那些數(shù)據(jù)格式(不包括SOAP)胃碾。而你所要做的就是把使用方式放在響應(yīng)數(shù)據(jù)的接收頭里面。

有些API的創(chuàng)建者會(huì)推薦把.json, .xml, .html等文件的擴(kuò)展名放在URL里面來(lái)指示返回內(nèi)容類型筋搏,但我個(gè)人并不習(xí)慣這么做仆百。我依然喜歡通過(guò)接收頭來(lái)指示返回內(nèi)容類型(這也是HTTP標(biāo)準(zhǔn)的一部分),并且我覺(jué)得這么做也比較適當(dāng)一些奔脐。

超媒體API

超媒體API很可能就是RESTful API設(shè)計(jì)的將來(lái)俄周。超媒體是一個(gè)非常棒的概念吁讨,它回歸到了HTTP和HTML如何運(yùn)作的“本質(zhì)”。

在非超媒體RESTful API的情景中峦朗,URL端點(diǎn)是服務(wù)器與客戶端契約的一部分建丧。這些端點(diǎn)必須讓客戶端事先知道,并且修改它們也意味著客戶端可能再也無(wú)法與服務(wù)器通信了波势。你可以先假定這是一個(gè)限制翎朱。

時(shí)至今日,英特網(wǎng)上的API客戶端已經(jīng)不僅僅只有那些創(chuàng)建HTTP請(qǐng)求的用戶代理了尺铣。大多數(shù)HTTP請(qǐng)求是由人們通過(guò)瀏覽器產(chǎn)生的拴曲。人們不會(huì)被哪些預(yù)先定義好的RESTful API端點(diǎn)URL所束縛。是什么讓人們變的如此與眾不同凛忿?因?yàn)槿藗兛梢蚤喿x內(nèi)容澈灼,可以點(diǎn)擊他們感興趣的鏈接,并瀏覽一下網(wǎng)站店溢,然后跳到他們關(guān)注的內(nèi)容那里叁熔。即使一個(gè)URL改變了,人們也不會(huì)受到影響(除非他們事先給某個(gè)頁(yè)面做了書簽床牧,這時(shí)他們回到主頁(yè)并發(fā)現(xiàn)原來(lái)有一條新的路徑可以去往之前的頁(yè)面)荣回。

超媒體API概念的運(yùn)作跟人們的行為類似。通過(guò)請(qǐng)求API的根來(lái)獲得一個(gè)URL的列表叠赦,這個(gè)列表里面的每一個(gè)URL都指向一個(gè)集合驹马,并且提供了客戶端可以理解的信息來(lái)描述每一個(gè)集合。是否為每一個(gè)資源提供ID并不重要(或者不是必須的)除秀,只要提供URL即可糯累。

一個(gè)超媒體API一旦具有了客戶端,那么它就可以爬行鏈接并收集信息册踩,而URL總是在響應(yīng)中被更新泳姐,并且不需要如契約的一部分那樣事先被知曉。如果一個(gè)URL曾經(jīng)被緩存過(guò)暂吉,并且在隨后的請(qǐng)求中返回404錯(cuò)誤胖秒,那么客戶端可以很簡(jiǎn)單的回退到根URL并重新發(fā)現(xiàn)內(nèi)容。

在獲取集合中的一個(gè)資源列表時(shí)會(huì)返回一個(gè)屬性慕的,這個(gè)屬性包含了各個(gè)資源的完整URL阎肝。當(dāng)實(shí)施一個(gè)POST/PATCH/PUT請(qǐng)求后,響應(yīng)可以被一個(gè)3xx的狀態(tài)碼重定向到完整的資源上肮街。

JSON不僅告訴了我們需要定義哪些屬性作為URL风题,也告訴了我們?nèi)绾螌RL與當(dāng)前文檔關(guān)聯(lián)的語(yǔ)義。正如你猜的那樣,HTML就提供了這樣的信息沛硅。我們可能很樂(lè)意看到我們的API走完了完整的周期眼刃,并回到了處理HTML上來(lái)。想一下我們與CSS一起前行了多遠(yuǎn)摇肌,有一天我們可能再次看到它變成了一個(gè)通用實(shí)踐讓API和網(wǎng)站可以去使用相同的URL和內(nèi)容擂红。

文檔

老實(shí)說(shuō),即使你不能百分之百的遵循指南中的條款围小,你的API也不是那么糟糕昵骤。但是,如果你不為API準(zhǔn)備文檔的話吩抓,沒(méi)有人會(huì)知道怎么使用它涉茧,那它真的會(huì)成為一個(gè)糟糕的API。

讓你的文檔對(duì)那些未經(jīng)認(rèn)證的開發(fā)者也可用

不要使用文檔自動(dòng)化生成器疹娶,即便你用了伴栓,你也要保證自己審閱過(guò)并讓它具有更好的版式。

不要截?cái)嗍纠姓?qǐng)求與響應(yīng)的內(nèi)容雨饺,要展示完整的東西钳垮。并在文檔中使用高亮語(yǔ)法。

文檔化每一個(gè)端點(diǎn)所預(yù)期的響應(yīng)代碼和可能的錯(cuò)誤消息额港,和在什么情況下會(huì)產(chǎn)生這些的錯(cuò)誤消息

如果你有富余的時(shí)間饺窿,那就創(chuàng)建一個(gè)控制臺(tái)來(lái)讓開發(fā)者可以立即體驗(yàn)一下API的功能。創(chuàng)建一個(gè)控制臺(tái)并沒(méi)有想象中那么難移斩,并且開發(fā)者們(內(nèi)部或者第三方)也會(huì)因此而擁戴你肚医。

另外確保你的文檔能夠被打印。CSS是個(gè)強(qiáng)大的工具可以幫助到你向瓷。而且在打印的時(shí)候也不用太擔(dān)心邊側(cè)欄的問(wèn)題肠套。即便沒(méi)有人會(huì)打印到紙上,你也會(huì)驚奇的發(fā)現(xiàn)很多開發(fā)者愿意轉(zhuǎn)化成PDF格式進(jìn)行離線閱讀猖任。

勘誤:原始的HTTP封包

因?yàn)槲覀兯龅亩际腔贖TTP協(xié)議你稚,所以我將展示給你一個(gè)解析了的HTTP封包。我經(jīng)常很驚訝的發(fā)現(xiàn)有多少人不知道這些東西朱躺。當(dāng)客戶端發(fā)送一個(gè)請(qǐng)求道服務(wù)器時(shí)刁赖,他們會(huì)提供一個(gè)鍵值對(duì)集,先是一個(gè)頭长搀,緊跟著是兩個(gè)回車換行符宇弛,然后才是請(qǐng)求體。所有這些都是在一個(gè)封包里被發(fā)送源请。

服務(wù)器響應(yīng)也是同樣的鍵值對(duì)集枪芒,帶兩個(gè)回車換行符轿钠,然后是響應(yīng)體。HTTP就是一個(gè)請(qǐng)求/響應(yīng)協(xié)議病苗;它不支持“推送”模式(服務(wù)器直接發(fā)送數(shù)據(jù)給客戶端),除非你采用其他協(xié)議症汹,如Websockets硫朦。

當(dāng)你設(shè)計(jì)API時(shí),你應(yīng)該能夠使用工具去查看原始的HTTP封包背镇。Wireshark是個(gè)不錯(cuò)的選擇咬展。同時(shí),你也該采用一個(gè)框架/web服務(wù)器瞒斩,使你能夠在必要時(shí)修改某些字段的值破婆。

Example HTTP Request

POST /v1/animal HTTP/1.1

Host: api.example.org

Accept: application/json

Content-Type: application/json

Content-Length: 24

{

"name": "Gir",

"animal_type": 12

}

Example HTTP Response

HTTP/1.1 200 OK

Date: Wed, 18 Dec 2013 06:08:22 GMT

Content-Type: application/json

Access-Control-Max-Age: 1728000

Cache-Control: no-cache

{

"id": 12,

"created": 1386363036,

"modified": 1386363036,

"name": "Gir",

"animal_type": 12

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市胸囱,隨后出現(xiàn)的幾起案子祷舀,更是在濱河造成了極大的恐慌,老刑警劉巖烹笔,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件裳扯,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡谤职,警方通過(guò)查閱死者的電腦和手機(jī)饰豺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)允蜈,“玉大人冤吨,你說(shuō)我怎么就攤上這事∪奶祝” “怎么了漩蟆?”我有些...
    開封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)凤跑。 經(jīng)常有香客問(wèn)我爆安,道長(zhǎng),這世上最難降的妖魔是什么仔引? 我笑而不...
    開封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任扔仓,我火速辦了婚禮,結(jié)果婚禮上咖耘,老公的妹妹穿的比我還像新娘翘簇。我一直安慰自己,他們只是感情好儿倒,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開白布版保。 她就那樣靜靜地躺著呜笑,像睡著了一般。 火紅的嫁衣襯著肌膚如雪彻犁。 梳的紋絲不亂的頭發(fā)上叫胁,一...
    開封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音汞幢,去河邊找鬼驼鹅。 笑死,一個(gè)胖子當(dāng)著我的面吹牛森篷,可吹牛的內(nèi)容都是我干的输钩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼仲智,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼买乃!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起钓辆,我...
    開封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤剪验,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后前联,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碉咆,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年蛀恩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了疫铜。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡双谆,死狀恐怖壳咕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情顽馋,我是刑警寧澤谓厘,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站寸谜,受9級(jí)特大地震影響竟稳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜熊痴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一他爸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧果善,春花似錦诊笤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)纪他。三九已至,卻和暖如春晾匠,著一層夾襖步出監(jiān)牢的瞬間茶袒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工凉馆, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留弹谁,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓句喜,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親沟于。 傳聞我的和親對(duì)象是個(gè)殘疾皇子咳胃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容