前言

由于API一旦发布,很难在进行改编。但是在api设计的时候,听从几点建议,将会使你的api适应性特别强。免费转载请注明引用自Bing’s Blog
这篇文章主要参考了

下面是总结的几个要点

API的主要要求

  • 对于现在的一些模糊标准,感觉不好,不要尝试去满足它。
  • 应该对开发者友好, 应该可以通过浏览器进行探索访问
  • 应该是简洁,一致的,很容易适配拓展

Use RESTful URLs and actions

  • RESTful的一个概念就是获得很好的适应性。一个关键概念就是把你的API都能分割成逻辑资源,这些资源通过http的方法进行操作。什么可以变成资源呢,从开发者角度,资源应该是名词而不是动词。虽然你可以把内部的model映射成资源,但是没有必要一一对应,不要给你的API去暴露无关的细节。比如 ticket, user and group

  • 一旦你的资源定义了,你需要定义动作来对它们操作和如何影射到你的API上。RESTful的原则是提供处理CRUD的action的策略。例如:

GET /tickets - Retrieves a list of tickets
GET /tickets/12 - Retrieves a specific ticket
POST /tickets - Creates a new ticket
PUT /tickets/12 - Updates ticket #12
PATCH /tickets/12 - Partially updates ticket #12
DELETE /tickets/12 - Deletes ticket #12
  • 资源名称应该复数。比如 /tickets /tickets/12
  • 如何处理资源关系。如果关系仅仅是存在另外一个资源中,可以这样。
GET /tickets/12/messages - Retrieves list of messages for ticket #12
GET /tickets/12/messages/5 - Retrieves message #5 for ticket #12
POST /tickets/12/messages - Creates a new message in ticket #12
PUT /tickets/12/messages/5 - Updates message #5 for ticket #12
PATCH /tickets/12/messages/5 - Partially updates message #5 for ticket #12
DELETE /tickets/12/messages/5 - Deletes message #5 for ticket #12
  • 如果actions不能满足当前CRUD操作,有几条建议
    • 重构action,使它能够像一个资源的field。
    • 把它看成一个子资源。例如PUT
      PUT /gists/:id/star  
      DELETE /gists/:id/star
      
    • 有时实在没法进行影射,可以重新设置一个特殊的资源。
  • SSL 任何地方,任何时候都需要。Always use SSL. No exceptions.
  • API文档的文档应该清晰。
  • 版本控制。其实关于放在URL还是header中呢,URL中比较好。
  • 资源的过滤 排列和搜索
    • Filtering 过滤。对每个fieldyo对应唯一的查询参数。例如 GET /tickets?state=open
     GET /api/v1/users?fields=id,first_name
    

    响应:

	
[
  {
    "id": "543abc",
    "first_name:": "John"
  },
  {
    "id": "543add",
    "first_name:": "Bob"
  }
]
- Sorting. 类似Fliter,sort用来描述排列规则。复杂的排列规则是一个逗号分隔的列表。负号表示下降排列。
 	GET /tickets?sort=-priority - Retrieves a list of tickets in descending order of priority
 	GET /tickets?sort=-priority,created_at - Retrieves a list of tickets in descending order of priority. Within a specific priority, older tickets are ordered first 	
  • Searching 搜索。如果基本的过滤不能满足的时候,需要进行全文搜索了。添加key来搜索吧
	GET /tickets?sort=-updated_at - Retrieve recently updated tickets
	GET /tickets?state=closed&sort=-updated_at - Retrieve recently closed tickets
	GET /tickets?q=return&state=open&sort=-priority,created_at - Retrieve the highest priority open tickets mentioning the word 'return'
  • 通过API限制啊返回的field。使用带逗号的fields来查询。
 GET /tickets?fields=id,subject,customer_name,updated_at&state=open&sort=-updated_at
  • 更新和创建需要返回资源表现(resource representation)
    在post的结果相应中,用201 状态码和包含Location的头部来指向新资源的URL
 {
  "id": "5338df",
  "name": "MyFile.zip",
  "type": "application/zip",
  "size": 197
}
  • JSON only responses
  • field名称用蛇形命名而不是骆驼命名法。蛇形命名方法更容易阅读。
  • 默认完美打印和gzip支持。
    返回提供空格会让打印出来的结果容易阅读。可以提供一个查询参数比如?pretty=true来开启完美打印功能。这种数据的额外损耗是非常小的,特别当支持压缩的时候。可以看下损耗的数据。
