Showing posts with label Web Service. Show all posts
Showing posts with label Web Service. Show all posts

Monday, September 28, 2015

Build APIs You Wont Hate



Build APIs You Wont Hate
https://github.com/philsturgeon/build-apis-you-wont-hate
《Build APIs You Won't Hate》筆記
1 Useful Database Seeding
Don’t use integers as timestamps.
If you store America/New_York or Asia/Khandyga for users, then the offset and daylight savings time will be automatically calculated.
java-faker

2 Planning and Creating Endpoints
GET /resources/X,Y,Z - The client wants multiple things, so give them multiple things.
• GET /places/X/checkins - Find all the checkins for a specific place.
• GET /users/X/checkins - Find all the checkins for a specific user.
• GET /users/X/checkins/Y - Find a specific checkin for a specific user.

Auto-Increment is the Devil - Use UUID
DELETE /places/X,Y,Z - Delete a bunch of places.
• DELETE /places
• DELETE /places/X/image - Delete the image for a place, or:
• DELETE /places/X/images - If you chose to have multiple images this would remove all of them.

PUT is used if you know the entire URL before hand and the action is idempotent.
Use Plural Noun

3 Input and Output Theory
No Form Data
x-www-form-urlencoded is a mess
Don't mix JSON with form data
Much Namespace, Nice Output
{
"data": {
"name": "Phil Sturgeon",
"id": "511501255"
}
}

Ask for a collection of things and get a collection of things, but namespaced:
{
"data": [
{
"name": "Hulk Hogan",
"id": "100002"
},
{
"name": "Mick Foley",
"id": "100003"
}
]
}
By placing the collection into the "data" namespace you can easily add other content next to it which relates to the response but is not part of the list of resources at all. Counts, links, etc can all go here

4 Status Codes, Errors and Messages
• 202 - Accepted but is being processed async (for a video means encoding, for an image means resizing,
etc)
• 400 - Bad Request (should really be for invalid syntax, but some folks use for validation)
• 403 - The current user is forbidden from accessing this data
• 410 - Data has been deleted, deactivated, suspended, etc
• 405 - Method Not Allowed (your framework will probably do this for you)
• 500 - Something unexpected happened and it is the APIs fault
• 503 - API is not here right now, please try again later
your Amazon Elastic Load Balancer has no healthy instances (503) or if your hard-drive fills up somehow (507)

{
"error": {
"type": "OAuthException",
"code": "ERR-01234",
"documentation_url": "http://example.com/docs/errors/#ERR-01234"
"message": "..."
}
}

4.4 Error or Errors
simply stop processing (exit out) after the first error to avoid further controller interaction.

5 Endpoint Testing

6 Outputting Data
Performance: If you return “all” items then that will be fine during development, but suck when you have a thousand records in that table… or a million.

6.4 Hiding Schema Updates
Change the old value to new value:
'status' => $place->status === 'available' ? 'active' : $place->status,

7 Data Relationships
“downloading enough data to avoid making the user wait for subsequent loads”
and “downloading too much data to make them wait for the initial load” is hard.

An API needs the flexibility and making sub-resources the only way to load related data is restrictive for the API consumer.

7.3 Foreign Key Arrays
7.4 Compound Documents (a.k.a Side-Loading)
7.5 Embedded Documents (a.k.a Nesting)
/places?embed=checkins,merchant
/places?embed=checkins,merchant,current_opp.images

8 Debugging
curl -X POST http://localhost/places/fg345d/checkins --data @payload.json
Postman
Debug Panel
RailsPanel² - Chrome-only DevTool panel with logging and profiling for Ruby on Rails. (RailsCasts Video³).
Clockwork⁴ - Chrome DevTool panel and standalone web app with logging and profiling for PHP.
Chrome Logger⁵ - Chrome Logger only for Python, PHP, Ruby, Node, .NET, CF and Go.

8.4 Network Debugging
Charles

9 Authentication
Approach #1: Basic Authentication
php -a
echo base64_decode('QWxhZGRpbjpvcGVuIHNlc2FtZQ==');

Approach #2: Digest Authentication
Approach #4: OAuth 2.0 - ssl, access token
file_get_contents('https://graph.facebook.com/me?access_token=DFGJKHDFGHDIFHGFKDJGHIU');
use the Authorization header to send your tokens whenever possible.

“Short”-life Tokens
Grant Types
OpenID
Hawk
Oz

10 Pagination
Define a Maximum
"pagination" : {
"total": 1000,
"count": 12,
"per_page": 12,
"current_page": 1,
"total_pages": 84,
"next_url": "https://api.example.com/places?page=2&number=12",
}

Counting lots of Data is Hard - SELECT count(*) is expensive
10.3 Offsets and Cursors
If there is more data to be found, the API will return that data. If there is not more data, then either an error (404) or an empty collection will be returned.
"pagination" : {
"cursors": {
"after": 12,
"next_url": "https://api.example.com/places?cursor=12&number=12"
}
}

Obscuring Cursors

11 Documentation
API Reference
Sample Code
Guides or Tutorials
Tool: swagger,API Blueprint

12 HATEOAS
1. Content negotiation
2. Hypermedia controls
"links": [
{
"rel": "self",
"uri": "/places/2"
},
{
"rel": "place.checkins",
"uri": "/places/2/checkins"
},
{
"rel": "place.image",
"uri": "/places/2/image"
}
]

http://www.iana.org/assignments/link-relations/link-relations.xhtml
$response = $client->options('/places/2/checkins');

13 API Versioning
Versioning and Types in REST/HTTP API Resources
http://thereisnorightway.blogspot.com/2011/02/versioning-and-types-in-resthttp-api.html

How are REST APIs versioned?
http://www.lexicalscope.com/blog/2012/03/12/how-are-rest-apis-versioned/

Approach #1: URI
https://api.example.com/v1/places

As long as you share a Git history you can pull from the other repository or branch, and merge changes up
from older versions to newer versions.

Approach #2: Hostname
https://api-v1.example.com/places

Approach #3: Body and Query Params
{"version" : "1.0"}

Approach #3: Custom Request Header
BadAPIVersion: 1.1
Vary: BadAPIVersion
• Cache systems can get confused

Approach #4: Content Negotiation
Accept: application/vnd.github.v3+json
• Keeps URLs the same
• HATEOAS-friendly
• Cache-friendly

Approach #5: Content Negotiation for Resources
1 Accept: application/vnd.github.user.v4+json
Alternatively, the Accept header is capable of containing arbitrary parameters.
1 Accept: application/vnd.github.user+json; version=4.0

application/vnd.example.place.v1+json and application/vnd.example.place.json;version=1

Approach #6: Feature Flagging
Expires, Etag, Retry-After


³http://barelyenough.org/blog/2008/05/versioning-rest-web-services/
⁴http://nvie.com/posts/a-successful-git-branching-model/

https://pastewall.com/sticker/c98b00bf9fbd46789a9302c5b98cbdfa
Ch1. Useful Database Seeding
凡事寫Testing的程式幾乎都會做Seeding,也就是準備好一些隨機的資料餵進資料庫,然後用這些資料做測試。當然Seeding也有可能用在人員做local端的開發時,需要假資料來測試一些功能的時候。
其中一個重點是,關於紀錄使用者的時區,可不能只有紀錄+8的offset,應該要紀錄完整的timezone時區名稱,不然北京跟台北都是+8,但很多人會在意時區名稱的差別。

