
從商業角度探討 API 設計
GET
HTTP request method.
As with every good action it should return a response
+ Response 200 (text/plain)
Hello World!
## PUT
OK, let's add an update action and send a response back confirming the posting was a success
+ Request (text/plain)
All your base are belong to us.
+ Response 204
RAML 、 Swagger 以及其它一些類似API描述規范的工作方式也是大同小異。
在采用 API 優先方式時,你需要通過工具將設計時創建的元語言轉換為可以在運行時起作用的東西。舉例來說,Swagger 的 codegen 工具能夠解析描述文檔,并生成相應的客戶端代碼。而 RAML-for-JAX-RS 項目則在 RAML 描述與通過 JAX-RS 進行注解的 Java 代碼之間提供雙向轉換的功能。
支持代碼優先方式的 API描述規范 為數極少,這種方式是通過源代碼生成服務描述。不過,這一領域中最知名的格式 —— Web Service 描述語言(WSDL)在企業級應用社區中仍然相當流行,并且支持 WSDL 的工具為數眾多,像微軟的 Visual Studio 與 Eclipse 等常見的編輯平臺都提供了對它的支持。
下面是通過 WSDL 對某個簡單的 Web API 進行描述的一個示例。
HelloService WSDL**** 示例
<definitions name="HelloService"
targetNamespace="http://www.examples.com/wsdl/HelloService.wsdl"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://www.examples.com/wsdl/HelloService.wsdl"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<message name="SayHelloRequest">
<part name="firstName" type="xsd:string"/>
</message>
<message name="SayHelloResponse">
<part name="greeting" type="xsd:string"/>
</message>
<portType name="Hello_PortType">
<operation name="sayHello">
<input message="tns:SayHelloRequest"/>
<output message="tns:SayHelloResponse"/>
</operation>
</portType>
<binding name="Hello_Binding" type="tns:Hello_PortType">
<soap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="sayHello">
<soap:operation soapAction="sayHello"/>
<input>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:examples:helloservice"
use="encoded"/>
</input>
<output>
<soap:body
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="urn:examples:helloservice"
use="encoded"/>
</output>
</operation>
</binding>
<service name="Hello_Service">
<documentation>WSDL File for HelloService</documentation>
<port binding="tns:Hello_Binding" name="Hello_Port">
<soap:address
location="http://www.examples.com/SayHello/" />
</port>
</service>
</definitions>
如果選擇了代碼優先方式,那么你就需要通過某些工具,將你的 _ 源代碼 _ 轉換為可重用的 API 描述元數據。Eclipse 與 Visual Studio 都可以一鍵實現通過代碼生成 WSDL 文件。另外還有一些工具能夠 _ 讀取 _WSDL 文件,并生成各種實現元素。例如 SmartBear 的 SoapUI 工具就能夠基于 WSDL 文件生成代碼、創建人類可讀的文檔,甚至是進行構建與運行測試集等任務。
大多數 API描述規范 同樣支持生成人類可讀的文檔,包括 RAML、Apiary 和 Swagger。實際上,開源的 Swagger-UI 工具就是以文檔生成器而聞名的(見下圖),甚至讓某些人產生了一種誤解,認為 Swagger 僅僅是一種用于生成人類可讀的 API文檔的工具。
有一些描述格式在設計時就專注于生成人類可讀的文檔,Mashery 的 I/O Docs 就是這方面的一個絕佳例子(見下圖),它同時還提供測試方面的支持。
Mashery的 I/O Docs示例
{
"name": "Lower Case API",
"description": "An example api.",
"protocol": "rest",
"basePath": "http://api.lowercase.sample.com",
"publicPath": "/v1",
"auth": {
"key": {
"param": "key"
}
},
"headers": {
"Accept": "application/json",
"Foo": "bar"
},
"resources": {
"Resource Group A": {
"methods": {
"MethodA1": {
"name": "Method A1",
"path": "/a1/grab",
"httpMethod": "GET",
"description": "Grabs information from the A1
data set.",
"parameters": {
"param1": {
"type": "string",
"required": true,
"default": "",
"description": "Description of the first
parameter."
}
}
},
"MethodA1User": {
"name": "Method A1 User",
"path": "/a1/grab/{userId}",
"httpMethod": "GET",
"description": "Grabs information from the A1
data set for a specific user",
"parameters": {
"param1": {
"type": "string",
"required": true,
"default": "",
"description": "Description of the first
parameter."
},
"userId": {
"type": "string",
"required": true,
"default": "",
"description": "The userId parameter that is in the URI."
}
}
}
}
}
}
}
不過,無論你的專注點是通過元語言生成代碼,或是由代碼生成文檔(或者其它任何一種方式),API描述規范 只是整個創建與部署 Web API 流程中的一個環節的約定而已。這一流程中還包括另一個重要環節,就是了解有哪些“現有”的 API 與服務,以及怎樣使用它們。為了實現這一環節,你需要去 發現這些 API。
API發現是指定位某個能夠完成特定任務所必須的 Web API 的能力。舉例來說,你可能需要某個 Web API,以實現 在線搜索、在線支付、管理用戶帳號或處理 在線客服 平臺上的請求的功能。在實際應用中,你應當能夠以最小的代價發起一次搜索、找到符合你需求的 API、獲得訪問該 API 的能力、實現對接代碼并且開始使用該 API。發現公共的開放API的最有效方法是通過 API平臺,發現私有API的最有效方法是企業API管理平臺。
可惜,現實情況總是有所差異的。
在討論 API 發現時,我們通常所指的是 應用程序編程接口 (理解API是什么?)與一個實際 “運行中”的服務這兩者之一。對于前一種情況來說,所討論的內容只是一個接口,我們將用這個接口設計、實現以及部署屬于自己的服務。而對后一種情況來說,我們所指的是某個現有的服務實例本身,你可以遠程連接到這個服務中,并立即開始使用。由于這兩種情況所面臨的困難以及所產生的好處各不相同,因此應當通過示例進行一些考查。
如果你所尋找的只是一種已經發布的 API 規范,并且能夠自己完成它的實現,那么 API Commons 是一個很好的資源。API Commons 的目的在于“為 API 規范、接口以及數據模型提供一種簡單而透明的機制,讓用戶能夠以協作的方式共享無版權歸屬的設計”。打個比方,如果你已經設計了一種 API,并且樂于將它的設計分享給他人,讓他們利用你設計的模型實現自己的服務,你就可以將自己的模型發布在 API Commons 上,并鼓勵其他人使用。
另一方面,如果你打算實現某個 API,而又想知道是否已經有人處理了相同的問題,那么你可以 API Commons 中進行一番搜索,看看有沒有什么現成的設計可以使用。這種發布 / 訂閱模式能夠促使相似的服務重用相同的接口,而無需與參與者之間討論協作的細節。在最理想的情況下,如果某個 API 調用者需要使用 API Commons 上所注冊的某個 API 的設計,那么他也應當能夠使用實現了相同設計的任何一種服務。
API Commons 的運維模式得到了 Creative Commons 的啟發,目前由 Kin Lane 與 Steve Willmott 負責維護。
在多數情況下,當人們談及 API發現時,他們所指的其實是發現某個能夠利用的 _ 運行實例 _,即某個可用的服務。目前已經出現了一定數量的服務,它們將追蹤實際的可用服務,并且大多數這種服務都是設計為人類可讀的搜索引擎。其中最著名的一個例子就是 冪簡集成 的 API Hub(見下圖)
當你的需求滿足下表中的任意一條時,就可以考慮這種解決方案:
你所選擇的服務有可能會支持一種或多種我們已在上文中涵蓋的 API 描述語言,這將簡化你創建對應的 API 調用代碼的工作。
這種發現服務有一個潛在的缺點,那就是并非所有的服務目錄都經過了認真地審查(某些目錄要依賴于“手動注冊過程”)。你在這一過程中可能會找到一些有問題的 API,它們或許不支持你所需的協議或格式,并且不再更新、或者無法滿足你在性能與許可方面的需求,等等。此外,如果你打算使用多個公共的開放API,那么整個搜索、評估、接入以及接口生成循環可能會顯得相當冗長。
除此之外,還存在著另一種類型的 API 發現方式,即 API 聚合器。這些聚合器就如同一個或多個現有 web service 的某種代理,并提供一個單一的、統一的 API,方便你進行編碼。這方面的一個例子是 Intel 的 Mashery API Network 。
如果你計劃調用多個第三方的 Web API,那么聚合器能夠讓你在接入以及 API 整合方面所需付出的精力大大減少。聚合服務能夠調用后端的 API 并進行正規化、管理你的訪問密鑰、某些服務甚至能夠為你提供一個自定義的 API,通過它簡化在每個第三方 API 中共享關聯數據的任務。
人們還可以以另一種方式來看待這些 API 發現服務,即將其視為一種可配置的本地服務發現引擎。這種發現引擎只存在于某個公司的邊界范圍之內,并處理尋找以及連接到一或多個運行中的服務實例的工作,例如某個數據存儲或業務組件。最近幾年來,這種方式正在不斷地發展中,這方面的例子包括 Apache Zookeeper 、HashiCorp 的 Consul 、以及 CoreOS 的 etcd 等等。
這種發現方式的一大優勢在于,當你準備連接到組織內部的運行服務時,它提供了一個額外的間接層。你可以在運行代碼中移除實際的服務地址與連接參數,并將這部分信息保存在配置文件中。某些服務甚至還允許你對于延遲與響應性進行一些限制,讓它自動忽略或跳過運行時間較長的、或是不可用的服務實例,并連接至下一個可用的、健康的實例。
當然,這種抽象也存在著負面的因素。首先,由此產生的復雜性只有在大型服務中才值得一試。其次,對于這種服務的配置模型還沒有實現標準化,這意味著你會最終對于某個提供商產生強依賴關系。最后,這種運行時的發現服務目前還無法用于組織之外的、公開的第三方服務。
與 API 的單一資源集合相對的是 APIs.io 的 分布式搜索概念,APIs.io 是由 3Scale 與來自 API Evangelist 的 Kin Lane 共同合作的成果。它是一種典型的搜索引擎,它本身并不負責 托管API 文檔,而是跟蹤互聯網上找到的所有發現文件,并為這些文件中的數據提供一個搜索界面。
這些發現文件都是以一種名為 APIs.json 的格式所生成的。與 API 描述文檔不同,APIs.json 文件并不包含可用的 URL、表述形式以及響應代碼的詳細信息,而是包含了指向這些描述文檔位置的 _ 指針 _,以及指向服務條款、許可、聯系方式以及其它相關數據的指針。
這一格式出現的時間很短,但它或許能夠提供一種“粘合劑”,將 API 的描述與其它數據連接至一個單一的位置。由于這一格式也是機器可讀的,因此它或許能夠幫助搜索功能、甚至是 Web API 的上線細節實現自動化。
API 描述與 API 發現的共同目標是簡化構建與定位 Web API 的工作,但它們將大量的精力都專注于低層面的實現細節中,例如如何描述協議方法、返回代碼以及響應的格式。對于編寫實際代碼來說,它們固然非常重要,但有時仍不足以實現 _ 設計 _ 優秀 API 的任務。由于它們的多數設計方式都專注于實現細節,因此往往只能描述某個運行中的服務的 _ 單一實例 _(或鏡像集群)的特性。
如果你希望專注于更高層面的設計(例如用例、輸入與輸出),而不是充斥著協議、表述與資源的細節,那么你還需要別的工具。
在 API 元服務的開發中的一個最新趨勢是創建與共享 API文檔信息的想法。與描述文檔不同,文檔提供了一個更高層次的視圖,它描述了 API 所支持的功能,在有些情況下還能夠描述客戶端與服務器如何以一種機器可讀的方式暴露它們的特性。
Web 文檔的出現已經有很長一段時間了,在 1999 年出現的 HTML 4.01 規范中就引入了文檔這一屬性。元數據文檔可以定義為 a) 一個全局唯一的名稱 (URI),或 b) 指向一個實際文檔的鏈接(URL)。它的設計理念是讓文檔的作者得以為響應中的內容提供額外的描述性信息(例如文檔的實用索引屬性、服務條款等等)。
在2003 年, Tantek ?elik 定義了 XHTML 元數據文檔(XMDP)。XMDP 支持定義同時實現人類可讀與機器可讀的文檔檔案(是不是聽上去有點熟悉?),這種文檔實際上看起來與如今的 API 描述格式非常相像(參考以下示例)。
XMDP**** 的一個示例
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head><title>sample HTML profile</title></head>
<body>
<dl class="profile">
<dt id='author'>author</dt>
<dd>A person who wrote (at least part of) the document.</dd>
<dt id='keywords'>keywords</dt>
<dd>A comma and/or space separated list of the
keywords or keyphrases of the document.</dd>
<dt id='copyright'>copyright</dt>
<dd>The name (or names) of the copyright holder(s)
for this document, and/or a complete statement of copyright.</dd>
<dt id='date'>date</dt>
<dd>The last updated date of the document, in ISO8601 date format.</dd>
<dt id='identifier'>identifier</dt>
<dd>The normative URI for the document.</dd>
<dt id='rel'>rel</dt>
<dd>
<dl>
<dt id='script'>script</dt>
<dd>A reference to a client-side script. When used with the
LINK element, the script is evaluated as the document loads and
may modify the contents of the document dynamically.</dd>
</dl>
</dd>
</dl>
</body>
</html>
遺憾的是,文檔屬性沒有得到廣泛的應用,并且在 HTML 5 發布時從規范中被刪除了,只能通過其它方式獨立地定義檔案屬性。
在文檔屬性從HTML 規范中移除之后, Erik Wilde 提出了 RFC6906 規范,定義了 Link Relation Value 這種文檔,并注冊在互聯網號碼分配局(IANA)中。Wilde 的想法是將URI 風格的文檔進行標準化,通過一種不透明的標識符,“允許資源表述指定它們是否遵循了一種或多種文檔”。
由于我們能夠將響應的細節以文檔的形式進行描述,因此人們開始不僅將文檔應用于人類可讀的文檔,并且也應用在API 的響應中。在最近幾年中也出現了一些URL 風格的文檔實現。在本文中,我將介紹其中的兩種:DCAP 與ALPS。
在2009 年,都柏林核心元數據啟動計劃(DCMI)發布了由他們設計的 DCAP 格式,用于描述文檔元數據。DCAP 專注于支持資源描述框架(RDF)文檔,它“定義了元數據記錄,以此滿足特定的應用程序需求。同時在全局定義詞匯表與模型的基礎上,提供了與其它應用程序在語義上互操作的能力。”
以下是 DCAP 文檔的一個示例:
DCAP**** 文檔示例
Description template: Person id=person
minimum = 0; maximum = unlimited
Statement template: givenName
Property: http://xmlns.com/foaf/0.1/givenname
minimum = 0; maximum = 1
Type of Value = "literal"
Statement template: familyName
Property: http://xmlns.com/foaf/0.1/family_name
minimum = 0; maximum = 1
Type of Value = "literal"
Statement template: email
Property: http://xmlns.com/foaf/0.1/mbox
minimum = 0; maximum = unlimited
Type of Value = "non-literal"
value URI = mandatory
創建 DCAP 的原因是因為如今的表述格式(RDF)非常受限,并且過于泛用(例如元組),在這種情況下,DCAP 能夠改進共享數據語義的能力。DCAP 的一個關鍵優勢在于,它并不是直接指出在響應中使用了哪些術語(例如 givenName、familyName、customer、user 等等),而是指出這些術語是 _ 如何 _ 進行通信的。這種方式為創建可共享的在線詞匯表鋪平了道路。
目前已經出現了一部分與 DCAP 相關的工具,包括在線編輯器、驗證功能以及 HTML 的生成器。但 DCAP 的使用主要限制于圖書館、信息科學與學術社區等領域,很少看到將 DCAP 用于業務相關的 Web API 中。
在 2013 年,Leonard Richardson、Mark Foster 與我共同發布了 ALPS 互聯網草案的第一個版本。與 DCAP 類似,ALPS 同樣借用了 XDMP 的思想,它為數據元素(例如 userName、userStatus 等等)與用例元素(例如 find-user 等等)都提供了元數據。
以下是通過一個 ALPS 文檔描述一個簡單的搜索 API 的示例:
由 ALPS 文檔所描述的搜索 API
{
"alps" : {
"version" : "1.0",
"doc" : {
"href" : "http://example.org/samples/full/doc.html"
},
"descriptor" : [
{
"id" : "find-user",
"type" : "safe",
"doc" : {"value" :
"User search form"
},
"descriptor" : [
{
"id" : "userName",
"type" : "descriptor",
"doc" : { "value" : "input for search" }
},
{ "href" : "#userStatus" }
]
},
{
"id" : "userStatus",
"type" : "descriptor",
"description" : {"value" : "results filter"},
"ext" : [
{
"href" : "http://alps.io/ext/range",
"value" : "active,inactive"
}
]
}
]
}
}
ALPS 文檔專注于接口層面的互動,按 Eric Evans 的話來說也就是邊界上下文。ALPS 并不會處理一些實現方面的細節,例如協議(HTTP、XMPP 等等)、格式(HTML、JSON 等等)甚至是資源的URL。由于它無需關注實現細節,因此ALPS 文檔能夠作為API 設計工具的原始資料,以生成人類可讀的文檔,甚至可作為發現過程中的一個環節,幫助你選擇一個合用的API。
目前,對于ALPS 最佳的描述是將其視為一個“不穩定的”規范,并且在網上關于如何使用它的示例也非常罕見。 Ronnie Mitra 設計了一個實驗性質的 Rapido API 設計器(見下圖)能夠使用 ALPS 文檔作為輸入,而 Pivotal 的 Spring-Data 工具能夠將 ALPS 文檔的生成作為 API 構建過程中的一環。
雖然在 Web API 中使用文檔的做法重新吸引了人們的注意,但這種技術到底是一種會逐漸消失的實驗?還是會由此開創一個新的趨勢,以專注于獨立地定義 Web API 的數據與行為語義?現在下判斷還為時過早。
在本文中,你對于 Web API 元數據的三個關鍵領域,即描述、發現及文檔有了一個初步的了解。Swagger、RAML 以及 Apiary 等技術目前掌握著主動權,它們控制了 API 描述這一領域,而在這個發展良好的生態系統中還存在著一些其它技術。有一些工具能夠通過描述格式實現代碼與文檔生成的自動化,并且圍繞著幾個關鍵的格式,出現了許多功能強大的工具集。
API 發現這一領域依然由以人類驅動的搜索與選擇方式作為主導,而像 Intel 的 Mashery 這種關鍵的 API 聚合器依然是通過提供一種聚合的方式訂閱遠程的 API。而自動化的、基于配置的定位服務也正在逐漸興起,以支持連接到企業級服務實例的功能。其中有部分方式或許將提供對基于互聯網的 API 進行自動化服務發現的功能。
最后是 API 文檔,它常用于圖書館與信息科學領域中,而它在與業務相關的 Web API 領域中也逐漸引起了人們的關注。目前出現的一些首創概念要么還屬于實驗性質,要么很少為人所知,但已經開始有一些 API 提供商開始支持 API 文檔了。
Web 是一個充滿活力的、飛速發展的領域,為了即時到來的時刻,不妨關注一下 API 生態系統中的這種“元層面”的思想,這一過程應當是充滿趣味的。
Mike Amundsen是一位國際知名作者與講師,他經常在全球各地旅行,就分布式網絡架構、Web 應用開發以及其它主題進行顧問工作與演講。Amundsen 在 API Academy 擔任架構總監的角色,負責為全球范圍內的公司提供服務,幫助客戶與企業了解如何最好地利用由 API 帶來的無數機遇。在過去 15 年間,Amundsen 編寫了大量與編程相關的書籍與論文。他目前正在撰寫一本名為《學習客戶端超媒體》的新書,其中涵蓋了構建能夠充分利用超媒體 API 服務的客戶端應用的常見方式。他最近出版的一本書是與 Leonard Richardson 合著的《RESTful Web APIs》,于 2013 年出版。Amundsen 在 2011 年出版的書籍《通過 HTML5 與 Node 創建超媒體 API》是在構建具有可適應性的 Web 應用時經常被引用的一本著作。他目前正在為 O’Reilly 創作一本新書《學習客戶端超媒體》,在 2015 年秋季應當能夠看到它的身影。
查看英文原文: Description, Discovery, and Profiles: A Primer
本譯文轉自 《API 描述、發現與文檔入門》,譯者:邵思華