REST API Design Guidelineshtml
V 1.0.201208 Draft 5git
Last Updated: 08/31/2012github
本文檔旨在規範REST API的設計和開發。web
REST API容許Newegg內部和外部開發人員經過編程方式訪問Newegg系統的各類對象與資源。算法
REST API需最大限度地知足平臺無關性。數據庫
REST全稱「Representational State Transfer」。REST是一組架構設計原則而不是規範。express
1) 可尋址性(Addressability)apache
每一個資源都至少有一個URI標識。每一個URI標識只能表明惟一一個資源。django
2) 無狀態性(Statelessness)編程
服務器不該保存「應用狀態」。相關狀態由客戶端自行保存。
3) 統一接口(Uniform Interface)
對全部資源的操做都採用一致的方式:使用GET,POST,PUT,DELETE等HTTP請求來瀏覽,添加,修改和刪除資源。
4) 可連通性(Connectedness)
資源之間是彼此聯繫的。客戶端的狀態遷移是在服務端的指引下完成的,即服務端響應中附帶相關連接,客服端根據該連接而不是預先約定的URI格式去請求相關資源。這避免了客戶端和服務端的強耦合。
1)不要假設你知道客戶是誰
2)面向資源而不是對象或活動
3)Web服務應該是粗粒度的(Coarse Grained)。Web服務一般會映射到頂級域對象(Top Level Domain Objects)
4)每一個API應專一一件事。API儘量小,但也不是越小越好
5)不能影響已經存在的API
6)考慮API設計決策的性能後果。很差的設計影響性能
7)禁止任意改變實現
8)不要實現Service Contract中沒有的功能特性
9)向外部客戶隱藏內部商業實體。經過Web服務公開每個持久對象不是一個好的作法。它暴露了內部應用程序設計,增長了冗餘和未使用服務,而且使服務很難理解和使用
10)不要讓實施細節「泄露」到API中,例如磁盤、傳輸格式等
11)顯性邊界:只能經過服務接口層訪問
12)在服務和客戶之間必須有共享詞彙表
13)檢測和管理重複請求(Idempotent)。確保重複請求不被處理
14)假設無效請求的存在
15)在發送和接收XML時,遵循「寬進嚴出」原則。在接收時,只校驗真正須要Schema的最小集合
16)確保能處理不按順序到達的消息
17)若是集合資源有可能有大量的數據返回,務必提供分頁功能支持
18)避免須要異常處理的返回參數:返回0字節數組或空集合,不是 NULL
19)不要讓客戶端使用異常進行流程控制
20)容易學習和使用,甚至是在沒有文檔的狀況下
21)不容易誤使用
22)對於重用,須要好的設計和好的文檔。
資源是REST架構的基礎,是系統中全部可用URI來定位的具體或抽象實體。全部的操做都是面向資源而不是對象或活動。設計REST API的第一步是設計資源模型。這個過程相似於對一個關係數據庫系統的數據建模,或對一個面向對象系統的對象建模。
資源模型識別和歸類全部客戶須要和服務進行交互的資源。
1) 個體資源
和集合資源相對而言。例如單一一個Customer,一張Sales Order,客戶的一個Shipping Address等。每一個個體資源都有惟一的ID。
2) 集合資源
包含0到多個個體資源。例如全部Customers,一個客戶的全部Sales Orders等。一般將集合做爲一個工廠,經過向集合提交一個HTTP POST請求來建立一個新成員。
3) 複合資源
由2種以上的資源構成。例如Customer資源能夠包括Billing Address資源和Shipping Address資源。複合資源使得它們的呈現和別的資源有重疊,一般只用於查詢。
4) 抽象資源
某些操做沒法映射到普通的HTTP方法上,例如計算運費,校驗信用卡等。這時將對應的處理函數抽象成一個資源,例如ShippingCalculator,CreditCardValidator。資源的呈現就是計算的結果。
5) Controller資源(事務)
爲資源設計Controller能夠將更新多個資源的操做做爲原子操做處理,或者可以使客戶觸發一系列複雜的業務操做。例如:Void SO操做,假設須要1)更新Order資源,2)發送Email給客戶,與其分別更新/order/{order#}資源和添加/order/{order#}/email資源,能夠設計一個controller資源/order/cancellation/{order#}來處理Void SO 操做。
1) 從客戶(使用者)的角度出發進行思考和設計
2) 數據庫表或者對象模型和資源不必定是一一對應的
3) 資源粒度
4) 關於Tunneling
當客戶用同一URI來進行不一樣的操做,或致使所謂的「Tunneling」。Tunneling下降了協議級別的可見性,由於請求的可見部分如URI,HTTP方法,HTTP頭和媒體類型並無無歧義地描述操做。所以須要儘可能避免Tunneling。
標識每一個資源的URI必須具備惟一性。
HTTP協議規範[3]沒有對URL長度進行限制。但特定的瀏覽器及服務器可能會對它有所限制。IE對URL長度的限制是2083字符[4]。其餘瀏覽器(Firefox、Safari等)理論上對URL長度沒有限制。
服務端可能返回的錯誤: 414 (Request-URI Too Long)
1) URL必須符合W3 Uniform Resource Identifier語法規範。這意味着URL中的字符只能是ASCII字符集的一個特殊子集。
集合 |
字符 |
URL中用途 |
字母數字 |
a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 |
文本, 協議 (http), 端口 (8080)等. |
非保留字符 |
- _ . ~ |
文本 |
保留字符 |
! * ' ( ) ; : @ & = + $ , / ? % # [ ] |
控制字符, 文本 |
2) URL中的特殊字符(例如漢字)和用於文本的保留字符(例如「?」)必須通過URL編碼(URL Encoding)。
3) 根據RFC3986,空格會被編碼成 %20;但若是使用application/x-www-form-urlencoded媒體類型(HTTP form元素使用[5]),空格會被編碼成 +。服務端須要正確地識別編碼方式。
4) RFC3986定義URL是大小寫敏感的,但協議、域名和參數部分除外。
5) 必須將任何用戶的URL輸入看成文本對待。
1) 無歧義識別目標資源
2) 模塊化。Web服務的資源和方法可能增加很快。使用Domain和sub domain對資源進行分組或分隔。只使用規定的domain和sub domain名字。
3) 在設計階段就要規劃好URI結構:{host: port}/{ domain}/{version#}/{sub domain}/{resource}
4) URL長度不超過2000字符(包括協議、域名和端口部分)
5) URL主幹部分(不包括參數)所有小寫;參數名稱第一個字母小寫
6) 資源所有使用單數形式。例如查詢order集合,使用/order而不是/orders
7) 使用連字符 - ,不使用下劃線 _
8) 使用 / 表示資源的層次關係。例如:GET /customer/{customer#}。
9) URL的末尾不添加 / 字符
10) 使用 ,或 ;表示平行資源。例如:POST /order/{order1},{order2} : 同時處理{order1}和{order2}。
11) URI標識的是資源而不是操做,URI中的資源儘可能只有名詞形式。例如,從一個帳戶轉錢到另外一個帳戶,不是使用動詞transfer,而是把之當作是一次交易記錄,使用名詞形式transaction。例如:
Request POST /transactions HTTP/1.1 Host: <snip, and all other headers> from=1&to=2&amount=500.00
Response HTTP/1.1 201 OK Date: Sun, 3 Jul 2011 23:59:59 GMT Content-Type: application/json Content-Length: 12345 Location: http://foo.com/transactions/1 [6] {"transaction":{"id":1,"uri":"/transactions/1","type":"transfer"}} |
12) URI對客戶是不透明(opaque)的,即客戶不須要了解URL的結構以自行拼接URI。可將實際的URI包含在服務響應中。這樣作的壞處是使輸出變大,但能夠很容易將客戶引向新的URI,從而下降客戶端和服務端的耦合。
13) URI儘可能不要改變。若是必須改變,當客戶訪問舊的URI使用301(Move Permanently)將客戶從新引導到新的URI;或者在指定的時間後,返回410(Gone)或者404(Not Found)。例如:
Request GET /order/12345678 HTTP/1.1 Host: <neweggcentral-rest> Accept: application/xml; charset=UTF-8
Response #1 HTTP/1.1 301 Move Permanently Location: http:// neweggcentral-rest2/order/12345678
Response #2 HTTP/1.1 410 Gone Content-Type:application/xml; charset=UTF-8 Expires: Sat, 01 Jan 2013 00:00:00 GMT |
在HTTP協議中定義了8種方法[7]。REST通常只使用有限的HTTP操做集合,包括HTTP GET, PUT, DELETE和POST。
在處理HTTP方法時,RFC 2616規範定義了兩個重要概念:」Safe」和」Idempotent」(冪等)。」Safe」指方法不能修改資源的狀態;」Idempotent」指將一個請求發送屢次和發送一次的結果是同樣的。
下表列出了用於RESTful服務的4種HTTP操做和它們的基本語義:
方法 |
Safe |
Idempotent |
描述 |
GET |
Y |
Y |
根據URL獲取資源的一個呈現。 GET請求永遠不該致使資源狀態的改變。 |
PUT |
N |
Y |
更新資源(若是指定資源不存在,在容許的條件下建立新資源) |
DELETE |
N |
Y |
刪除資源 |
POST |
N |
N |
提交數據到服務器,對指定資源進行處理。用於建立新資源或者向已有資源添加數據。 |
經過HTTP Allow頭能夠知道資源支持哪些HTTP方法。例如:
Allow: GET, PUT, DELETE
1) 支持GET, POST, PUT, DELETE四種HTTP方法。使用GET查詢資源;POST建立資源;PUT更新資源;DELETE刪除資源
2) 若是服務器、防火牆或網絡不支持PUT和DELETE操做,應容許使用method參數,經過POST方法執行相應操做。例如:
POST https://graph.facebook.com/COMMENT_ID?method=delete.
REST框架下資源呈現和資源自己是有區別的。資源具體呈現出來的形式,叫作它的"表現形式"(representation)。各類表現形式由已定義的媒體類型、字符集和編碼的字節流構成。一個資源能夠沒有,或者有一個,或者有多個表現形式。若是有多個表現形式存在,則稱該資源是可協商的(negotiable)。
服務驅動的內容協商使用HTTP請求頭來決定響應變體。服務驅動的侷限:
1) 內容協商不包括貨幣單位、距離單位、日期格式等區域設置。
2) 有時候因爲複雜的本地化需求,可能須要爲不一樣的區域維護不一樣的資源。
3) Web瀏覽器廣泛會爲Accept頭設置一個範圍很廣的媒體類型選項,使得瀏覽器難以經過內容協商機制來呈現資源。
代理驅動[8]的內容協商是對每種響應變體(資源呈現)都使用不一樣的URI。雖然能夠爲全部的Accept-*頭實施代理協商,但最常使用的是媒體類型和語言。代理協商廣泛使用的方法包括:
方法 |
示例 |
經過查詢參數 |
/order/status/12345678? format=json /order/status/12345678? format=xml |
經過子域名 |
en.wikipedia.org (顯示英語) de.wikipedia.org(顯示德語) |
客戶端可經過HTTP頭Accept-*將它的偏好和處理能力傳遞給服務端,包括表現形式(representation)、語言偏好、字符集、對壓縮的支持等。
HTTP請求頭(Accept-*[9]) |
描述 |
Accept |
設置可接受的媒體格式。可指定多種媒體格式,媒體格式越具體優先級越高。另外,經過在每種媒體格式後添加q參數[10]來設置相對偏好。q=0表示不接受,q=1表示最大偏好,q參數缺省默認爲q=1。 |
Accept-Charset |
設置可接受的字符集。例如: Accept-Charset: iso-8859-5, unicode-1-1;q=0.8 |
Accept-Encoding |
設置可接受的數據壓縮方式。例如: Accept-Encoding: compress, gzip, deflate |
Accept-Language |
設置可接受的語言。例如: Accept-Language: da,en-gb;q=0.8,en;q=0.7 |
服務響應中相關的HTTP頭包括:
HTTP響應頭(Content-*) |
描述 |
示例 |
Content-Type |
媒體類型(或稱media-type, MIME type),能夠有charset等參數。例如:若是是application/xml或者以+xml結尾,則能夠用XML解析器處理消息。JSON媒體類型application/json不指定charset參數,默認使用UTF-8[11]。 |
application/xml;charset=UFT-8 application/json |
Content-Length |
內容長度(bytes) |
|
Content-Language |
本地化語言。使用兩個字母的RFC5646語言標籤。「-」+ 兩個字母的國家代碼是可選的。 |
en-US |
Content-MD5 |
內容的MD5摘要,用於一致性檢查(注:TCP用戶使用傳輸級別的校驗和(checksum)進行一致性檢查)。接收方能夠用這個頭校驗數據的完整性,特別是在不穩定的網絡上發送或接受大量數據時。(因爲可能被篡改,不能用於安全控制目的) |
bbdc7bbb8ea589666e33ac922c0f83 |
Content-Encoding |
gzip, compress或deflate編碼。若是是gzip,接收方在解析消息前必須先對消息進行解壓。 客戶端可用Accept-Encoding來設置對Content-Encoding的偏好。避免在HTTP請求中使用這個頭,除非服務端支持。 |
gzip |
Last-Modified |
服務端最近一次修改資源或呈現的時間 |
Sun, 29 Mar 2009 04:51:38 GMT |
Accept-*一般指定的是一個範圍 (Range) ; Content-*一般返回的是一個特定的值。
例如,若是客戶:
Request Accept: application/atom+xml;q=1.0, application/xml;q=0.6, */*;q=0.0 Accept-Language: fr;q=1.0, en;q=0.5 Accept-Encoding: gzip
Response Content-Type: application/atom+xml; charset=UTF-8 Content-Language: fr Content-Encoding: gzip Vary: Accept-Encoding |
使用標準的HTTP頭來使用/產生不一樣的MIME類型,在將來能夠在不用改變服務接口的狀況下很容易地支持新的內容類型。
有2種方式來請求一個資源的呈現類型:
類型 |
描述 |
服務響應優先級 |
使用查詢參數 (代理驅動) |
可使用一個特殊的查詢參數forma:{JSON, XML, JSV}。例如:/invoice/11111?format=JSON |
高 |
HTTP請求頭 (服務驅動) |
Accept: text/xml Accept: application/json |
低 |
若是在請求中以上兩種方式同時指定,則按優先級最高的格式進行響應。
在服務響應中用Content-Type頭表示。Content-Type使用MIME類型[12][13]定義。
須要支持的類型有:
類型 |
請求方式 |
描述 |
XML |
application/xml[14] (資源擴展名.xml,參數名accept=xml) |
避免使用text/xml,因其默認字符集是us-ascii,而application/xml默認使用UTF-8. |
JSON |
application/json (資源路徑擴展名.json,參數名accept=json) |
|
自定義媒體類型使得用戶能夠定義公司專有的數據顯示格式。這時它就成爲客戶和服務之間的數據格式合約。自定義媒體媒體類型能夠和特定資源相關。
例如:
GET /customers/1234 HTTP/1.1
Host: newegg.com
Accept: application/vnd.newegg.customer+xml
若是Response支持多語言,則使用Accept-Language HTTP頭來指定語言選項。服務使用Content-Language頭顯示實際使用的語言。
若是業務數據支持多語言(例如item description),則可以使用languageCode查詢參數來指定業務數據語言選項。Response DTO中使用LanguageCode來表示業務數據的實際語言類型。
語言代碼能夠是兩個字母(ISO-639代碼)或者語言+’-‘+國別的形式。例如en, en-US。
客戶使用Accept-Charset頭或者URI參數charset來指定字符編碼選項。
在服務響應中,若是媒體類型是文本的而且支持charset參數,在Content-Type中包含charset屬性以顯示服務所使用的字符編碼方式。注意在XML中:若是使用Content-Type: application/xml; charset=UTF-8,需和XML內的encoding類型一致:<xml version=」1.0」 encoding=」」>。
主要使用UTF-8來支持Unicode類型。
HTTP壓縮又稱內容編碼 (Content Encoding[15]) 。主要選項有gzip, deflate, compress, identity[16]。新的內容編碼選項在IANA註冊。
客戶使用Accept-Encoding頭來指定內容編碼。服務使用Content-Encoding頭顯示實際使用的內容編碼。默認使用identity。
當存在多種資源呈現時,在響應中包含Vary頭能夠通知客戶和緩存基於服務端驅動的內容協商的條件和結果。它的值是以逗號分隔的服務端使用的請求頭列表。例如:
Request Accept-Language: en; q=1.0, */*; q=0.0
Response Content-Language: en Vary: Accept-Language |
當服務端沒有使用HTTP請求頭,而是使用客戶IP地址、時間、用戶個性化設置等信息時,Vary值爲 * .
1)客戶需明確關於內容協商的要求,除個別選項外,不該依賴服務端提供的默認值。
2)服務同時支持前文中2種關於資源呈現的請求方式(同時支持服務驅動和客戶驅動的內容協商)。若是客戶端同時指定了2種方式,優先順序依次是查詢參數和HTTP頭。
3)服務至少支持XML和JSON2種資源呈現類型來傳輸通常文本數據(二進制數據請使用對應的媒體格式)。若是客戶沒有指定媒體格式,默認返回XML。
4)暫不容許自定義媒體格式。
5)對於大數據量傳輸,推薦使用JSON。
6)使用Accept-Language頭來指定和業務數據無關的通常語言偏好;使用查詢參數languageCode來匹配實際業務數據中的語言類型。
7)若是服務不支持請求的語言類型,將使用客戶或BU(Business Unit)默認使用的語言類型。
8)不用添加Accept-Charset頭(或者相應查詢參數),除非客戶端只能處理一種特定的字符集。
9)若是沒有Accept-Charset頭(或者相應查詢參數),默認使用UTF-8編碼[17]。
10)若是服務不支持任何指定的字符編碼,且沒有*;q=0限制,則使用UTF-8編碼。
11)內部客戶老是使用Accept-Encoding: gzip (生產環境IIS的 gzip壓縮是打開的)。
12)使用Vary頭以正確地緩存數據。
13)若是服務不支持請求的媒體格式且客戶顯示地設置了*/*;q=0.0,應返回406 (Not Acceptable) 錯誤。
14)若是服務不支持請求的壓縮格式,直接返回非壓縮原始數據。
15)暫不容許自定義HTTP頭。
每一種請求均可以經過如下三種方式傳值:
n URI 參數
n Request Body
n HTTP Header
URI QueryString,主要用於GET和DELETE請求。
例如: GET /order?customerNumber=123456&orderStatus=open HTTP/1.1
把相應的請求數據以XML / JSON / NameValue形式經過Request DTO POST/PUT到服務器上。
Request Body主要用於POST/PUT請求。不推薦在GET請求中包含Request Body[18]。
任何HTTP協議容許的Request Headers均可以直接使用。但不該將放在實體主體裏的信息放進報頭。
暫不可使用自定義Request Headers。
當向服務端POST/PUT數據時, 能夠經過HTTP頭Content-Type來指定請求數據的格式。若是不能正確指定, 在服務器端會解析錯誤。
Content-Type通常有如下幾種:
n XML:application/xml
n JSON:application/json
n JSON:application/jsv
n URL Encoded:application/x-www-form-urlencoded
若是不指定Content-Type, 會默認請求數據編碼爲:application/x-www-form-urlencoded.
請求格式示例請參考: /* Key/Cross-Team Projects/* API/4. API接口描述
容許客戶同時建立、更新或刪除多個資源。
在一個Request Body裏包含多條記錄,一次提交。
響應格式示例請參考: /* Key/Cross-Team Projects/* API/4. API接口描述
<text xml: lang=」en-US」>Newegg Inc. </text>
<text xml: lang=」cn」>新蛋</text>
除非數據是面對最終用戶的,不要使用本地化的數據格式,而應使用可攜式數據格式:
<https://api.github.com/repos?page=3&per_page=100>; rel="next",
<https://api.github.com/repos?page=50&per_page=100>; rel="last"
RESTful服務有3種主要方法來封裝集合,分別是:容器對象、訂閱列表和分段返回。
打包資源集合最經常使用方法是將它們包裝(wrap)成一個更高級別的集合對象。
Feeds的兩個主要標準是Atom和RSS。RSS可以用一個列表來表示一個帶註釋的超鏈集合。Atom也是用一個列表來表示一個帶註釋的超鏈集合,但容許在每一個單項的」content」標籤中嵌入該項的實際內容。
使用Feeds來表示資源集合的優勢是RESTful服務的行爲相似於在web上提供新聞提要。
HTTP協議容許經過使用Multipurpose Internet Mail Extensions (MIME) multipart content types [20]來在單一返回消息體中傳遞多種資源。這使得能夠在單一的HTTP返回中傳遞資源集合。隨着」HTTP Put」和」Comet」技術的出現,這種方法逐漸流行。
Comet技術[21]在持久HTTP鏈接上使用持久運行的多段返回,使得服務器能夠向瀏覽器推送內容。使用這個技術的好處是: 運行時間較長或者結果集較大的查詢,能夠在查詢結束前向請求者推送數據。
爲了最大限度減小網絡流量,並從客戶的角度簡化一些API,可提供標題擴展功能。即在返回中默認隱藏某些集合的明細列表,只有在用戶須要時才提供對象的擴展顯示。
利用開關參數expand={expand object}來打開/關閉擴展。參考Atlassian REST API Design Guidelines version 1: Title Expansion for Entities
除了XML和JSON格式的數據,有時也須要在文本中嵌入二進制數據,例如顯示圖片或者電影片斷。
經過分段消息(Multipart messages)能夠將不一樣格式的數據合併成一個HTTP消息。使用下列媒體類型來處理二進制數據:[22]
例如:
Content-type: multipart/mixed; boundary=」abcd」
--abcd Content-type: application/xml; charset=UTF-8
<movie>…</movie> --abcd Content-type: video/mpeg ---image here--- ---abcd--- |
User-Agent頭信息讓你知道你在和移動設備打交道,於是返回內容的移動版本。
例如:iPhone User Agent
Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543a Safari/419.3
REST的原則之一是HATEOAS(Hypermedia as the Engine of Application State),即服務爲客戶端提供全部信息使得客戶端能夠和服務進行交互。
可使用<link>標籤[23]或者HTTP Link頭來關聯資源。
1) <link>標籤屬性
屬性 |
描述 |
href |
目標資源的URI[24] |
rel(」relation」的縮寫) |
Link關聯類型 (Link Relation Type) 。主要目的是標識相關連接的語義。大小寫敏感。能夠有多個值,例如rel=」alternate help」。 |
title |
可選。對被連接資源的簡短描述 |
type |
可選。該連接返回內容的媒體類型(media type) |
hreflang |
可選。該連接返回內容的語言 |
length |
可選。該連接返回內容的長度 |
2) 屬性rel的可能值
<review xmlns=」org:example:books」 xmlns:atom=」http://www.w3.org/2005/atom」> <atom:link rel=」http://example.org/rels/book」 href=」…」/> <atom:link rel=」http://example.org/rels/author」 href=」…」/> <atom:link rel=」http://example.org/rels/add-review」 href=」…」/> </review> |
3) 在JSON格式中使用Links
完整形式 {「links」: [ {「rel」: 」alternate」, 「href」: 」 http://example.org/rels/book」 }, {「rel」: 」owner」, 「href」: 」 http://example.org/rels/author」 } ]}
簡化形式 { 「alternate」: 」http://example.org/rels/book」 「owner」: 「http://example.org/rels/author」 } |
HTTP Link頭提供了一種獨立於資源呈現格式的link傳送方式,而且在協議層可見。
格式:Link: <{URI}>;rel=」{relation}」;type=」{media type}」;title=」{title}」 …
Link頭適用於:
1) 資源呈現使用二進制格式,例如images,rich-text documents, spreadsheets等
2) 資源呈現的格式使得links不容易被找到(例如純文本文件)
3) 但願在不解析內容的狀況下添加或者讀取links
Location:一般用於POST返回,將接收方重定向到Location所指向的URI。使用場景:
1) 當服務器建立新資源後,例如返回代碼201
2) 在出現3xx錯誤時,將客戶引向Location所指向的URI
Content-Location:一般用於GET返回,申明資源呈現的URI
URI模板容許服務端提供「半透明」的URIs給客戶端。客戶端填充缺失的內容以產生有效的URI。
例如:
XML格式 <link-template href=」 http:// neweggcentral-rest/order/{order-number}」 title=」…」 rel=」…」/> <link-template href=」 http:// neweggcentral-rest/order/search/customer-number={customer-number}」 title=」…」 rel=」…」/>
JSON格式 「link-templates」: [{ 「ref 」: 「…」, 「href」: 「http:// neweggcentral-rest/order/{order-number}」, 「title」: 「…」 }, { 「ref」: 「…」, 「href」: 「http:// neweggcentral-rest/order/search/customer-number={customer-number}」, 「title」: 「…」 }] |
1) 經過atom:link元素或者HTTP Link頭提供相關操做或者關聯資源
2) 每個資源都應使用HTTP Link頭提供一個連接指向自身:Link: <{href}>;rel=」self」;…
3) 擴展Relation Types統一管理。不能自行隨意添加擴展Relation Types
4) 目前提供的擴展Relation Types:/rel/add, /rel/void, /rel/delete
5) 全部Link Relation Types使用小寫
6) 擴展Relation Type的URI指向對該擴展Relation Type的描述,包括:
7) 對URI模板中的參數提供說明
8) 若是用戶指定了擴展名(例如.xml, .json),連接中儘量使用相同的擴展名
9) 客戶經過URI根能夠最終找到全部的資源入口
查詢是HTTP GET方法最廣泛的應用。針對極其複雜的查詢也可以使用POST方法。不過一般 POST 方法沒有緩存機制,所以不是查詢數據的首選。
GET是安全的(Safe)和冪等的(Idempotent),是獲取資源的默認HTTP方法。不能用GET查詢來修改數據。
GET經過URI的Query String傳遞查詢參數。查詢參數包括過濾條件、排序字段、返回字段列表、返回記錄數等。例如:
例如,按Invoice日期排序,從第80張invoice開始,返回20張invoices:
http://business.com/invoices?numInvoices=20&startIndex=80&sortOrder=invoiceDate
能夠經過查詢參數選擇須要返回的字段。參考Facebook graph API.
https://graph.facebook.com/bgolub?fields=id,name,picture
查詢參數的不一樣排列和組合會致使不一樣的URI。太多的URI會下降緩存的有效性。因此查詢參數的排列順序儘量保持不變。
服務要默認全部查詢參數都是能夠省略的,並在客戶省略查詢參數的狀況下提供參數的默認值或默認行爲(對於確實不能省略的查詢參數,返回明確的錯誤信息)。服務須要忽略多餘的參數。服務須要對返回的記錄總數進行限制(用戶已指定最大返回記錄數除外)。
查詢參數最主要用來返回資源集合,不該用來提供資源ID和任何操做指示。將資源ID嵌入查詢字符串的問題之一是系統再也不能根據URL base和資源路徑來判斷是否同一資源。查詢字符串不能有業務操做是由於它不該直接影響資源或者服務器的狀態。例如:
❶ GET /order/123456 正確
❷ GET /order?orderNumber=123456 錯誤
若是order 123456不存在,URL❶會返回404錯誤,URL❷會返回空集合。
若是查詢條件太長使得URI超出瀏覽器或服務器對URI長度的限制,可使用POST方法來執行查詢。這時在請求Body中含有使用application/x-www-form-urlencoded編碼的查詢條件。例如:
Request POST /order HTTP/1.1 Host: neweggcentral-rest Content-Type: application/x-www-form-urlencoded
zipcode=91765 & status=’V’
Response HTTP/1.1 200 OK Content-Type: application/xml; charset=UTF-8 … |
使用POST方法來獲取資源的壞處:
1) 削弱了REST的「統一接口」原則
2) 返回結果不能緩存
3) 因不能緩存,翻頁操做會反覆從新執行查詢
能夠爲每一次不一樣的查詢建立一個新資源。這樣經過資源能夠訪問和執行歷史查詢。
經過POST方法執行的查詢,能夠首先建立一個關於該查詢的新資源,而後利用GET 方法去訪問該查詢資源,這樣POST查詢就轉成了GET查詢。這樣查詢的結果是可緩存的。任何一樣的 查詢均可經過重定向到該資源的URI得以執行。並且,該查詢還能夠接收新的條件。
資源建立成功後,返回代碼201(Created)以及一個指向新資源的Location頭。例如:
Request #1 POST /order HTTP/1.1 Host: neweggcentral-rest Content-Type: application/x-www-form-urlencoded
zipcode=91765 & status=’V’
Response HTTP/1.1 201 Created Content-Type: application/xml; charset=UTF-8 Location: http:// neweggcentral-rest/order/query/1
Request #2 GET /order/query/1?start=10 HTTP/1.1 Host: neweggcentral-rest |
預約義廣泛使用的查詢,有助於服務端優化設計和提升響應速度。例如:返回order查詢的summary視圖:
/order? OrderDateFrom=2012-05-21 & view=summary
爲了保持各子系通通一接口和風格,有必要約定參數命名。
1) HTTP頭相關
小寫的HTTP頭名稱。例如:accept;
2) 業務對象相關
通常和字段命名相同,儘可能和業界規範保持一致,各子系統務必統一。例如:customerNumber, orderNumber, vendorNumber,
3) 日期、時間相關
例如:訂單起始/截止日期:orderDateFrom,orderDateTo
4) 控制有關
1) 使用查詢參數來指定過濾條件、排序字段或返回字段列表等
2) 查詢參數的排列順序儘量保持不變
3) 若是客戶沒有設置查詢參數,服務返回默認視圖
4) 爲廣泛使用的查詢預約義命名查詢
5) 服務要假設全部查詢參數都是能夠省略的,並在客戶省略查詢參數的狀況下提供默認視圖。
6) 服務須要忽略多餘的參數。。
7) 提供經過HTTP Link頭提供翻頁機制
8) 對於隱藏內容,明確申明,不要讓用戶覺得是服務端配置問題
9) 若是字段較多,考慮支持fields參數
10) 考慮支持標題擴展功能以支持返回子對象更詳細的信息
11) 文檔化每個查詢參數
HTTP過時緩存機制(Expiration Caching)能夠有效減小對服務端的請求數量。服務只應該緩存成功的GET請求。
過時緩存機制基於Cache-Control(HTTP 1.1)和Expires(HTTP 1.0)頭。這些頭信息告訴客戶和緩存在一段特定的時間內保留服務端響應結果的一份拷貝。服務應在響應中添加這兩個HTTP頭,使得客戶端能夠決定是否能夠緩存結果。例如:
First Request GET /order/12345678 HTTP/1.1
First Response HTTP/1.1 200 OK Date: Sun, 09 Aug 2009 00:44:14 GMT Last-Modified: Sun, 09 Aug 2009 00:40:14 GMT Expires: Sun, 09 Aug 2009 01:44:14 GMT Cache-Control: max-age=3600,must-revalidate
Second Request (10分鐘後) GET /order/12345678 HTTP/1.1
Second Response HTTP/1.1 200 OK Date: Sun, 09 Aug 2009 00:54:14 GMT Last-Modified: Sun, 09 Aug 2009 00:40:14 GMT Expires: Sun, 09 Aug 2009 01:44:14 GMT Cache-Control: max-age=3600,must-revalidate Age: 600[28] |
Cache-Control的主要參數有:
屬性 |
描述 |
public |
默認值。即便請求是須要認證的,也可用之來使用共享緩存(Shared Cache) |
private |
客戶端緩存(例如瀏覽器緩存或者正向代理(Forward Proxy))能夠緩存結果,但共享緩存(例如服務端或者網絡緩存)不能緩存結果。用於當響應只針對認證客戶時。 |
no-cache & no-store |
禁止緩存。同時包含Pragma: no-cache頭能夠支持HTTP 1.0 |
max-age |
有效期(freshness lifetime),以秒爲單位 |
must-revalidate |
告訴緩存返回過時結果前必須檢查服務端數據(條件請求) |
proxy-revalidate |
和must-revalidate相似,但只用於共享緩存 |
此外,對可緩存的服務端響應,服務應該設置HTTP Vary頭。例如:Vary: Content-Type,表示基於URL+返回的content-type服務響應是可緩存的。
條件請求用於GET請求時,能夠驗證緩存是否過時;用於POST、PUT和DELETE時,能夠提供併發控制。
服務端用Last-Modified和ETag(Entity Tag)響應頭來驅動條件請求。
客戶端:
服務端必須處理客戶端提供的If-Match 和 If-None-Match頭。客戶端在使用If-Match 和If-None-Match頭請求資源時需提供ETag值。ETag是由針對同一資源的前一次請求的結果提供的。服務端比較結果返回給用戶最新內容,或者用 HTTP 響應碼 304 告知用戶,內容沒有變化。
Last-Modified的刻度較粗(以秒爲單位),所以屬於「弱驗證」。ETag屬於「強驗證」,由於每一個不一樣的資源呈現都會不一樣。沒必要要同時使用Last-Modified和ETag。
如何生成Last-Modified和ETag標籤?確保每次產生的ETag值都不重複。
1) 利用數據庫表結構的時間戳或者序列號字段
2) 使用資源數據產生ETag值。將該值和時間戳保存在一個單獨的表或數據源中
3) 若是資源呈現的數據量不大,能夠將其進行MD5哈希後獲得ETag值
併發控制有兩種方式:
1) 「悲觀」併發控制:客戶首先加鎖資源,而後修改,而後解鎖。期間服務會阻止其它客戶得到資源鎖。關係數據庫採用這種方式。
2) 「樂觀」 併發控制:客戶首先獲得一個token,並嘗試包含有token的寫請求。若是token仍然有效,寫入會成功。
HTTP採用的是樂觀併發控制:
1) 連同每一個資源呈現,服務提供給客戶一個token
2) 當資源狀態發生改變時,token的服務端版本發生改變
3) 客戶在請求修改或刪除資源時,提供token給服務端,是爲「條件請求」
4) 服務驗證token是否仍然有效。若是無效,併發操做失敗,服務中斷請求
服務端:
若是資源不存在,返回404(Not Found)(或者在許可的狀況下建立新資源)。
若是客戶沒有提供If-Unmodified-Since和If –Match頭,返回403(Forbidden)。
若是客戶提供的If-Unmodified-Since或If –Match值和服務端的實際修改日期和ETag值不符,返回412(Precondition Failed)。
若是條件知足,更新(或刪除)資源,返回200(OK)或者204(No Content)。
客戶端:
若是收到412錯誤,利用無條件GET請求獲得最新的Last-Modified和ETag值,而後從新提交請求。
條件POST請求用來發現和阻止重複提交。
若是用POST建立新資源,會返回201或者303+新的URI。在這種狀況下,客戶端本地沒有該資源呈現的拷貝和與條件請求相關的HTTP頭信息。
方法是經過爲每一個POST請求提供一個一次性的URI。URI裏包含有一個服務端產生的token,只能用於一次POST請求。將全部使用過的token保存在服務端的事務日誌裏。例如:
Request GET /vendor/address/address-token HTTP/1.1
Response HTTP/1.1 204 No Content Cache-Control: no-cache Link: <http://neweggcentral-rest/address;t=360eerfg8gg34j4d6ff8>;rel=」http://neweggcentral-rest/rel/resource」 |
若是客戶提交的POST請求裏含有已經使用過的token,返回403(Forbidden)。不然處理請求,返回201(Created)或者303(See Other),並標記該token爲可以使用。
能夠經過MD5當前時間+隨機數+ETag等來產生用於一次性URI的token。能夠在URI中包含數字簽名來提升其安全性。
1) 肯定每種資源表示的緩存策略
2) 在使用no-cache或者no-store以前,考慮使用教小數值的max-age替代
3) 各子系統須要決定各資源的PUT操做在資源不存在時是否能建立新資源
4) 針對不一樣狀況,肯定是否使用條件請求和併發控制
在面向service的過程
1) 收到請求後,立刻返回ACK,包括:
例如:
<receipt xmlns="http://www.xml.org/2004/rest/receipt" requestUri = "http://www.example.com/xya343343" received = "2004-10-03T12:34:33+10:00"> |
2) 校驗請求
3) 儘快處理請求
Status資源能夠看着是transaction資源的不一樣視圖:
Transaction URI: http://www.example.com/xyz2343
Transaction Status URI: http://www.example.com/xyz2343?view=status
狀態 |
描述 |
Received |
<status state="received" timestamp="2004-10-03T12:34:33+10:00" /> |
Processing |
|
Processed |
<status state="processed" timestamp="2004-10-03T12:34:33+10:00" > |
Failed |
<status state="failed" timestamp="2002-10-03T12:34:33+10:00" > <error code="3" > <message>A bad request. </message> <exception>line 3234</exception> </error> </status> |
服務應堅持使用HTTP狀態代碼來標識成功/失敗。在很是特殊的狀況下可能須要添加自定義HTTP狀態代碼(只能做爲例外處理,而且保持在最低水平)。任何有關該錯誤的上下文信息應包含在HTTP響應的Body中。
Code |
Name |
Description |
Notes |
400 |
Bad Request |
請求沒有包含Host頭 |
|
401 |
Unauthorized |
客戶無受權。若是客戶通過認證後依然不容許訪問,則返回403(Forbidden) |
返回該錯誤時,同時返回一個包含有認證方法的WWW-Authenticate頭。廣泛使用的認證方法有Basic和Digest。 |
403 |
Forbidden |
禁止客戶訪問該資源 |
|
404 |
Not found |
資源未找到 |
|
405 |
Not Allowed |
HTTP方法不容許 |
返回一個包含有效方法的Allow頭 |
406 |
Not Acceptable |
請求返回的媒體格式不支持 |
|
409 |
Conflict |
請求和現有資源狀態衝突 |
|
410 |
Gone |
資源已經不存在 |
|
412 |
Precondition Failed |
提供的If-Unmodified-Since或者If-Match和實際修改時間或ETag值不一樣 |
|
413 |
Request Entity Too Large |
POST或PUT的Body太大 |
在Body中指明容許的值,或者變通方法 |
422 |
Unprocessable |
請求數據格式正確但語義錯誤 |
|
415 |
Unsupported Media Type |
不認識請求消息格式 |
|
500 |
Internal Server Error |
因爲服務實現代碼的bug形成的失敗 |
|
509 |
Service Unavailable |
服務不能在某些時間段或者肯定的時間內完成請求。兩種廣泛的狀況是server出現故障或者客戶請求超過限額 |
可能的話返回一個Retry-After頭 |
GET https://api.newegg.com/customer
HTTP/1.1 401 Unauthorized
Link: <http://neweggcentral-rest/errors/20003>;rel=http://neweggcentral-rest/rel/error
Link:<http://neweggcentral-rest/doc/errors/20003>;rel=http://neweggcentral-rest/rel/error_doc
<ResponseBody>
<Errors>
<Error>
<Code>20003</Code>
<Message>Authenticate</Message>
<MoreInfo>{More detail info about the error}</MoreInfo>
</Error>
</Errors>
</ResponseBody>
涉及用戶輸入數據的服務請求(POST,PUT),應在響應消息中額外包含用戶錯誤信息列表,以清楚地展現糾正錯誤須要採起的行動。能夠添加一個新的自定義HTTP狀態代碼以清楚地向客戶端傳達這裏是一個用戶數據校驗錯誤。
用戶輸入錯誤對象有資源(Resource)和字段(Field)屬性使得用戶可以定位錯誤所在。也有一個錯誤代碼(Error Code)使用戶可以知道錯誤緣由。可能的校驗錯誤類型:
missing |
資源不存在 |
missing_field |
資源的必填字段缺值 |
invalid |
字段格式無效 |
already_exists |
字段值重複(別的資源已經有一樣的值) |
兩種主要的版本定義方式:
例如:
GET /account/1 HTTP/1.1
#1: Accept: application/vnd.steveklabnik-v2+json
#2: Accept: application/xml; version=1.0
例如:
http://api.newegg.com / order_mgmt /order/v1/order
http://api.newegg.com /order_mgmt/claim/v1/claim
不能假設服務的每次響應格式可以保持一致(例如某些元素可能丟失或出如今多個位置)。須要經過表達式(expressions)從Web服務的返回中動態抓取值。
解析模式取決於返回的是XML仍是JSON格式。例如用XPath處理XML,用JavaScript處理JSON。
1) 在比較URL時大小寫是敏感的[29]
2) 使用小寫字母
3) 不要使用下劃線,用-代替(? Dropbox, Twitter, Google maps 和 Facebook都使用下劃線)
4) 使用最簡單的名詞形式
5) 自解釋:避免含義模糊的縮寫
6) 一致性:一樣的單詞表示一樣的意思
7) 儘可能整齊勻稱
1) 方法列表
2) 對每一個服務方法,應該包括:
完整的HTTP返回代碼列表請參考 section 6 of RFC 2616.
HTTP 響應代碼 |
代碼含義 |
200 |
已建立,請求成功且服務器已建立了新的資源。 |
201 |
是否只顯示處於警告狀態的應用實例 |
301 |
重定向 , 請求的網頁已被永久移動到新位置。服務器返回此響應時,會自動將請求者轉到新位置。 |
302 |
重定向 , 請求的網頁臨時移動到新位置,但求者應繼續使用原有位置來進行之後的請求。302 會自動將請求者轉到不一樣的臨時位置。 |
304 |
未修改,自從上次請求後,請求的網頁未被修改過。服務器返回此響應時,不會返回網頁內容。 |
[1] 在本文中URI和URL等同使用,並不做特別區分。
[3] Sitemaps protocol 對URL長度的限制爲2048字符。Sitemap 可方便管理員通知搜索引擎他們網站上有哪些可供抓取的網頁。Google網站管理工具也使用了該協議。所以,若是要使用該特性進行SEO,URL長度不能超過2048字符。
[5] 例如:<form method=」get」 action=http://www.google.com/search enctype=」application/x-www-form-urlencoded」>…</form>
[6] Location 頭告知用戶新資源在哪裏。若是僅僅告訴用戶新的ID,用戶還得本身去組裝URI。BODY中不須要新資源的URL。
[7] RFC 2616: http://tools.ietf.org/html/rfc2616
[8] 代理Agent指User Agents。最普編的User Agents是瀏覽器。
[10] q參數在Accept頭上使用的最廣泛,但可應用於全部的Accept-*頭。另,並非全部的服務器都支持q參數。
[11] RFC 4627描述瞭如何肯定JSON格式數據的字符編碼。
[13] 媒體類型的完整列表參見http://www.iana.org/assignments/media-types/
[14]不要使用text/xml,因其默認編碼方式是US-ASCII
[16] Content-Encoding的說明中指出deflate指的是在RFC1950說明的zlib格式。也就是說當Content-Encoding爲deflate時,內容應該爲zlib格式。Deflate (RFC1951):一種壓縮算法,使用LZ77和哈弗曼進行編碼;zlib (RFC1950):一種格式,是對deflate進行了簡單的封裝;gzip (RFC1952):一種格式,也是對deflate進行的封裝。gzip = gzip頭(10字節) + deflate編碼的實際內容 + gzip尾(8字節)。
[17] UTF是Unicode Transformation Format的縮寫。UTF-8是Unicode的實現方式之一,也是application/xml和application/json的默認編碼方式。UTF-8最大的好處是和ASCII兼容。參考http://www.ietf.org/rfc/rfc3629.txt
[20] 在RFC2046 5.1節定義 http://tools.ietf.org/html/rfc2046#section-5.1
[22] 避免使用Base64編碼文本格式中的二進制數據。
[24] 對於XML,可經過在link元素或者父節點上使用xml: base屬性來使用相對URI。但XML解析庫並不自動使用xml: base來解析相對路徑。
[25] 完整列表參見Web Linking draft-nottingham-http-link-header-10 Section 6.2.2: Initial Registry Contents
[27] 「緩存」一般用於指對象緩存(Object Cache)(例如memcached)或者HTTP緩存(例如Squid或者Traffic Server)。它們最大的不一樣是HTTP緩存不須要調用任何編程API來存儲、訪問或刪除緩存中的對象。
[28] 由緩存添加,顯示該拷貝存在於緩存的時間