Ch2. Planning and Creating Endpoints
這邊教了一個開始訂API的一個小技巧:首先將所有會用到的物件(everything)列出來
,然後對這些物件訂下會執行的動作(action)。這裡的動作可能不單純只是Create、Read、Update、Delete,也有可能是List(列出這物件的清單)、Image(為這物件附加一個圖片)、Like(使用者喜歡這物件),盡可能地列出。接下來就可以訂API的URL了。

接下來就是訂URL的技巧了:
  1. 善用HTTP Method:GET、POST、PUT、DELETE、PATCH,其中POST跟PUT都算是更新資料,但就差在前者不是idempotent,也就是前者重複呼叫結果可能會不同,後者呼叫多少次應該都要有相同結果。像是POST /users/1/messages會新增一則訊息,POST很多次就會產生很多個新訊息,但PUT /users/1/messages可能只是修改這則訊息的內容。
  2. URL中的所有物件名稱都應該使用複數,這樣單一Endpoint也可只支援多個參數,像是:
    /users - 條列出多個使用者的資料
    /users/1 - 指定某個使用者的資料
    /users/1,2 - 指定多個使用者的資料
  3. URL應該都只有出現名詞,因為動詞已經用HTTP Method來表達了。

Ch3. Input and Output Theory
由於現在瀏覽器跟網頁技術很先進了,呼叫Server API可以的話應該使用application/json來傳遞參數給server端。
Server的回應也應該以application/json為主,XML格式因為無法表達出字串以外的資料型態,像JSON就有boolean、number、string這些差別,所以不建議使用XML的回應。若要使用的話應該用程式從JSON格式自動轉XML就可以了。
回應的格式應該將回傳的資料部分用個變數"data"包起來:
GET /users/1 回傳單筆資料:
{
"data" : { "id": 1, "username": "Foo" }
}
GET /users 回傳清單資料:
{
"data": [ { "id": 1, "username": "Foo" }, { "id": 2, "username": "Bar" } ]
}
這樣就避免回傳就直接是 { "id": 1, "username": "Foo" } 結果一些metadata就放不進回傳之中了,像是pagination或是error message,因為放進去就跟物件的資料混在一起了。

Ch4. Status Codes, Errors and Messages
善用HTTP Status Code:
  1. 2xx is all about success
  2. 3xx is all about redirection
  3. 4xx is all about client errors
  4. 5xx is all about service errors
但記住Status Code也不要用太多,全用上並不會解除成就(You won’t unlock any achievements for using them all.)。
當發生錯誤時,建議回傳一個錯誤的清單:
{
"errors": [
{
"code": "109",
"title": "OAuth Exception",
"details": "There is no access_token in the request header",
"href": "http://doc.example.com/errors/109"
}
]
}
使用HTTP Status Code搭配error code,讓語意能夠被理解,又有code可以讓程式對這個錯誤做反應。title跟details都是給人看的資料,當然也可以做成給使用者看的錯誤訊息。href就讓使用者知道錯誤的詳細資訊的連結。

Ch5. Endpoint Testing
這就跟每個Framework或是開發語言相關了,就不再詳細介紹。

Ch6. Outputting Data
輸出資料時記得不要傻傻地將ORM回傳的東西直接轉JSON丟出去了,這樣可能會有一些不應該被公開的欄位被拿到。