$ curl https://api.github.com/users/veesahni > with-whitespace.txt
$ ruby -r json -e 'puts JSON JSON.parse(STDIN.read)' < with-whitespace.txt > without-whitespace.txt
$ gzip -c with-whitespace.txt > with-whitespace.txt.gz
$ gzip -c without-whitespace.txt > without-whitespace.txt.gz

输出数据的大小比较:

without-whitespace.txt - 1252 bytes
with-whitespace.txt - 1369 bytes
without-whitespace.txt.gz - 496 bytes
with-whitespace.txt.gz - 509 bytes
  • 数据不要使用默认分装,除非需要。
  • JSON encoded POST, PUT & PATCH bodies
    如果你遵守上面的建议的话,你会把所有的输出包装成Json格式。那么API的输入呢。
    很多API都在她们请求body中使用URL编码。URL encoding确切看起来请求的body是Key value对,对于简单的查询,这些已经满足工作了。但是URL enconding还是有一些问题的,因为它没有数据类型的概念,它会强制分析整型和布尔型等。更重要的是,它没有继承结构的概念。简单的API,UR encoding是有效的,复杂的API将会坚持JSON作为输入。

    一个接受Json encoded的POST PUT PATCH请求的API应该需要 Content-Type头部去设置application/json,或者跑出415不支持的状态码

  • Pagination 分页。
Link: <https://api.github.com/user/repos?page=3&per_page=100>; rel="next", <https://api.github.com/user/repos?page=50&per_page=100>; rel="last"

An API that requires sending a count can use a custom HTTP header like X-Total-Count.

 GET /api/v1/tickets?count=true

响应:

200 OK
Total-Count: 135
Rate-Limit-Limit: 100
Rate-Limit-Remaining: 98
Rate-Limit-Used: 2
Rate-Limit-Reset: 20
Content-Type: application/json
  • 自动加载相关资源表现(Auto loading related resource representations)
    很多情况下需要加载请求中的相关数据。对开发者来说,不用重复去请求相关的数据,使用一个参数就可以满足这种情况。比如 embed或者expand .下面例子中将会展示相关的单独的field
GET /tickets/12?embed=customer.name,assigned_user

输入可能像这样:

{
  "id" : 12,
  "subject" : "I have a question!",
  "summary" : "Hi, ....",
  "customer" : {
    "name" : "Bob"
  },
  assigned_user: {
   "id" : 42,
   "name" : "Jim",
  }
}
  • 重载HTTP方法

    一些客户端智能支持GET POST请求。可以在头部X-HTTP-Method-Override中用一个字符串来包含PUT, PATCH or DELETE

	
$ curl -u email:password https://site.enchant.com/api/v1/users/543abc \
    -X POST \
    -H "X-HTTP-Method-Override: DELETE"	
  • Rate Limiting 访问的频率限制

    At a minimum, include the following headers (using Twitter’s naming conventions as headers typically don’t have mid-word capitalization):

    • 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
  • Authentication 认证相关

    一个RESTful API 应该无状态的。这也就意味着请求认证不能依赖于cookies或者sesion,每一个请求都应该有相关的授权证书。通常使用SSL,认证证书将会通过一个继承于HTTP Basic Auth的field的随机随机产生的access token。完全可以通过浏览器进行探索检验。如果接受到401无认证的状态码,浏览球会弹出查询证书的提示。
    OAuth 2使用Bearer tokens也依赖于SSL给他的底层传输加密。
    支持JSONP的API需要一个第三个验证,因为JSONP不能发功基本的Auth认证或者Bearer tokens,这个时候需要使用一个access_token。注意,这有一个潜在安全问题,查询的时候这个token。

  • HTTP status codes

200 OK - Response to a successful GET, PUT, PATCH or DELETE. Can also be used for a POST that doesn't result in a creation.
201 Created - Response to a POST that results in a creation. Should be combined with a Location header pointing to the location of the new resource
204 No Content - Response to a successful request that won't be returning a body (like a DELETE request)
304 Not Modified - Used when HTTP caching headers are in play
400 Bad Request - The request is malformed, such as if the body does not parse
401 Unauthorized - When no or invalid authentication details are provided. Also useful to trigger an auth popup if the API is used from a browser
403 Forbidden - When authentication succeeded but authenticated user doesn't have access to the resource
404 Not Found - When a non-existent resource is requested
405 Method Not Allowed - When an HTTP method is being requested that isn't allowed for the authenticated user
410 Gone - Indicates that the resource at this end point is no longer available. Useful as a blanket response for old API versions
415 Unsupported Media Type - If incorrect content type was provided as part of the request
422 Unprocessable Entity - Used for validation errors
429 Too Many Requests - When a request is rejected due to rate limiting
500, 501, 502, 503, etc - An internal server error occured