說在前面,這篇文章是無心中發現的,由於感受寫的很好,因此翻譯了一下。因爲英文水平有限,不免有出錯的地方,請看官理解一下。翻譯和校訂文章花了我大約2周的業餘時間,若有人願意轉載請註明出處,謝謝^_^html
Principles of good RESTful API Design程序員
好RESTful API的設計原則
web
Good API design is hard! An API represents a contract between you and those who Consume your data. Breaking this contract will result in many angry emails, and a slew of sad users with mobile apps which no longer work. Documentation is half the battle, and it is very difficult to find programmer who also likes to write.數據庫
Building an API is one of the most important things you can do to increase the value of your service. By having an API, your service / core application has the potential to become a platform from which other services grow. Look at the current huge tech companies: Facebook, Twitter, Google, GitHub, Amazon, Netflix… None of them would be nearly as big as they are today if they hadn’t opened up their data via API. In fact, an entire industry exists with the sole purpose of consuming data provided by said platforms.json
The easier your API is to consume, the more people that will consume it.api |
The principles of this document, if followed closely when designing your API, will ensure that Consumers of your API will be able to understand what is going on, and should drastically reduce the number of confused and/or angry emails you receive. I’ve organized everything into topics, which don’t necessarily need to be read in order.瀏覽器
作出一個好的API設計很難。API表達的是你的數據和你的數據使用者之間的契約。打破這個契約將會招致不少憤怒的郵件,和一大堆傷心的用戶-由於他們手機上的App不工做了。而文檔化只能達到一半的效果,而且也很難找到一個願意寫文檔的程序員。緩存
你所能作的最重要一件事來提升服務的價值就是建立一個API。由於隨着其餘服務的成長,有這樣一個API會使你的服務或者核心應用將有機會變成一個平臺。環顧一下現有的這些大公司:Facebook,Twitter,Google, Github,Amazon,Netflix等。若是當時他們沒有經過API來開放數據的話,也不可能成長到現在的規模。事實上,整個行業存在的惟一目的就是消費所謂平臺上的數據。服務器
你的API越容易使用,那麼就會有越多的人去用它 |
本文提到的這些原則,若是你的API能嚴格按照這些原則來設計,使用者就能夠知道它接下來要作什麼,而且能減小大量沒必要要的疑惑或者是憤怒的郵件。我已經把全部內容都整理到不一樣的主題裏了,你無需按順序去閱讀它。網絡
定義
Here’s a few of the important terms I will use throughout the course of this document:
這裏有一些很是重要的術語,我將在本文裏面一直用到它們:
數據設計與抽象
Planning how your API will look begins earlier than you’d think; first you need to decide how your data will be designed and how your core service / application will work. If you’re doing API First Development this should be easy. If you’re attaching an API to an existing project, you may need to provide more abstraction.
Occasionally, a Collection can represent a database table, and a Resource can represent a row within that table. However, this is not the usual case. In fact, your API should abstract away as much of your data and business logic as possible. It is very important that you don’t overwhelm Third-Party Developers with any complex application data, if you do they won’t want to use your API.
There are also many parts of your service which you SHOULD NOT expose via API at all. A common example is that many APIs will not allow third parties to create users.
規劃好你的API的外觀要先於開發它實際的功能。首先你要知道數據該如何設計和核心服務/應用程序會如何工做。若是你純粹新開發一個API,這樣會比較容易一些。但若是你是往已有的項目中增長API,你可能須要提供更多的抽象。
有時候一個集合能夠表達一個數據庫表,而一個資源能夠表達成裏面的一行記錄,可是這並非常態。事實上,你的API應該儘量經過抽象來分離數據與業務邏輯。這點很是重要,只有這樣作你纔不會打擊到那些擁有複雜業務的第三方開發者,不然他們是不會使用你的API的。
固然你的服務可能不少部分是不該該經過API暴露出去的。比較常見的例子就是不少API是不容許第三方來建立用戶的。
動詞
Surely you know about GET and POST requests. These are the two most commonly requests used when your browser visits different webpages. The term POST is so popular that it has even invaded common language, where people who know nothing about how the Internet works do know they can 「post」 something on a friends Facebook wall.
There are four and a half very important HTTP verbs that you need to know about. I say 「and a half」, because the PATCH verb is very similar to the PUT verb, and two two are often combined by many an API developer. Here are the verbs, and next to them are their associated database call (I’m assuming most people reading this know more about writing to a database than designing an API).
Here are two lesser known HTTP verbs:
A good RESTful API will make use of the four and a half HTTP verbs for allowing third parties to interact with its data, and will never include actions / verbs as URL segments.
Typically, GET requests can be cached (and often are!) Browsers, for example, will cache GET requests (depending on cache headers), and will go as far as prompt the user if they attempt to POST for a second time. A HEAD request is basically a GET without the response body, and can be cached as well.
顯然你瞭解GET和POST請求。當你用瀏覽器去訪問不一樣頁面的時候,這兩個是最多見的請求。POST術語如此流行以致於開始侵擾通俗用語。即便是那些不知道互聯網如何工做的人們也能「post」一些東西到朋友的Facebook牆上。
這裏至少有四個半很是重要的HTTP動詞須要你知道。我之因此說「半個」的意思是PATCH這個動詞很是相似於PUT,而且它們倆也經常被開發者綁定到同一個API上。
還有兩個不經常使用的HTTP動詞:
一個好的RESTful API只容許第三方調用者使用這四個半HTTP動詞進行數據交互,而且在URL段裏面不出現任何其餘的動詞。
通常來講,GET請求能夠被瀏覽器緩存(一般也是這樣的)。例如,緩存請求頭用於第二次用戶的POST請求。HEAD請求是基於一個無響應體的GET請求,而且也能夠被緩存的。
版本化
No matter what you are building, no matter how much planning you do beforehand, your core application is going to change, your data relationships will change, attributes will invariably be added and removed from your Resources. This is just how software development works, and is especially true if your project is alive and used by many people (which is likely the case if you’re building an API).
Remember than an API is a published contract between a Server and a Consumer. If you make changes to the Servers API and these changes break backwards compatibility, you will break things for your Consumer and they will resent you for it. Do it enough, and they will leave. To ensure your application evolves AND you keep your Consumers happy, you need to occasionally introduce new versions of the API while still allowing old versions to be accessible.
As a side note, if you are simply ADDING new features to your API, such as new attributes on a Resource (which are not required and the Resource will function without), or if you are ADDING new Endpoints, you do not need to increment your API version number since these changes do not break backwards compatibility. You will want to update your API Documentation (your Contract), of course.
Over time you can deprecate old versions of the API. To deprecate a feature doesn’t mean to shut if off or diminish the quality of it, but to tell Consumers of your API that the older version will be removed on a specific date and that they should upgrade to a newer version.
A good RESTful API will keep track of the version in the URL. The other most common solution is to put a version number in a request header, but after working with many different Third Party Developers, I can tell you that adding headers is no where near as easy as adding a URL Segment.
不管你正在構建什麼,不管你在入手前作了多少計劃,你核心的應用總會發生變化,數據關係也會變化,資源上的屬性也會被增長或刪除。只要你的項目還活着,而且有大量的用戶在用,這種狀況老是會發生。
請謹記一點,API是服務器與客戶端之間的一個公共契約。若是你對服務器上的API作了一個更改,而且這些更改沒法向後兼容,那麼你就打破了這個契約,客戶端又會要求你從新支持它。爲了不這樣的事情,你既要確保應用程序逐步的演變,又要讓客戶端滿意。那麼你必須在引入新版本API的同時保持舊版本API仍然可用。
注:若是你只是簡單的增長一個新的特性到API上,如資源上的一個新屬性或者增長一個新的端點,你不須要增長API的版本。由於這些並不會形成向後兼容性的問題,你只須要修改文檔便可。
隨着時間的推移,你可能聲明再也不支持某些舊版本的API。申明不支持一個特性並不意味着關閉或者破壞它。而是告訴客戶端舊版本的API將在某個特定的時間被刪除,而且建議他們使用新版本的API。
一個好的RESTful API會在URL中包含版本信息。另外一種比較常見的方案是在請求頭裏面保持版本信息。可是跟不少不一樣的第三方開發者一塊兒工做後,我能夠很明確的告訴你,在請求頭裏麪包含版本信息遠沒有放在URL裏面來的容易。
分析
Keep track of the version/endpoints of your API being used by Consumers. This can be as simple as incrementing an integer in a database each time a request is made. There are many reasons that keeping track of API Analytics is a good idea, for example, the most commonly used API calls should be made efficient.
For the purposes of building an API which Third Party Developers will love, the most important thing is that when you do deprecate a version of your API, you can actually contact developers using deprecated API features. This is the perfect way to remind them to upgrade before you kill the old API version.
The process of Third Party Developer notification can be automated, e.g. mail the developer every time 10,000 requests to a deprecated feature are made.
所謂API分析就是持續跟蹤那些正爲人使用的API的版本和端點信息。而這可能就跟每次請求都往數據庫增長一個整數那樣簡單。有不少的緣由顯示API跟蹤分析是一個好主意,例如,對那些使用最普遍的API來講效率是最重要的。
第三方開發者一般會關注API的構建目的,其中最重要的一個目的是你決定何時再也不支持某個版本。你須要明確的告知開發者他們正在使用那些即將被移除的API特性。這是一個很好的方式在你準備刪除舊的API以前去提醒他們進行升級。
固然第三方開發者的通知流程能夠以某種條件被自動觸發,例如每當一個過期的特性上發生10000次請求時就發郵件通知開發者。
API根URL
The root location of your API is important, believe it or not. When a developer (read as code archaeologist) inherits an old project using your API and needs to build new features, they may not know about your service at all. Perhaps all they know is a list of URLs which the Consumer calls out to. It’s important that the root entry point into your API is as simple as possible, as a long complex URL will appear daunting and can turn developers away.
Here are two common URL Roots:
If your application is huge, or you anticipate it becoming huge, putting the API on its own subdomain (e.g. api.) is a good choice. This can allow for some more flexible scalability down the road.
If you anticipate your API will never grow to be that large, or you want a much simpler application setup (e.g. you want to host the website AND API from the same framework), placing your API beneath a URL segment at the root of the domain (e.g. /api/) works as well.
It’s a good idea to have content at the root of your API. Hitting the root of GitHub’s API returns a listing of endpoints, for example. Personally, I’m a fan of having the root URL give information which a lost developer would find useful, e.g., how to get to the developer documentation for the API.
Also, notice the HTTPS prefix. As a good RESTful API, you must host your API behind HTTPS.
不管你信不信,API的根地址很重要。當一個開發者接手了一箇舊項目(如進行代碼考古時)。而這個項目正在使用你的API,同時開發者還想構建一個新的特性,但他們徹底不知道你的服務。幸運的是他們知道客戶端對外調用的那些URL列表。讓你的API根入口點保持儘量的簡單是很重要的,由於開發者極可能一看到那些冗長而又複雜的URL就轉身而走。
這裏有兩個常見的URL根例子:
若是你的應用很龐大或者你預期它將會變的很龐大,那麼將API放到子域下一般是一個好選擇。這種作法能夠保持某些規模化上的靈活性。
但若是你以爲你的API不會變的很龐大,或是你只是想讓應用安裝更簡單些(如你想用相同的框架來支持站點和API),將你的API放到根域名下也是能夠的。
讓API根擁有一些內容一般也是個好主意。Github的API根就是一個典型的例子。從我的角度來講我是一個經過根URL發佈信息的粉絲,這對不少人來講是有用的,例如如何獲取API相關的開發文檔。
一樣也請注意HTTPS前綴,一個好的RESTful API老是基於HTTPS來發布的。
端點
An Endpoint is a URL wi個thin your API which points to a specific Resource or a Collection of Resources.
If you were building a fictional API to represent several different Zoo’s, each containing many Animals (with an animal belonging to exactly one Zoo), employees (who can work at multiple zoos) and keeping track of the species of each animal, you might have the following endpoints:
When referring to what each endpoint can do, you’ll want to list valid HTTP Verb and Endpoint combinations. For example, here’s a semi-comprehensive list of actions one can perform with our fictional API. Notice that I’ve preceded each endpoint with the HTTP Verb, as this is the same notation used within an HTTP Request header.
In the above list, ZID means Zoo ID, AID means Animal ID, EID means Employee ID, and ATID means Animal Type ID. Having a key in your documentation for whatever convention you choose is a good idea.
I’ve left out the common API URL prefix in the above examples for brevity. While this can be fine during communications, in your actual API documentation, you should always display the full URL to each endpoint (e.g. GET http://api.example.com/v1/animal_type/ATID).
Notice how the relationships between data is displayed, specifically the many to many relationships between employees and zoos. By adding an additional URL segment, one can perform more specific interactions. Of course there is no HTTP verb for 「FIRE」-ing an employee, but by performing a DELETE on an Employee located within a Zoo, we’re able to achieve the same effect.
一個端點就是指向特定資源或資源集合的URL。
若是你正在構建一個虛構的API來展示幾個不一樣的動物園,每個動物園又包含不少動物,員工和每一個動物的物種,你可能會有以下的端點信息:
針對每個端點來講,你可能想列出全部可行的HTTP動詞和端點的組合。以下所示,請注意我把HTTP動詞都放在了虛構的API以前,正如將一樣的註解放在每個HTTP請求頭裏同樣。(下面的URL就不翻譯了,我以爲沒啥必要翻^_^)
在上面的列表裏,ZID表示動物園的ID, AID表示動物的ID,EID表示僱員的ID,還有ATID表示物種的ID。讓文檔裏全部的東西都有一個關鍵字是一個好主意。
爲了簡潔起見,我已經省略了全部API共有的URL前綴。做爲溝通方式這沒什麼問題,可是若是你真要寫到API文檔中,那就必須包含完整的路徑(如,GET http://api.example.com/v1/animal_type/ATID)。
請注意如何展現數據之間的關係,特別是僱員與動物園之間的多對多關係。經過添加一個額外的URL段就能夠實現更多的交互能力。固然沒有一個HTTP動詞能表示正在解僱一我的,可是你可使用DELETE一個動物園裏的僱員來達到相同的效果。
過濾器
When a Consumer makes a request for a listing of objects, it is important that you give them a list of every single object matching the requested criteria. This list could be massive. But, it is important that you don’t perform any arbitrary limitations of the data. It is these arbitrary limits which make it hard for a third party developer to know what is going on. If they request a certain Collection, and iterate over the results, and they never see more than 100 items, it is now their job to figure out where this limit is coming from. Is their ORM buggy and limiting items to 100? Is the network chopping up large packets?
Minimize the arbitrary limits imposed on Third Party Developers. |
It is important, however, that you do offer the ability for a Consumer to specify some sort of filtering/limitation of the results. The most important reason for this is that the network activity is minimal and the Consumer gets their results back as soon as possible. The second most important reason for this is the Consumer may be lazy, and if the Server can do filtering and pagination for them, all the better. The not-so-important reason (from the Consumers perspective), yet a great benefit for the Server, is that the request will be less resource heavy.
Filtering is mostly useful for performing GETs on Collections of resources. Since these are GET requests, filtering information should be passed via the URL. Here are some examples of the types of filtering you could conceivably add to your API:
name
ASC) Some of these filterings can be redundant with endpoint URLS. For example I previously mentioned GET /zoo/ZID/animals. This would be the same thing as GET /animals?zoo_id=ZID. Dedicated endpoints being made available to the Consumer will make their lives easier, this is especially true with requests you anticipate they will make a lot. In the documentation, mention this redundancy so that Third Party Developers aren’t left wondering if differences exist.
Also, this goes without saying, but whenever you perform filtering or sorting of data, make sure you white-list the columns for which the Consumer can filter and sort by. We don’t want any database errors being sent to Consumers!
當客戶端建立了一個請求來獲取一個對象列表時,很重要一點就是你要返回給他們一個符合查詢條件的全部對象的列表。這個列表可能會很大。但你不能隨意給返回數據的數量作限制。由於這些無謂的限制會致使第三方開發者不知道發生了什麼。若是他們請求一個確切的集合而且要遍歷結果,然而他們發現只拿到了100條數據。接下來他們就不得不去查找這個限制條件的出處。究竟是ORM的bug致使的,仍是由於網絡截斷了大數據包?
儘量減小那些會影響到第三方開發者的無謂限制 |
這點很重要,但你可讓客戶端本身對結果作一些具體的過濾或限制。這麼作最重要的一個緣由是能夠最小化網絡傳輸,並讓客戶端儘量快的獲得查詢結果。其次是客戶端可能比較懶,若是這時服務器能對結果作一些過濾或分頁,對你們都是好事。另一個不那麼重要的緣由是(從客戶端角度來講),對服務器來講響應請求的負載越少越好。
過濾器是最有效的方式去處理那些獲取資源集合的請求。因此只要出現GET的請求,就應該經過URL來過濾信息。如下有一些過濾器的例子,多是你想要填加到API中的:
有些過濾器可能會與端點URL的效果重複。例如我以前提到的GET /zoo/ZID/animals。它也一樣能夠經過GET /animals?zoo_id=ZID來實現。獨立的端點會讓客戶端更好過一些,由於他們的需求每每超出你的預期。本文中提到這種冗餘差別可能對第三方開發者並不可見。
不管怎麼說,當你準備過濾或排序數據時,你必須明確的將那些客戶端能夠過濾或排序的列放到白名單中,由於咱們不想將任何的數據庫錯誤發送給客戶端。
狀態碼
It is very important that as a RESTful API, you make use of the proper HTTP Status Codes; they are a standard after all! Various network equipment is able to read these status codes, e.g. load balancers can be configured to avoid sending requests to a web server sending out lots of 50x errors. There are a plethora of HTTP Status Codes to choose from, however this list should be a good starting point:
對於一個RESTful API來講很重要的一點就是要使用HTTP的狀態碼,由於它們是HTTP的標準。不少的網絡設備均可以識別這些狀態碼,例如負載均衡器可能會經過配置來避免發送請求到一臺web服務器,若是這臺服務器已經發送了不少的50x錯誤回來。這裏有大量的HTTP狀態碼能夠選擇,可是下面的列表只給出了一些重要的代碼做爲一個參考:
狀態碼範圍
The 1xx range is reserved for low-level HTTP stuff, and you’ll very likely go your entire career without manually sending one of these status codes.
The 2xx range is reserved for successful messages where all goes as planned. Do your best to ensure your Server sends as many of these to the Consumer as possible.
The 3xx range is reserved for traffic redirection. Most APIs do not use these requests much (not nearly as often as the SEO folks use them ;), however, the newer Hypermedia style APIs will make more use of these.
The 4xx range is reserved for responding to errors made by the Consumer, e.g. they’re providing bad data or asking for things which don’t exist. These requests should be be idempotent, and not change the state of the server.
The 5xx range is reserved as a response when the Server makes a mistake. Often times, these errors are thrown by low-level functions even outside of the developers hands, to ensure a Consumer gets some sort of response. The Consumer can’t possibly know the state of the server when a 5xx response is received, and so these should be avoidable.
1xx範圍的狀態碼是保留給底層HTTP功能使用的,而且估計在你的職業生涯裏面也用不着手動發送這樣一個狀態碼出來。
2xx範圍的狀態碼是保留給成功消息使用的,你儘量的確保服務器總髮送這些狀態碼給用戶。
3xx範圍的狀態碼是保留給重定向用的。大多數的API不會太常使用這類狀態碼,可是在新的超媒體樣式的API中會使用更多一些。
4xx範圍的狀態碼是保留給客戶端錯誤用的。例如,客戶端提供了一些錯誤的數據或請求了不存在的內容。這些請求應該是冪等的,不會改變任何服務器的狀態。
5xx範圍的狀態碼是保留給服務器端錯誤用的。這些錯誤經常是從底層的函數拋出來的,而且開發人員也一般無法處理。發送這類狀態碼的目的是確保客戶端能獲得一些響應。收到5xx響應後,客戶端沒辦法知道服務器端的狀態,因此這類狀態碼是要儘量的避免。
預期的返回文檔
When performing actions using the different HTTP verbs to Server endpoints, a Consumer needs to get some sort of information in return. This list is pretty typical of RESTful APIs:
Note that when a Consumer creates a Resource, they usually do not know the ID of the Resource being created (nor other attributes such as created and modified timestamps, if applicable). These additional attributes are returned with subsequent request, and of course as a response to the initial POST.
當使用不一樣的HTTP動詞向服務器請求時,客戶端須要在返回結果裏面拿到一系列的信息。下面的列表是很是經典的RESTful API定義:
請注意當一個客戶端建立一個資源時,她們經常不知道新建資源的ID(也許還有其餘的屬性,如建立和修改的時間戳等)。這些屬性將在隨後的請求中返回,而且做爲剛纔POST請求的一個響應結果。
認證
Most of the time a Server will want to know exactly who is making which Requests. Sure, some APIs provide endpoints to be consumed by the general (anonymous) public, but most of the time work is being perform on behalf of someone.
OAuth 2.0 provides a great way of doing this. With each Request, you can be sure you know which Consumer is making requests, which User they are making requests on behalf of, and provides a (mostly) standardized way of expiring access or allowing Users to revoke access from a Consumer, all without the need for a third-party consumer to know the Users login credentials.
There are also OAuth 1.0 and xAuth, which fill the same space. Whichever method you choose, make sure it is something common and well documented with many different libraries written for the languages/platforms which your Consumers will likely be using.
I can honestly tell you that OAuth 1.0a, while it is the most secure of the options, is a huge pain in the ass to implement. I was surprised by the number of Third Party Developers who had to implement their own library since one didn’t exist for their language already. I’ve spent enough hours debugging cryptic 「invalid signature」 errors to recommend you choose an alternative.
服務器在大多數狀況下是想確切的知道誰建立了什麼請求。固然,有些API是提供給公共用戶(匿名用戶)的,可是大部分時間裏也是表明某人的利益。
OAuth2.0提供了一個很是好的方法去作這件事。在每個請求裏,你能夠明確知道哪一個客戶端建立了請求,哪一個用戶提交了請求,而且提供了一種標準的訪問過時機制或容許用戶從客戶端註銷,全部這些都不須要第三方的客戶端知道用戶的登錄認證信息。
還有OAuth1.0和xAuth一樣適用這樣的場景。不管你選擇哪一個方法,請確保它爲多種不一樣語言/平臺上的庫提供了一些通用的而且設計良好文檔,由於你的用戶可能會使用這些語言和平臺來編寫客戶端。
內容類型
Currently, the most 「exciting」 of APIs provide JSON data from RESTful interfaces. This includes Facebook, Twitter, GitHub, you name it. XML appears to have lost the war a while ago (except in large corporate environments). SOAP, thankfully, is all but dead, and we really don’t see much APIs providing HTML to be consumed (unless, that is, you’re building a scraper!)
Developers using popular languages and frameworks can very likely parse any valid data format you return to them. You can even provide data in any of the aforementioned data formats (not including SOAP) quite easily, if you’re building a common response object and using a different serializer. What does matter though, is that you make use of the Accept header when responding with data.
Some API creators recommend adding a .json, .xml, or .html file extension to the URL (after the endpoint) for specifying the content type to be returned, although I’m personally not a fan of this. I really like the Accept header (which is built into the HTTP spec) and feel that is the appropriate thing to use.
目前,大多數「精彩」的API都爲RESTful接口提供JSON數據。諸如Facebook,Twitter,Github等等你所知的。XML曾經也火過一把(一般在一個大企業級環境下)。這要感謝SOAP,不過它已經掛了,而且咱們也沒看到太多的API把HTML做爲結果返回給客戶端(除非你在構建一個爬蟲程序)。
只要你返回給他們有效的數據格式,開發者就可使用流行的語言和框架進行解析。若是你正在構建一個通用的響應對象,經過使用一個不一樣的序列化器,你也能夠很容易的提供以前所提到的那些數據格式(不包括SOAP)。而你所要作的就是把使用方式放在響應數據的接收頭裏面。
有些API的建立者會推薦把.json, .xml, .html等文件的擴展名放在URL裏面來指示返回內容類型,但我我的並不習慣這麼作。我依然喜歡經過接收頭來指示返回內容類型(這也是HTTP標準的一部分),而且我以爲這麼作也比較適當一些。
超媒體API
Hypermedia APIs are very likely the future of RESTful API design. They’re actually a pretty amazing concept, going 「back to the roots」 of how HTTP and HTML was intended to work.
When working with non-Hypermedia RESTful APIs, the URL Endpoints are part of the contract between the Server and the Consumer. These Endpoints MUST be known by the Consumer ahead of time, and changing them means the Consumer is no longer able to communicate with the Server as intended. This, as you can assume, is quite a limitation.
Now, API Consumers are of course not the only user agent making HTTP requests on the Internet. Far from it. Humans, with their web browsers, are the most common user agent making HTTP requests. Humans, however, are NOT locked into this predefined Endpoint URL contract that RESTful APIs are. What makes humans so special? Well, they’re able to read content, click links for headings which look interesting, and in general explore a website and interpret content to get to where they want to go. If a URL changes, a human is not affected (unless, that is, they bookmarked a page, in which case they go to the homepage and find a new route to their beloved data).
The Hypermedia API concept works the same way a human would. Requesting the Root of the API returns a listing of URLs which point perhaps to each collection of information, and describing each collection in a way which the Consumer can understand. Providing IDs for each resource isn’t important (or necessarily required), as long as a URL is provided.
With the Consumer of a Hypermedia API crawling links and gathering information, URLs are always up-to-date within responses, and do not need to be known beforehand as part of a contract. If a URL is ever cached, and a subsequent request returns a 404, the Consumer can simply go back to the root and discover the content again.
When retrieving a list of Resources within a Collection, an attribute containing a complete URL for the individual Resources are returned. When performing a POST/PATCH/PUT, the response can be a 3xx redirect to the complete Resource.
JSON doesn’t quite give us the semantics we need for specifying which attributes are URLs, nor how URLs relate to the current document. HTML, as you can probably guess, does provide this information. We may very well see our APIs coming full circle and returning back to consuming HTML. Considering how far we’ve come with CSS, one day we may even see it be common practice for APIs and Websites to use the exact same URLs and content.
超媒體API極可能就是RESTful API設計的未來。超媒體是一個很是棒的概念,它迴歸到了HTTP和HTML如何運做的「本質」。
在非超媒體RESTful API的情景中,URL端點是服務器與客戶端契約的一部分。這些端點必須讓客戶端事先知道,而且修改它們也意味着客戶端可能再也沒法與服務器通訊了。你能夠先假定這是一個限制。
時至今日,英特網上的API客戶端已經不只僅只有那些建立HTTP請求的用戶代理了。大多數HTTP請求是由人們經過瀏覽器產生的。人們不會被哪些預先定義好的RESTful API端點URL所束縛。是什麼讓人們變的如此不同凡響?由於人們能夠閱讀內容,能夠點擊他們感興趣的連接,並瀏覽一下網站,而後跳到他們關注的內容那裏。即便一個URL改變了,人們也不會受到影響(除非他們事先給某個頁面作了書籤,這時他們回到主頁並發現原來有一條新的路徑能夠去往以前的頁面)。
超媒體API概念的運做跟人們的行爲相似。經過請求API的根來得到一個URL的列表,這個列表裏面的每個URL都指向一個集合,而且提供了客戶端能夠理解的信息來描述每個集合。是否爲每個資源提供ID並不重要(或者不是必須的),只要提供URL便可。
一個超媒體API一旦具備了客戶端,那麼它就能夠爬行連接並收集信息,而URL老是在響應中被更新,而且不須要如契約的一部分那樣事先被知曉。若是一個URL曾經被緩存過,而且在隨後的請求中返回404錯誤,那麼客戶端能夠很簡單的回退到根URL並從新發現內容。
在獲取集合中的一個資源列表時會返回一個屬性,這個屬性包含了各個資源的完整URL。當實施一個POST/PATCH/PUT請求後,響應能夠被一個3xx的狀態碼重定向到完整的資源上。
JSON不只告訴了咱們須要定義哪些屬性做爲URL,也告訴了咱們如何將URL與當前文檔關聯的語義。正如你猜的那樣,HTML就提供了這樣的信息。咱們可能很樂意看到咱們的API走完了完整的週期,並回到了處理HTML上來。想一下咱們與CSS一塊兒前行了多遠,有一天咱們可能再次看到它變成了一個通用實踐讓API和網站能夠去使用相同的URL和內容。
文檔
Honestly, if you don’t conform 100% to the criteria in this guide, your API will not necessarily be horrible. However, if you don’t properly document your API, nobody is going to know how to use it, and it WILL be a horrible API.
Make your Documentation available to unauthenticated developers.
Do not use automatic documentation generators, or if you do, at least make sure you’re doctoring it up and making it presentable.
Do not truncate example request and response bodies; show the whole thing. Use a syntax highlighter in your documentation.
Document expected response codes and possible error messages for each endpoint, and what could have gone wrong to cause those error messages.
If you’ve got the spare time, build a developer API console so that developers can immediately experiment with your API. It’s not as hard as you might think and developers (both internal and third party) will love you for it!
Make sure your documentation can be printed; CSS is a powerful thing; don’t be afraid to hide that sidebar when the docs are printed. Even if nobody ever prints a physical copy, you’d be surprised at how many developers like to print to PDF for offline reading.
老實說,即便你不能百分之百的遵循指南中的條款,你的API也不是那麼糟糕。可是,若是你不爲API準備文檔的話,沒有人會知道怎麼使用它,那它真的會成爲一個糟糕的API。
若是你有富餘的時間,那就建立一個控制檯來讓開發者能夠當即體驗一下API的功能。建立一個控制檯並無想象中那麼難,而且開發者們(內部或者第三方)也會所以而擁戴你。
另外確保你的文檔可以被打印。CSS是個強大的工具能夠幫助到你。並且在打印的時候也不用太擔憂邊側欄的問題。即使沒有人會打印到紙上,你也會驚奇的發現不少開發者願意轉化成PDF格式進行離線閱讀。
勘誤:原始的HTTP封包
Since everything we do is over HTTP, I’m going to show you a dissection of an HTTP packet. I’m often surprised at how many people don’t know what these things look like! When the Consumer sends a Request to the Server, they provide a set of Key/Value pairs, called a Header, along with two newline characters, and finally the request body. This is all sent in the same packet.
The server then responds in the say Key/Value pair format, with two newlines and then the response body. HTTP is very much a request/response protocol; there is no 「Push」 support (the Server sending data to the Consumer unprovoked), unless you use a different protocol such as Websockets.
When designing your API, you should be able to work with tools which allow you to look at raw HTTP packets. Consider using Wireshark, for example. Also, make sure you are using a framework / web server which allows you to read and change as many of these fields as possible.
由於咱們所作的都是基於HTTP協議,因此我將展現給你一個解析了的HTTP封包。我常常很驚訝的發現有多少人不知道這些東西。當客戶端發送一個請求道服務器時,他們會提供一個鍵值對集,先是一個頭,緊跟着是兩個回車換行符,而後纔是請求體。全部這些都是在一個封包裏被髮送。
服務器響應也是一樣的鍵值對集,帶兩個回車換行符,而後是響應體。HTTP就是一個請求/響應協議;它不支持「推送」模式(服務器直接發送數據給客戶端),除非你採用其餘協議,如Websockets。
當你設計API時,你應該可以使用工具去查看原始的HTTP封包。Wireshark是個不錯的選擇。同時,你也該採用一個框架/web服務器,使你可以在必要時修改某些字段的值。
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 }