Ch7. Data Relationships
這張主要介紹當物件之間有關連時,例如物件A包含多個物件B,要怎麼在讀取A的時候拿到B的資料。
最簡單的方法就是A一個API,然後B一個API,但因為拿到A之後就要發很多個request去將B撈齊全,這樣會有太多request讓效能變超級差,所以要依照client合理的使用狀況將資料嵌入A的回傳資料中。嵌入方法有以下:

    Foreign Key Arrays:在物件A裡面加入一串B的key,然後之後再呼叫B的API去撈取這些資料。雖然可以合併拿資料,像是使用 /b-resource/1,2 ,但還是有點麻煩,而且前端會很累。

    Compound Documents(Side Loading):回應裡面物件A長的跟「Foreign Key Arrays」一樣,但是"data"之外再加上一個區塊"included"表示物件B的資料:
    {
    "data": { "id": 1, "title": "Foo", "content": "Bar", "comments": [ "1", "2" ] },
    "included": [{
    "type": "comment",
    "id": 1,
    "author": "Baz",
    "content": "Baz"
    }]
    }
    這也是有client端要手動縫合資料的問題,可能也不是非常建議。

    Embedded Documents:將物件B的資料直接放在物件A當中,但要展開的資料可以從request URL裡面指定:
    http://api.example.com/a-resource?include=comments,related_posts
    這樣應該是最好用的,只是Server端就要做額外處理,像是Ch6提到的欄位過濾就要變成聽include去額外包進一些欄位。

    Ch8. Debugging
    這章節只是提一下如何用一些工具看到API回傳的值,像是curl、Postman、Fiddler之類的工具介紹。

    Ch9. Authorization
    作者推薦使用OAuth 2.0,不過建議找一個別人已經實作好驗證過的版本,這樣比較不會有問題。作者除了有使用OAuth 2.0預設的四種Grant Type,還有自己實作一個"social" Grant Type,用來連接其他第三方的登入,像是Facebook或Google。

    Ch10. Pagination
    列資料清單的時候通常都要有個方法只拿取區塊的資料,這時就要有資料分頁的設計了。這裡有兩種主要設計方法:

    Paginators:使用page跟per_page的資料指定每頁多少筆資料,然後是第幾頁。
    Server回應的資料可以長得像:
    {
    "data": [ ... ],
    "pagination": {
    "total": 1000,
    "count": 12,
    "per_page": 12,
    "current_page": 1,
    "total_pages": 84,
    "next_url": "/a_resources?page=2&per_page=12"
    }
    }
    total指這個物件總共有多少筆,count指目前data種有多少筆,per_page跟current_page就是client呼叫API時的per_page跟page,total_pages幫你算好應該要有多少頁資料,next_url給你下一頁的API連結。
    但這種方法會因為資料一直在增加減少,而導致抓下一頁資料的時候出現重複資訊,現實中這有點難避免,因為當資料順序都可以任意換的時候,應該是沒有一個簡單的解決方法可以處理這問題。

    Cursors:使用一個字串代表一個定位點,像是使用資料的ID當作cursor,使用這個cursor就代表要找這個ID之後(或之前,都沒有包含這個ID)的資料。或是直接用Offset當成cursor,找多少筆之後的資料,不過這就有點像Paginators,只是cursor=page*per_page而已。
    Server回應的資料可以長得像:
    {
    "data": [ ... ],
    "pagination": {
    "after": "MjAwMA==",
    "before": "MTk4MA==",
    "next_url": "/a_resources?after=MjAwMA%3d%3d&per_page=20"
    }
    }
    after跟before都是Server知道是什麼就可以了,而做這個隨機字串的原因並非安全性,而是讓client無法自己簡單的推算出其他的cursor。

    Ch11. Documentation
    寫文件重點基本上就是API Reference清單、Sample Code、Guides或Tutorial。有Sample Code可以讓讀文件的人快速上手API實際上怎麼呼叫,Guides可以快速學習API的使用方式。
    之後作者就推了幾個整理文件的工具,像是SwaggerAPI Blueprint

    Ch12. HATEOAS
    全名「Hypermedia as the Engine of Application State」,主要就是做到API就像是網頁一樣能夠從一個點瀏覽到其他地方,總之重點是這兩個:
    1. Content negotiation:這個下面API Versioning講到的很像,回傳文件的資料格式可以在request的時候,利用HTTP Accept header指定我想要哪種回傳格式(json, xml, yaml)。
    2. Hypermedia controls:簡單的說法就是為資料再加上相關資料的連結,像是文章裡可以加上文章留言的API網址。基本上要稱為100% RESTful的API就得做到這點才行。
    Server回應的資料可以長得像:
    {
    "data": {
    "id": 1,
    "title": "Foo",
    "content": "Bar",
    "links": {
    "rel": "post.comments",
    "uri": "/post/1/comments"
    }
    }
    }
    links就放跟這個文件相關的資料連結,rel代表這個資料的層級關係,只要自己訂出一套命名法則,讓每個物件的關係都能夠不至於混淆,就算是合格的設計。uri就代表要跟這個資源互動的API。而互動時可以用的HTTP Method,就使用HTTP的OPTIONS Method去跟API Server詢問,這樣就可以大致知道能夠對這個資源做的操作。

    Ch13. API Versioning
    這章節就沒有所謂的Best practices了,書中提到一些方法都各有優缺點,這裡有個網頁列出大家通常都用哪種方式做versioning的。
    1. URI加版本號
      http://api.example.com/v1/users
      好處是client超好使用,server也算好寫,只是當不同版本的API若要給不同的server去handle的話,可能就要用Apache或Nginx寫特別的條件才行。
      壞處是這一點也不RESTful(不過誰管他,大家都在用呢XD)
    2. 在Hostname家版本號
      http://api.v1.example.com/users
      優缺點就跟「URI加版本號」一樣,只是放版本號的地方不同
    3. 在Request Body或query放版本號
      http://api.example.com/users?version=1
      http://api.example.com/users(然後Request Body放 { "version": 1 })
      好處是API的網址都會是相同的,只要丟不同的version參數就好。
      壞處是當client傳送的資料不是JSON的時候,就比較難在Request Body放這個版本號
    4. 自訂版本號的Header
      http://api.example.com/users(然後加Header X-Api-Version: 1)
      好處跟「在Request Body或query放版本號」一樣
      壞處是Server要做cache的時候會比較麻煩,因為有可能會將version 1.1的資料回傳給version 1.0的request,這時就要加上vary: X-Api-Version的資料才能確保cache正確。
    5. Content Negotiation
      http://api.example.com/users(然後Accept設定application/vnd.service[.version]+json)
      這方法比較神秘,使用這種方法的人有Github,應該可以參考Github的文件來瞭解這種版本設定的方法。
      好處是HATEOAS-friendly, Cache-friendly
      壞處是client一開始可能不太會用這種方法
    6. Content Negotiation for Resources
      http://api.example.com/users(然後Accept設定application/vnd.service.user+json; version=1)
      很像「Content Negotiation」,但這種方法我真的不熟,感覺很難用的樣子XD
    7. Feature Flagging
      API版本號不在request之中,而是呼叫API時的token相對應的後台可以設定要用哪種版本的回傳資料。Facebook就使用這種方法,讓你有時間可以migrate,但是一定要在一定時間內migrate不然就永遠無法使用這支API了。
      好處是版本控制變簡單。
      壞處是client可能要依照新舊API版本多加判斷式,然後deploy之後在由後台切換API版本,這樣才能夠不中斷服務又能正常使用API。不過這樣的行為讓維護client程式變得有點複雜,甚至之後要手動移除判斷版本的程式,維護就變成兩個階段。

    書經過一個禮拜的努力終於看完了,只有180頁左右,大家應該也能夠快速KO掉這本書吧。整體來說是本很不錯的API設計入門書,但是更細節的部份可能就要有實際的經驗來補足了。


    Monday, July 6, 2015

    Stability patterns applied in a RESTful architecture | JavaWorld



    Stability patterns applied in a RESTful architecture | JavaWorld
    The 'Retry' pattern
    Apache HttpClient and other network clients implement some stability features out of the box. For instance, the client might execute retries internally under some circumstances. This strategy helps to handle transient network errors such as dropped connections or server failovers. 
    Retrying will not help in the case of permanent errors, however. In this case retrying wastes resource and time on both the client and server side.

    'Use Timeouts' pattern
    Essentially, the Sockets API defines two types of timeouts:

    The connection timeout denotes the maximum time elapsed before the connection is established or an error occurs.
    The socket timeout defines the maximum period of inactivity between two consecutive data packets arriving on the client side after a connection has been established.

    The current version of Jersey uses HttpURLConnection. By default Jersey sets a connection or socket timeout of 0 millis, meaning that the timeout is infinite.

    Connection pooling ensures that after performing an HTTP transaction the connection will be reused for further HTTP transactions, assuming the connection is identified as a persistent connection. This approach saves the overhead of establishing new TCP/IP connections, which is significant.

    Read full article from Stability patterns applied in a RESTful architecture | JavaWorld

    Wednesday, July 1, 2015

    Restful Web Service Design



    http://zalando.github.io/restful-api-guidelines/#_zalando_restful_api_and_event_scheme_guidelines
    https://devcenter.heroku.com/articles/platform-api-reference#ranges

    https://geemus.gitbooks.io/http-api-design/content/en/foundations/require-versioning-in-the-accepts-header.html
    To prevent surprise, breaking changes to users, it is best to require a version be specified with all requests. Default versions should be avoided as they are very difficult, at best, to change in the future.

    It is best to provide version specification in the headers, with other metadata, using the Accept header with a custom content type, e.g.:
    Accept: application/vnd.heroku+json; version=3

    https://geemus.gitbooks.io/http-api-design/content/en/requests/actions.html
    Prefer endpoint layouts that don’t need any special actions for individual resources. In cases where special actions are needed, place them under a standard actions prefix, to clearly delineate them:
    +

    /resources/:resource/actions/:action
    
    e.g.
    /runs/{run_id}/actions/stop

    Use downcased and dash-separated path names, for alignment with hostnames, e.g:
    +

    service-api.com/users
    service-api.com/app-setups
    
    Downcase attributes as well, but use underscore separators so that attribute names can be typed without quotes in JavaScript, e.g.:
    service_class: "first"

    Support non-id dereferencing for convenience

    In some cases it may be inconvenient for end-users to provide IDs to identify a resource. For example, a user may think in terms of a Heroku app name, but that app may be identified by a UUID. In these cases you may want to accept both an id or name, e.g.:
    $ curl https://service.com/apps/{app_id_or_name}
    $ curl https://service.com/apps/97addcf0-c182
    $ curl https://service.com/apps/www-prod
    
    Do not accept only names to the exclusion of IDs.

    Minimize path nesting

    In data models with nested parent/child resource relationships, paths may become deeply nested, e.g.:
    /orgs/{org_id}/apps/{app_id}/dynos/{dyno_id}
    
    Limit nesting depth by preferring to locate resources at the root path. Use nesting to indicate scoped collections. For example, for the case above where a dyno belongs to an app belongs to an org:
    /orgs/{org_id}
    /orgs/{org_id}/apps
    /apps/{app_id}
    /apps/{app_id}/dynos
    /dynos/{dyno_id}

    Provide the full resource representation (i.e. the object with all attributes) whenever possible in the response. Always provide the full resource on 200 and 201 responses, including PUT/PATCH and DELETE requests

    202 responses will not include the full resource representation

    Provide resource (UU)IDs

    Give each resource an id attribute by default. Use UUIDs unless you have a very good reason not to. Don’t use IDs that won’t be globally unique across instances of the service or other resources in the service, especially auto-incrementing IDs.
    Render UUIDs in downcased 8-4-4-4-12 forma

    Nest foreign key relations

    Serialize foreign key references with a nested object, e.g.:
    {
      "name": "service-production",
      "owner": {
        "id": "5d8201b0..."
      },
      // ...
    }
    
    Instead of e.g.:
    {
      "name": "service-production",
      "owner_id": "5d8201b0...",
      // ...
    }
    
    This approach makes it possible to inline more information about the related resource without having to change the structure of the response or introduce more top-level response fields

    Show rate limit status

    Rate limit requests from clients to protect the health of the service and maintain high service quality for other clients. You can use a token bucket algorithm to quantify request limits.
    Return the remaining number of request tokens with each request in the RateLimit-Remaining response header.

    Keep JSON minified in all responses

    Provide machine-readable JSON schema

    Provide a machine-readable schema to exactly specify your API. Use prmd to manage your schema, and ensure it validates with prmd verify

    In addition to endpoint details, provide an API overview with information about:
    • Authentication, including acquiring and using authentication tokens.
    • API stability and versioning, including how to select the desired API version.
    • Common request and response headers.
    • Error serialization format.
    • Examples of using the API with clients in different languages

    Provide executable examples

    If you use prmd to generate Markdown docs, you will get examples for each endpoint for free
    https://www.infoq.com/news/2014/08/heroku-http-design-guide
    The guide starts with a number of general recommendations:
    • Require all API calls to use TLS. Reply with 403 Forbidden to non-TLS ones
    • Always version an API. Use the Accept header to specify the version. Require clients to specify the API version instead of relying on a default one
    • Use the ETag header to support caching of resources
    • Identify each response with X-Request-ID; useful for logging or debugging purposes
    • Use Range, Content-Range, Next-Range to deal with large responses
    Return appropriate status codes,
    • Return complete resources when available
    • Accept serialized JSON in requests for symmetry with JSON responses
    • Use consistent path formats. Resource names should use the plural form unless they refer to singletons
    • Use lowercasing for paths and attributes to keep in line with host names
    • Allow some resources, such as application names, to be specified by their name besides UUID
    • Try to limit the depth of resource nesting by allocating them closer to the root path
    • Use globally unique IDs for each resource?
    • Provide standard timestamps for resources
    • For timestamps, use UTC in format ISO8601
    • Nest foreign key relations to be able to include more details for the related resource without changing the structure of the response
    • Provide detailed errors, including a machine readable ID, a human readable error message and an optional URL pointing to more details
    • Establish a request rate limit and inform the client on the number of available request tokens with each response, using a header like RateLimit-Remaining
    • JSON should be minified in all responses
    The guide includes more related advice:
    • Provide the JSON schema in a machine readable format
    • Include detailed APIs documentation for developers
    • Allow developers to test the APIs
    • APIs should be marked according to their stability status: prototype/development/production
    https://zhuanlan.zhihu.com/p/34289466
    两大原则:规范的(regular),可预测的(predictable)。
    1. 只使用名词
    2. 要有切入点
    3. 要合适的选择id形式
    4. 要表达资源间的联系
    5. 要支持查询
    6. 要支持返回部分资源
    7. 处理更复杂的计算逻辑
    要有切入点:原则上来讲,一个api应该有一个root path '/', 其返回一个url map,包括了所有的resouces所对应的url。这样客户端更容易去发现和使用api。

    要合适的选择id形式:例如api/dogs/{id} 中的{id}如何表达,是类似于‘/dogs/1’,还是‘/dogs/haha’? 一般来说取决于后端的数据库,大多数情况下,使用RDBMS,像mysql之类的主键自增功能,我比较倾向于使用自增主键的整数直接作为entity的id,避免很多问题,如果使用MongoDB的话,不妨试一试用字符串作为entity的id,可读性会提高,但是如何维护一个全局唯一的字符主键,你得三思。

    要表达资源间的联系:比如 GET /persons/1/dogs 返回所有属于person 1的狗。
    这种模式可以表述为:
    /{relationship-name}[/{resource-id}]/…/{relationship-name}[/{resource-id}]
    

    要支持查询

    GET /persons;1/dogs GET /persons;name=blabla/dogs 
    
    这种模式可以表述为:
    /{relationship-name}[;{selector}]/…/{relationship-name}[;{selector}]
    
    更复杂的查询条件:
    GET /dogs?color=red&state=running&location=park 
    
    注意这里的三个查询子条件之间的关系是‘与(and)’,如果要表达是‘或(or)’的逻辑,那就得设计更复杂的query解释机制。但其实实际使用中,表达‘或’的查询条件很少使用。
    要支持返回部分资源:
    /dogs?fields=name,color,location 
    
    返回的resource中,只包含name,color,location三种信息。
    处理更复杂的计算逻辑: 有很多例子,比如货币转换,大多数开发人员给出的方案是:
    /convert/100/EUR/CNY 或者 /convert?quantity=100&unit=EUR&in=CNY
    切记:URL是用来表述资源resource,而不是表述计算的过程。在URL中使用名词,避免动词。
    改进的方案1:
    GET /monetary-amount/100/EUR HTTP/1.1
    Host: Currency-Converter.com
    Accept-Currency:CNY //在http的header中添加Accept-Currency来指明货币的种类。
    
    改进的方案2:
    POST /currency-converter HTTP/1.1
    Host: Currency-Converter.com 
    Content-Length: 69
    {
    "amount": 100,
    "inputCurrency": "EUR",
    "outputCurrency": "CNY"
    }
    
    两个方案都可行,但是方案2有两个注意的地方:POST返回的结果可能无法再server端缓存;你是在构建一个计算的过程,而非资源的表述,如何理解?就像数据库操作中的‘store procedure’,你可以使用,并且功能强大,但是接口变得复杂,逻辑变得耦合。
    添加self link:self link提供了一个上下文环境,客户端可以更容易理解当前的resource的位置
    {
     "user": {
       "html_url": "octocat (The Octocat)",
       "type": "User",
       "url": "https://api.github.com/users/octocat"
     }
    }
    

    集合数据:

    方案1:集合collection也是一种resource,也具有self和kind属性,这样所有的单独entity和collection都具有更加统一的规范
    {
     "self": "https://dogtracker.com/dogs",
     "kind": "Collection",
     "contents": [
       {
        "self": "https://dogtracker.com/dogs/12344",
        "kind": "Dog",
        "name": "Fido",
        "furColor": "white"
       },
       {
        "self": "https://dogtracker.com/dogs/12345",
        "kind": "Dog",
        "name": "Rover",
        "furColor": "brown"
       }
      ]
    }
    
    方案2:看起来更加简洁,但是客户端可能需要去添加额外的逻辑去处理collection
    [
     {
      "self": "https://dogtracker.com/dogs/12344",
      "kind": "Dog",
       "name": "Fido",
       "furColor": "white"
      },
      {
       "self": "https://dogtracker.com/dogs/12345",
       "kind": "Dog",
       "name": "Rover",
       "furColor": "brown"
      }
    ]
    
    还有一种做法是,针对一个collection,使用自定义的media type header(比如‘Collection+JSON’)这个方法可行,但是会让客户端的处理逻辑复杂。
    集合数据:
    方案1:集合collection也是一种resource,也具有self和kind属性,这样所有的单独entity和collection都具有更加统一的规范
    {
     "self": "https://dogtracker.com/dogs",
     "kind": "Collection",
     "contents": [
       {
        "self": "https://dogtracker.com/dogs/12344",
        "kind": "Dog",
        "name": "Fido",
        "furColor": "white"
       },
       {
        "self": "https://dogtracker.com/dogs/12345",
        "kind": "Dog",
        "name": "Rover",
        "furColor": "brown"
       }
      ]
    }
    
    方案2:看起来更加简洁,但是客户端可能需要去添加额外的逻辑去处理collection
    [
     {
      "self": "https://dogtracker.com/dogs/12344",
      "kind": "Dog",
       "name": "Fido",
       "furColor": "white"
      },
      {
       "self": "https://dogtracker.com/dogs/12345",
       "kind": "Dog",
       "name": "Rover",
       "furColor": "brown"
      }
    ]
    
    还有一种做法是,针对一个collection,使用自定义的media type header(比如‘Collection+JSON’)这个方法可行,但是会让客户端的处理逻辑复杂。
    数据分页:当数据返回的集合变大时,显然不可能一次性把所有数据都返回给客户端,最好能分批的返回,比如:
    GET https://dogtracker.com/dogs?limit=25,offset=0 
    返回
    {
    "self": "https://dogtracker.com/dogs?limit=25,offset=0",
    "kind": "Page",
    "pageOf": "https://dogtracker.com/dogs",
    "next": "https://dogtracker.com/dogs?limit=25,offset=25",
    "contents": [...】
    }
    
    在返回的数据中,加入‘pageOf’来指明查询的起点,‘next’指明下一页的url,当返回第二页的时候,还需加入‘previous’来指明上一页。
    如果选择JSON作为唯一的数据格式,那么最好支持Http的patch方法,现在有两种patch的模式:JSON Patch和JSON Merge Patch,选择一个来用于资源的更新操作。现在也有很多API只提供PUT来更新资源,这意味着每次请求都必须发送整个resource enrity,势必会消耗更多的payload,但是实现起来更容易。
    1. 不(显式)支持多版本
    2. 使用Http Accept Header
    第一种,什么都不做,不支持多版本的api。这个想法的背后依据是,根据调研发现,大多数的中小型规模的平台服务,客户规模都在一个可控的范围,api的升级不会很频繁,你只需通知你的客户,在某个时间点api会更新,然后再server端做一些兼容性的数据迁移,比如增加或删除某个数据库中的表的某个列的名字。大多数情况下,支持多版本api费力不讨好,测试和部署的成本很大,收益却很小。你要做就是保持唯一个可用api服务的兼容性,而不是提供多个版本的api让用户使用。
    第二种,如果你一定要支持versioning,那么就在http的accept header中添加version信息,不要在url中使用version信息,千万不要用/api/v1/xxx。
    https://pages.apigee.com/rs/apigee/images/api-design-ebook-2012-03.pdf

    https://blog.philipphauer.de/restful-api-design-best-practices/

    POST (Create)
    GET (Read)PUT (Update)DELETE (Delete)
    /employeesCreates a new employeeLists all employeesBatch update employeesDelete all employees
    /employees/56(Error)Show details of the employee 56Update the employee 56Delete the employee 56
    Use the Query String (?) for Optional or Complex Parameter.
    GET /employees?state=internal&maturity=senior
    
    2xx: Success3xx: Redirect4xx: Client Error5xx: Server Error200 OK
    201 Created301 Moved Permanently
    304 Not Modified400 Bad Request
    401 Unauthorized
    403 Forbidden
    404 Not Found500 Internal Server Error
    http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api
    What about actions that don't fit into the world of CRUD operations?
    This is where things can get fuzzy. There are a number of approaches:
    1. Restructure the action to appear like a field of a resource. This works if the action doesn't take parameters. For example an activate action could be mapped to a boolean activatedfield and updated via a PATCH to the resource.
    2. Treat it like a sub-resource with RESTful principles. For example, GitHub's API lets you star a gist with PUT /gists/:id/star and unstar with DELETE /gists/:id/star.
    3. Sometimes you really have no way to map the action to a sensible RESTful structure. For example, a multi-resource search doesn't really make sense to be applied to a specific resource's endpoint. In this case, /search would make the most sense even though it isn't a resource. This is OK - just do what's right from the perspective of the API consumer and make sure it's documented clearly to avoid confusion.
    PUT to /resources/:id and send the data transition=void with the request.
    Pretty print by default & ensure gzip is supported
    • X-Rate-Limit-Limit - The number of allowed requests in the current period
    • X-Rate-Limit-Remaining - The number of remaining requests in the current period
    • X-Rate-Limit-Reset - The number of seconds left in the current period
    Auto loading related resource representations
    there would be a significant efficiency gain from allowing related data to be returned and loaded alongside the original resource on demand.
    In this case, embed would be a comma separated list of fields to be embedded. Dot-notation could be used to refer to sub-fields. For example:
    GET /tickets/12?embed=customer.name,assigned_user

    Of course, ability to implement something like this really depends on internal complexity. This kind of embedding can easily result in an N+1 select issue.



    - Short, Meaningful, Predictable, Readable urls
    - Nouns, not verbs.
    Lower case.
    Use hypens rather than spaces or underlines.
    Use a plural path for collections.
    Put individual resources under the plural collection path.
    Favor hackable urls over direct urls.

    Idempotent
    stateless
    versioned
    PATCH      → Patch - to update parts of a resource, update one or more fields as opposed to all fields.
    SLA
    Bankers Algorithm

    actions
    POST /transactions
    from=1&to=2&amount=500.00

    https://codeplanet.io/principles-good-restful-api-design/
    • Resource: A single instance of an object. For example, an animal.
    • Collection: A collection of homogeneous objects. For example, animals.
    • Endpoint: An API URL on a Server which represents either a Resource or an entire Collection.
    • Idempotent: Side-effect free, can happen multiple times without penalty.
    • URL Segment: A slash-separated piece of information in the URL.
    • ?limit=10: Reduce the number of results returned to the Consumer (for Pagination)
    • ?offset=10: Send sets of information to the Consumer (for Pagination)
    • ?animal_type_id=1: Filter records which match the following condition (WHERE animal_type_id = 1)
    • ?sortby=name&order=asc: Sort the results based on the specified attribute (ORDER BY name ASC)
    • GET /collection: Return a listing (array) of Resource objects
    • GET /collection/resource: Return an individual Resource object
    • POST /collection: Return the newly created Resource object
    • PUT /collection/resource: Return the complete Resource object
    • PATCH /collection/resource: Return the complete Resource object
    • DELETE /collection/resource: Return an empty document
    When designing your API, you should be able to work with tools which allow you to look at raw HTTP packets. Consider using Wireshark
    http://blog.2partsmagic.com/restful-uri-design/
    What are the criteria for a good REST-ful URI?
    Short (as possible). 
    Hackable ‘up the tree’. The user should be able to remove the leaf path and get an expected page back.
    Meaningful
    Predictable
    Help visualize the site structure. 
    Readable.
    Nouns, not verbs.
    Query args (everything after the ?) are used on querying/searching resources (exclusively). They contain data the affects the query.
    Consistent
    Return a representation (e.g. XML or json) based on the request headers, like Accept and Accept-Language rather than a change in the URI.
    Tied to a resource. Permanent.
    The URI will continue to work while the resource exists, and despite the resource potentially changing over time.
    Report canonical URIs. If you have two different URIs for the same resource, ensure you put the canonical URL in the response.
    Follows the digging-deeper-path-and-backspace convention. URI path can be used like a backspace.
    Uses name1=value1;name2=value2 (aka matrix parameters) when filtering collections of resources.

    Tips for creating good REST-ful URIs
    Lower case. 
    Use hypens rather than spaces or underlines. 
    Use a plural path for collections. 
    Put individual resources under the plural collection path.
    Favor hackable urls over direct urls.

    Avoid query args on an non-query/non-search resource.
    Avoid extensions

    Using Accept HTTP request headers to negotiate views.
    Using ‘vendor specific’ Accept headers
    e.g. text/vnd.redrata.summary+html; application/vnd.redrata.deep+json.

    Direct URLs vs. hackable URLs
    hackable/discoverable/end-user-editable url:
    /conversations/conversation-{id}/messages/message-{id}

    Use Query params for filtering.
    Ideally we should use Matrix params to return part of a resource.
    Use query params to model matrix use cases
    API implementation must be stateless
    API should reflect client use case, not represent implementation specifics
    Service API is versioned

    HTTP Verbs:
    POST   → Create – to create a new resource
    GET     → Read - to get a resource
    PUT     → Update - to replace all fields of previously created resource
    DELETE → Delete - to delete a resource, can be marked for deletion OR physical deletion?
    PATCH      → Patch - to update parts of a resource, update one or more fields as opposed to all fields.

    Subdomains to separate logical parts of the system
    APIs have proper governance
    APIs are backward compatible
    API must establish SLAs for CRUD operations
    Well documented.

    Services adhere to separation of data , business logic and integration points
    Every service must provide health check APIs that implement sanity functional health
    Services must be built with resiliency and security at the core
    If a dependent service is down or not performing to SLAs, the service should still be able to self heal and serve the clients.
    http://www.w3.org/Provider/Style/URI

    http://restful-api-design.readthedocs.org/en/latest/methods.html
    Asynchronous Requests
    In that case, a “202 Accepted” status can be returned to the client. 

    Action on Resource
    http://stackoverflow.com/questions/25287734/rest-actions-and-url-api-design-considerations
    http://blog.steveklabnik.com/posts/2011-07-03-nobody-understands-rest-or-http
    POST /accounts/1/transfer/500.00/to/2
    But this is wrong! What you really need to do is consider the nouns. You're not transferring money, you're creating a Transaction resource:

    POST /transactions HTTP/1.1
    Host: <snip, and all other headers>

    from=1&to=2&amount=500.00
    https://en.wikipedia.org/wiki/Create,_read,_update_and_delete
    http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
    OperationSQLHTTPDDS
    CreateINSERTPUT / POSTwrite
    Read (Retrieve)SELECTGETread / take
    Update (Modify)UPDATEPUT / PATCHwrite
    Delete (Destroy)DELETEDELETEdispose
    http://stackoverflow.com/questions/10711122/other-restful-actions-on-a-resource
    "Remove redeye", "crop", and "resize" all sound like actions which "change or update a resource." They would belong in a PUT action. (I think you mixed up PUT and POST in your question.
    For several modifying actions on the same resource (POST), I tend to use a custom header to define the kind of modification. In this case your resources

    /images/[id]/remove_redeye
    /images/[id]/crop
    /images/[id]/resize
    would translate to:

    POST /images/[id] HTTP/1.1
    X-RESTAction [remove_redeye|crop|resize]
    PUT /vm/123 
    { "state": "restarting" } 

    Restful API Example:

    HTTP Caching in Java with JAX-RS
    https://devcenter.heroku.com/articles/jax-rs-http-caching
    Time-based cache headers
            CacheControl cc = new CacheControl();
            cc.setMaxAge(86400);
            cc.setPrivate(true);
    
            ResponseBuilder builder = Response.ok(myBook);
            builder.cacheControl(cc)
    Conditional cache headers
    Conditional requests are those where the browser can ask the server if it has an updated copy of the resource. The browser will send one or both of the ETag andIf-Modified-Since headers about the cached resource it holds. The server can then determine whether updated content should be returned or the browser’s copy is the most recent.
            Book myBook = getBookFromDB(id);
            CacheControl cc = new CacheControl();
            cc.setMaxAge(86400);
    
            EntityTag etag = new EntityTag(Integer.toString(myBook.hashCode()));
            ResponseBuilder builder = request.evaluatePreconditions(etag);
    
            // cached resource did change -> serve updated content
            if(builder == null){
                    builder = Response.ok(myBook);
                    builder.tag(etag);
            }
    
            builder.cacheControl(cc);
            return builder.build();
    http://www.programcreek.com/java-api-examples/index.php?source_dir=JerseyTest-master/contribs/bill-burke-book/ex10_1/src/main/java/com/restfully/shop/services/CustomerResource.java
    public Response getCustomer(@PathParam("id") int id,
                                @HeaderParam("If-None-Match") String sent,
                                @Context Request request)
    {
       Customer cust = customerDB.get(id);
       if (cust == null)
       {
          throw new WebApplicationException(Response.Status.NOT_FOUND);
       }
    
       if (sent == null) System.out.println("No If-None-Match sent by client");
    
       EntityTag tag = new EntityTag(Integer.toString(cust.hashCode()));
    
       CacheControl cc = new CacheControl();
       cc.setMaxAge(5);
    
       Response.ResponseBuilder builder = request.evaluatePreconditions(tag);
       if (builder != null)
       {
          System.out.println("** revalidation on the server was successful");
          builder.cacheControl(cc);
          return builder.build();
       }
       // Preconditions not met!
    
       cust.setLastViewed(new Date().toString());
       builder = Response.ok(cust, "application/xml");
       builder.cacheControl(cc);
       builder.tag(tag);
       return builder.build();
    }
    http://stackoverflow.com/questions/20022683/why-does-evaluatepreconditions-dont-work
    @Test
    public void testETag() throws InterruptedException {
        WebTarget webTarget = target("rest").path("e_tag").queryParam("userId", "eric");
    
        Response head = webTarget.request().get();
        EntityTag eTag = head.getEntityTag();
        System.out.println(head.getStatus() + "\t" + eTag);
        Assert.assertEquals(200, head.getStatus());
        Thread.sleep(1000);
    
        Response head1 = webTarget.request().header("If-None-Match", eTag).get();
        System.out.println(head1.getStatus() + "\t" + head1.getEntityTag());
        Assert.assertEquals(304, head1.getStatus());
    }
    https://docs.oracle.com/javaee/6/api/javax/ws/rs/core/Request.html
    Method Summary
     Response.ResponseBuilderevaluatePreconditions()
              Evaluate request preconditions for a resource that does not currently exist.
     Response.ResponseBuilderevaluatePreconditions(java.util.Date lastModified)
              Evaluate request preconditions based on the passed in value.
     Response.ResponseBuilderevaluatePreconditions(java.util.Date lastModified, EntityTag eTag)
              Evaluate request preconditions based on the passed in value.
     Response.ResponseBuilderevaluatePreconditions(EntityTag eTag)
              Evaluate request preconditions based on the passed in value.
    Making a conditional PUT
    Resource updates can also be conditional. The client would receive 412 Precondition Failed if the ETag send by the client does not match the one on the server.
    http://www.javacodegeeks.com/2013/10/http-caching-using-jax-rs.html

    https://dzone.com/articles/restful-design-patterns
    HTTP PUT is used to modify the object, the request body contains the representation of the Object after successful modification.


    HTTP PUT is also used to create the object if the caller has complete control of assigning the object id, the request body contains the representation of the Object after successful creation.
    If the caller has no control in the object id, HTTP POST is made to the object's parent container with the request body contains the representation of the Object. The response body should contain a reference to the URL of the created object.

    Call a method of the Object

    HTTP POST is used to invoke a method of the object, the method is indicated in a mandated parameter "action". The arguments of the method can also be encoded in the URL (for primitive types) or in the request body (for complex types)
    POST /library/books/668102?action=buy&user=ricky

    Container Patterns

    The immediate parent of a container must be an object (can be a singleton object without an id or an object with an id). Container "contains" other objects or containers. If a container is destroyed, everything underneath will be destroyed automatically in a recursive manner.
    http://www.xyz.com/library/books
    http://www.xyz.com/library/dvds
    http://www.xyz.com/library/books/668102/chapters

    In GET operation, by default the container only return the URL reference of its immediate children. An optional parameter "expand" can be used to request the actual representation of all children and descendants.

    A more sophisticated GET operation can contain a "criteria" parameter to show only the children that fulfills certain criteria.
    GET http://www.xyz.com/library/book?query=(author='ricky')

    Reference Patterns
    Long running transactions
    Concurrent Updates (ETags)
    http://calvin1978.blogcn.com/articles/services.html
    4. 现在更喜欢称呼自家的东西是JSON内容的HTTP API,而不是Restful Service。由那些R虫上脑,坚信一切皆是资源,死抱着Restful的大腿不肯放的学院派来设计协议,简直是灾难。

    我遇到的死硬派:一个原子的操作,但认为涉及到两个"资源",生生拆成两个调用,万一第二个调用没收到我咋办呢,再额外做个超时回退的操作么。又如,通知接口明明可以把要通知的内容都送过去,被换成了一个资源的超链接...问题是,我发完通知就想删掉它呀,不会供着它让你哪天来访问...   2014-9-18

    后来发现,其实不止我一个这样想,比如Heroku的那份设计指南,也叫做HTTP API Design Guide,这份指南集合了学院派与实战派中好的那一部分。

    但后来,TW的技术雷达将Micro Services放到了谨慎的位置,毕竟分拆多了,运维的水平一定要跟上 2014-04-15

    http://blog.dataman-inc.com/115-shurenyun-rest
    面向用户的服务通常包含大量以同步方式通信的微服务,这意味着各微服务彼此依赖。这种处理方式在各服务皆能正常运转时并不会带来什么麻烦,然而一旦某项服务发生故障,即会引发不可阻挡的故障链并导致最终用户面临拒绝服务状况。
    同样的,如果某项微服务速度缓慢,则响应时间也会被同时拖累。
    使用REST等同步通信机制,Roper指出,“从技术角度讲,我们仍然相当于构建了一款整体式方案。无论各组件是否被拆分为独立服务,系统本身还是会像整体应用那样高度关联。”
    像Apache Kafka这样的软件能够自动处理异步服务中的低级细节,“如果使用简单的REST调用,大家的服务有可能瘫痪。而通过这种方式,情况就不会那么糟糕。”
    http://blog.jimmylv.info/2015-11-11-what-is-really-rest
    2008年10月Fielding写了一篇博 客,做出了一个非常明确的断言:REST APIs must be hypertext-driven!(REST API必须是超文本驱动的!)超文本驱动这个理念变成了一个缩写词HATEOAS,这个缩写词来自于当初Fielding博士论文中的一句话: hypermedia as the engine of application state(将超媒体作为应用状态的引擎)。其实超文本驱动(Hypertext Driven)的理念才是REST架构风格最核心的理念,也是REST风格的架构达到松耦合目标的根本原因。
    第一级:在架构中引入资源(Resource)的概念。
    第二级:每一个URI代表一种资源,支持HTTP动词。

    第三级:HATEOAS,使用超媒体(hypermedia)作为应用状态引擎。
    GET https://api.example.com/profile
    
    {
      "name": "Steve",
      "picture": {
        "large": "https://somecdn.com/pictures/1200x1200.png",
        "medium": "https://somecdn.com/pictures/100x100.png",
        "small": "https://somecdn.com/pictures/10x10.png"
      }
    }
    由于在响应中包含了链接地址,因此使用该API的客户端就能够自由选择要下载怎样的信息。这些链接告知了客户端有哪些选择,并且它们的地址在哪里。因此在这里我们无需同时返回三个不同版本的用户档案图片,我们所做的只是告诉客户端有三种可用的图片尺寸可以选择,并且告诉客户端能够在哪里找到这些图片。这样一来,客户端就能够根据不同的场景,做出符合自身需要的选择。而且,如果客户端只需要一种格式的图片,那就无需下载全部三种版本的图片了。这样一来可谓一箭三雕:既减少了网络负载,又增进了客户端的灵活性,更增进了API的可探索性。
    超媒体的核心概念就是所谓的<link>元素,而这些相互链接的资源实际上描述了一个协议,即引导我们达成某个目标的一系列步骤,例如订购一杯咖啡所需要的点单、付款、取咖啡等等。这就是超媒体的本质:经由资源之间的链接,我们改变整个应用的状态,即超媒体转换了分布式应用的状态。需要注意的是,服务器和消费者两者间交换的是资源状态的表述,而不是应用的状态,被转移的表述中包括了反应应用状态的链接。
    http://softwareengineering.stackexchange.com/questions/294445/rest-autocomplete-endpoint-design
    REST services are typically focused on resources, with the path separator (forward slash) denoting sub-resources in a hierarchy. Resources are typically things ("nouns" in English) rather than actions ("verbs" in English).
    To conform to this style, your "suggest" endpoint (a verb) could be named "suggestions" (a noun) because it provides access to suggestions. Just as an example, a sub-resource for suggestions might be individual suggestions with URLs like /suggest/Hilton%20New%20York where the response could be a link to the actual "Hilton New York" hotel resource.
    Actions like "suggest" are typically represented through HTTP verbs, e.g. GET /suggestions/.
    Your "term" is essentially a filter, which are typically passed through query parameters or in the request body. A more robust implementation is described in this SO answer.
    https://maps.googleapis.com/maps/api/place/autocomplete/output?parameters
    
    For example, you want to make a simple algorithmic calculation like how much tax someone should pay, or do a natural language translation (one language in request; another in response), or convert one currency to another. None involve resources returned from a database.
    In these cases:
    Use verbs not nouns
    For example, an API to convert 100 euros to Chinese Yen
    /convert?from=EUR&to=CNY&amount=100
    Make it clear in your API documentation that these are different.
    While a simple search could be modeled as a resourceful API (for example, dogs/?q=red), a more complex search across multiple resources requires a different design.
    This will sound familiar if you've read the aforementioned API design tip about using verbs not nouns when results don't return a resource from the database - rather the result is some action or calculation.

    If you want to do a global search across resources, I suggest you follow the Google model
    Global search
    /search?q=fluffy+fur
    Here, search is the verb; ?q represents the query.
    Scoped search
    To add scope to your search, you can prepend with the scope of the search. For example, search in dogs owned by resource ID 5678
    /owners/5678/dogs/search?q=fluffy+fur
    Formatted results
    For search or for any of the action oriented (non-resource) responses, you can prepend with the format as follows:
    /search.xml?q=fluffy+fur

    Say, you want a count of all of some resource (scoped based on whatever the requester has access to).
    /dogs/count
    In this way, you think about "count" as a reserved word.
    This means that you can never have "count" as an identifier on a resource - doing so would return that specific resource.  Otherwise, this would get a count of the resource (dogs) available in the database.
    https://groups.google.com/forum/#!forum/api-craft


    https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
    201 Created
    202 Accepted
    204 No Content
    301 Moved Permanently

    304 Not Modified
    If the client has performed a conditional GET request and access is allowed, but the document has not been modified, the server SHOULD respond with this status code. The 304 response MUST NOT contain a message-body, and thus is always terminated by the first empty line after the header fields.

    400 Bad Request

    The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications.

    401 Unauthorized

    The request requires user authentication. The response MUST include a WWW-Authenticate header field (section 14.47) containing a challenge applicable to the requested resource. The client MAY repeat the request with a suitable Authorization header field (section 14.8). If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials. If the 401 response contains the same challenge as the prior response

    403 Forbidden

    The server understood the request, but is refusing to fulfill it. Authorization will not help and the request SHOULD NOT be repeated. If the request method was not HEAD and the server wishes to make public why the request has not been fulfilled, it SHOULD describe the reason for the refusal in the entity. If the server does not wish to make this information available to the client, the status code 404 (Not Found) can be used instead.

    10.4.5 404 Not Found

    The server has not found anything matching the Request-URI. No indication is given of whether the condition is temporary or permanent. The 410 (Gone) status code SHOULD be used if the server knows, through some internally configurable mechanism, that an old resource is permanently unavailable and has no forwarding address. This status code is commonly used when the server does not wish to reveal exactly why the request has been refused, or when no other response is applicable.

    10.4.6 405 Method Not Allowed

    The method specified in the Request-Line is not allowed for the resource identified by the Request-URI. The response MUST include an Allow header containing a list of valid methods for the requested resource.

    10.4.9 408 Request Timeout

    The client did not produce a request within the time that the server was prepared to wait. The client MAY repeat the request without modifications at any later time.

    10.4.10 409 Conflict

    The request could not be completed due to a conflict with the current state of the resource. This code is only allowed in situations where it is expected that the user might be able to resolve the conflict and resubmit the request. The response body SHOULD include enough
    information for the user to recognize the source of the conflict. Ideally, the response entity would include enough information for the user or user agent to fix the problem; however, that might not be possible and is not required.
    Conflicts are most likely to occur in response to a PUT request. For example, if versioning were being used and the entity being PUT included changes to a resource which conflict with those made by an earlier (third-party) request, the server might use the 409 response to indicate that it can't complete the request. In this case, the response entity would likely contain a list of the differences between the two versions in a format defined by the response Content-Type.

    10.4.11 410 Gone

    The requested resource is no longer available at the server and no forwarding address is known. This condition is expected to be considered permanent. Clients with link editing capabilities SHOULD delete references to the Request-URI after user approval. If the server does not know, or has no facility to determine, whether or not the condition is permanent, the status code 404 (Not Found) SHOULD be used instead. This response is cacheable unless indicated otherwise.
    The 410 response is primarily intended to assist the task of web maintenance by notifying the recipient that the resource is intentionally unavailable and that the server owners desire that remote links to that resource be removed. Such an event is common for limited-time, promotional services and for resources belonging to individuals no longer working at the server's site. It is not necessary to mark all permanently unavailable resources as "gone" or to keep the mark for any length of time -- that is left to the discretion of the server owner.

    10.4.12 411 Length Required

    The server refuses to accept the request without a defined Content- Length. The client MAY repeat the request if it adds a valid Content-Length header field containing the length of the message-body in the request message.

    10.4.13 412 Precondition Failed

    The precondition given in one or more of the request-header fields evaluated to false when it was tested on the server. This response code allows the client to place preconditions on the current resource metainformation (header field data) and thus prevent the requested method from being applied to a resource other than the one intended.

    10.4.14 413 Request Entity Too Large

    The server is refusing to process a request because the request entity is larger than the server is willing or able to process. The server MAY close the connection to prevent the client from continuing the request.
    If the condition is temporary, the server SHOULD include a Retry- After header field to indicate that it is temporary and after what time the client MAY try again.

    10.4.15 414 Request-URI Too Long

    10.5.3 502 Bad Gateway

    The server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulfill the request.

    10.5.4 503 Service Unavailable

    The server is currently unable to handle the request due to a temporary overloading or maintenance of the server. The implication is that this is a temporary condition which will be alleviated after some delay. If known, the length of the delay MAY be indicated in a Retry-After header.

    10.5.5 504 Gateway Timeout

    The server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specified by the URI (e.g. HTTP, FTP, LDAP) or some other auxiliary server (e.g. DNS) it needed to access in attempting to complete the request.

    Labels

    Review (572) System Design (334) System Design - Review (198) Java (189) Coding (75) Interview-System Design (65) Interview (63) Book Notes (59) Coding - Review (59) to-do (45) Linux (43) Knowledge (39) Interview-Java (35) Knowledge - Review (32) Database (31) Design Patterns (31) Big Data (29) Product Architecture (28) MultiThread (27) Soft Skills (27) Concurrency (26) Cracking Code Interview (26) Miscs (25) Distributed (24) OOD Design (24) Google (23) Career (22) Interview - Review (21) Java - Code (21) Operating System (21) Interview Q&A (20) System Design - Practice (20) Tips (19) Algorithm (17) Company - Facebook (17) Security (17) How to Ace Interview (16) Brain Teaser (14) Linux - Shell (14) Redis (14) Testing (14) Tools (14) Code Quality (13) Search (13) Spark (13) Spring (13) Company - LinkedIn (12) How to (12) Interview-Database (12) Interview-Operating System (12) Solr (12) Architecture Principles (11) Resource (10) Amazon (9) Cache (9) Git (9) Interview - MultiThread (9) Scalability (9) Trouble Shooting (9) Web Dev (9) Architecture Model (8) Better Programmer (8) Cassandra (8) Company - Uber (8) Java67 (8) Math (8) OO Design principles (8) SOLID (8) Design (7) Interview Corner (7) JVM (7) Java Basics (7) Kafka (7) Mac (7) Machine Learning (7) NoSQL (7) C++ (6) Chrome (6) File System (6) Highscalability (6) How to Better (6) Network (6) Restful (6) CareerCup (5) Code Review (5) Hash (5) How to Interview (5) JDK Source Code (5) JavaScript (5) Leetcode (5) Must Known (5) Python (5)

    Popular Posts