Better RESTful API实践
Github地址: https://github.com/DavadDi/micro_services_arch/blob/master/Better_RESTful_API.md
1. 资源采用名词,采用复数形式
Resource | GET read | POST create | PUT update | PATCH Partially update | DELETE |
---|---|---|---|---|---|
/cars | Returns a list of cars | Create a new car | Bulk update of cars | Bulk Partially update of cars | Delete all cars |
/cars/711 | Returns a specific car | Method not allowed (405) | Updates a specific car [200/204, or 404] | Partially Updates a specific car | Deletes a specific car |
200 or 404 | 201[ Location’ header or 404 | 200/204 or 404 | 200 or 404 |
尽量采用复数格式,不是绝对,如果对于全局唯一的配置,则可以采用单数格式。
- Twitter: https://developer.twitter.com/en/docs/api-reference-index
- Facebook: https://developers.facebook.com/docs/graph-api
- LinkedIn: https://developer.linkedin.com/
- GitHub API: https://developer.github.com/v3/
2. 采用子资源关联
GET /cars/711/drivers/ Returns a list of drivers for car 711
GET /cars/711/drivers/4 Returns driver #4 for car 711
Action | URI | DESC |
---|---|---|
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 |
是否需要采用多层次的关联,则需要根据子资源能否脱离父类资源独立存在。
如果对于某些资源的 Action 不能够对应到 CRUD 操作,
- 比如对于资源的 activated,操作则可以映射成 PATCH 操作
- 比如对于GitHub上的gist点赞,则可以映射成资源的子属性
PUT /gists/:id/star
DELETE /gists/:id/star
- 如果对于多个资源的 search,不能够映射到各个资源上
GET /search
3. 采用 HATEOAS
HATEOAS = ypermedia as the Engine of Application State
{
"id": 711,
"manufacturer": "bmw",
"model": "X5",
"seats": 5,
"drivers": [
{
# "id": "23", no need
"name": "Stefan Jauker",
"links": [
{
"rel": "self",
"href": "/api/v1/drivers/23"
}
]
}
]
}
4. 提供过滤、排序、字段选择和分页
过滤
GET /cars?color=red Returns a list of red cars
GET /cars?seats<=2 Returns a list of cars with a maximum of 2 seats
gt – Greater than
lt – Less than
eq – Equal to
ge – Greater than or equal to
le – Less than or equal to
排序
GET /cars?sort=-manufactorer,+model
字段选择
GET /cars?fields=manufacturer,model,id,color
分页
GET /cars?offset=10&limit=5
# github存放在 http HEADer中,参见:https://developer.github.com/v3/#pagination
Link: <https://xxx/api/v1/cars?offset=15&limit=5>; rel="next",
<https://xxx/api/v1/cars?offset=50&limit=3>; rel="last",
<https://xxx/api/v1/cars?offset=0&limit=5>; rel="first",
<https://xxx/api/v1/cars?offset=5&limit=5>; rel="prev",
Google API开发中对于分页的处理方式如下https://cloud.google.com/apis/design/design_patterns:
对于所有的List操作都应该支持分页,即使当前的结果集数量非常小,因为对于API添加分页功能的支持也是一个 behavior-breaking的修改。在List操作中增加如下定义字段:
- page_token (string) 字段出现在 List 方法的请求消息中,表明客户端请求的具体页数的数据的结果,该字段的内容必须是url-safe的base64编码,如果包含敏感数据则应该进行加密,服务端必须保证被篡改page_token不能够访问到不是预期暴露的数据:
- require query parameters to be respecifed on follow up requests
- only reference server-side session state in the page token
- encrypt and sign the query parameters in the page token and revalidate and reauthorize these parameters on every call
- total_size (int32)
- page_size (int32),如果 page_size == 0, 则由服务端决定返回的数目,当然服务端也可能有自己的限制条件,比如比 page_size 更小;
- next_page_token (string) 字段出现在 List 方法的 Response Message中,代表下一页数据的 Token,如果该字段为空 “”, 则表明没有后续的数据;
rpc ListBooks(ListBooksRequest) returns (ListBooksResponse);
message ListBooksRequest {
string name = 1;
int32 page_size = 2;
string page_token = 3;
}
message ListBooksResponse {
repeated Book books = 1;
string next_page_token = 2;
}
5. API 版本号
版本号放在url中,方便与日志收集后的过滤,同时版本号用整数, v1、v2, 不采用 v2.5的方式
/blog/api/v1
6. 创建和更新操作返回资源主题信息
如果是采用Post Create参数应该返回创建后的资源信息,HTTP Status Code == 201, 并在 Header 中填写 “ Location = “Location” “:” absoluteURI” 表明创建资源后的URL地址;
如果是采用PUT操作资源,则应该返回更新后的资源字段,或者附件 created_at 或 updated_at等字段信息;
7. 采用HTTP Status Code处理错误
code | Status | Message |
---|---|---|
200 | OK | Eyerything is working |
201 | OK | New resource has been created |
204 | OK | The resource was successfully deleted |
304 | Not Modified | The client can use cached data |
400 | Bad Request | The request was invalid or cannot be served. The exact error should be explained in the error payload. E.g. „The JSON is not valid“ |
401 | Unauthorized | The request requires an user authentication |
403 | Forbidden | The server understood the request, but is refusing it or the access is not allowed. |
404 | Not found | There is no resource behind the URI. |
409 | CONFLICT | Whenever a resource conflict would be caused by fulfilling the request. Duplicate entries, deleting root objects when cascade-delete not supported are a couple of examples. |
422 | Unprocessable Entity | Should be used if the server cannot process the enitity, e.g. if an image cannot be formatted or mandatory fields are missing in the payload. |
429 | Too Many Requests | Too many requests hit the API too quickly. We recommend an exponential backoff of your requests. |
500 | Internal Server Error | API developers should avoid this error. If an error occurs in the global catch blog, the stracktrace should be logged and not returned as response. |
使用Error Payload
{
"errors": [
{
"userMessage": "Sorry, the requested resource does not exist",
"internalMessage": "No car found in the database",
"code": 34,
"more info": "http://dev.mwaysolutions.com/blog/api/v1/errors/12345"
}
]
}
or
{
"code" : 1234,
"message" : "Something bad happened :(",
"description" : "More details about the error here"
}
or
{
"code" : 1024,
"message" : "Validation Failed",
"errors" : [
{
"code" : 5432,
"field" : "first_name",
"message" : "First name cannot have fancy characters"
},
{
"code" : 5622,
"field" : "password",
"message" : "Password cannot be blank"
}
]
}
or
{
"code":200,
"status":"success",
"data":
{
"lacksTOS":false,
"invalidCredentials":false,
"authToken":"4ee683baa2a3332c3c86026d"
}
}
{
"code":401,
"status":"error",
"message":"token is invalid",
"data":"UnauthorizedException"
}
9. 输入和输出尽可能JSON格式
XML API vs JSON API,目前JSON API的调用已经大幅度领先于XML;尽可能返回JSON格式的数据,并保证返回的JSON格式数据具备良好的阅读性,例如默认设置参数(?pretty=true),方便与用户展示和调试;返回的JSON格式内容启用压缩功能;JSON格式推荐采用下划线分割page_size,而不是大小写格式pageSize;
10. API 访问限速
RFC 6585 中 429 Too Many Requests 定义了该种情况。
- 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
##参考资料
- 10 Best Practices for Better RESTful API
- ReST APIs | Best Practices & Security
- Best Practices for Designing a Pragmatic RESTful API
- Node.js Restful API tutorial
- Google Cloud Plateform
- Google Seller Rest Filter
- API version should be included in the URL or in a header
- GitHub API
- Striper
- XML API vs JSON API
- Architectural Styles andthe Design of Network-based Software Architectures
- RESTful Service Best Practices
Pingback引用通告: 分页机制 | 程序印象