## 响应(Responses)
### 返回合适的状态码
为每一次的响应返回合适的HTTP状态码。 好的响应应该使用如下的状态码:
* `200`: `GET`请求成功,及`DELETE`或`PATCH`同步请求完成,或者`PUT`同步更新一个已存在的资源
* `201`: `POST` 同步请求完成,或者`PUT`同步创建一个新的资源
* `202`: `POST`,`PUT`,`DELETE`,或`PATCH`请求接收,将被异步处理
* `206`: `GET` 请求成功,但是只返回一部分,参考:[上文中范围分页](https://github.com/ZhangBohan/http-api-design-ZH_CN#%E6%8C%89%E8%8C%83%E5%9B%B4%E5%88%86%E9%A1%B5)
使用身份认证(authentication)和授权(authorization)错误码时需要注意:
* `401 Unauthorized`: 用户未认证,请求失败
* `403 Forbidden`: 用户无权限访问该资源,请求失败
当用户请求错误时,提供合适的状态码可以提供额外的信息:
* `422 Unprocessable Entity`: 请求被服务器正确解析,但是包含无效字段
* `429 Too Many Requests`: 因为访问频繁,你已经被限制访问,稍后重试
* `500 Internal Server Error`: 服务器错误,确认状态并报告问题
对于用户错误和服务器错误情况状态码,参考: [HTTP response code spec](https://tools.ietf.org/html/rfc7231#section-6)
### 提供全部可用的资源
提供全部可显现的资源 (例如: 这个对象的所有属性) ,当响应码为200或是201时返回所有可用资源,包含 `PUT`/`PATCH` 和 `DELETE` 请求,例如:
~~~
$ curl -X DELETE \
https://service.com/apps/1f9b/domains/0fd4
HTTP/1.1 200 OK
Content-Type: application/json;charset=utf-8
...
{
"created_at": "2012-01-01T12:00:00Z",
"hostname": "subdomain.example.com",
"id": "01234567-89ab-cdef-0123-456789abcdef",
"updated_at": "2012-01-01T12:00:00Z"
}
~~~
当请求状态码为202时,不返回所有可用资源,例如:
~~~
$ curl -X DELETE \
https://service.com/apps/1f9b/dynos/05bd
HTTP/1.1 202 Accepted
Content-Type: application/json;charset=utf-8
...
{}
~~~
### 提供资源的(UU)ID
在默认情况给每一个资源一个`id`属性。除非有更好的理由,否则请使用UUID。不要使用那种在服务器上或是资源中不是全局唯一的标识,尤其是自动增长的id。
生成小写的UUID格式 `8-4-4-4-12`,例如:
~~~
"id": "01234567-89ab-cdef-0123-456789abcdef"
~~~
### 提供标准的时间戳
为资源提供默认的创建时间 `created_at` 和更新时间 `updated_at`,例如:
~~~
{
...
"created_at": "2012-01-01T12:00:00Z",
"updated_at": "2012-01-01T13:00:00Z",
...
}
~~~
有些资源不需要使用时间戳那么就忽略这两个字段。
### 使用UTC(世界标准时间)时间,用ISO8601进行格式化
在接收和返回时都只使用UTC格式。ISO8601格式的数据,例如:
~~~
"finished_at": "2012-01-01T12:00:00Z"
~~~
### 嵌套外键关系
使用嵌套对象序列化外键关联,例如:
~~~
{
"name": "service-production",
"owner": {
"id": "5d8201b0..."
},
// ...
}
~~~
而不是像这样:
~~~
{
"name": "service-production",
"owner_id": "5d8201b0...",
...
}
~~~
这种方式尽可能的把相关联的资源信息内联在一起,而不用改变资源的结构,或者引入更多的字段,例如:
~~~
{
"name": "service-production",
"owner": {
"id": "5d8201b0...",
"name": "Alice",
"email": "alice@heroku.com"
},
...
}
~~~
### 生成结构化的错误
响应错误的时,生成统一的、结构化的错误信息。包含一个机器可读的错误 `id`,一个人类能识别的错误信息(`message`),根据情况可以添加一个`url`来告诉客户端关于这个错误的更多信息以及如何去解决它,例如:
~~~
HTTP/1.1 429 Too Many Requests
~~~
~~~
{
"id": "rate_limit",
"message": "Account reached its API rate limit.",
"url": "https://docs.service.com/rate-limits"
}
~~~
文档化客户端可能遇到的错误信息格式,以及这些可能的错误信息`id`。
### 显示频率限制状态
客户端的访问速度限制可以维护服务器的良好状态,保证为其他客户端请求提供高性的服务。你可以使用[token bucket algorithm](http://en.wikipedia.org/wiki/Token_bucket)技术量化请求限制。
为每一个带有`RateLimit-Remaining`响应头的请求,返回预留的请求tokens。
### 保证响应JSON最小化
请求中多余的空格会增加响应大小,而且现在很多的HTTP客户端都会自己输出可读格式("prettify")的JSON。所以最好保证响应JSON最小化,例如:
~~~
{"beta":false,"email":"alice@heroku.com","id":"01234567-89ab-cdef-0123-456789abcdef","last_login":"2012-01-01T12:00:00Z","created_at":"2012-01-01T12:00:00Z","updated_at":"2012-01-01T12:00:00Z"}
~~~
而不是这样:
~~~
{
"beta": false,
"email": "alice@heroku.com",
"id": "01234567-89ab-cdef-0123-456789abcdef",
"last_login": "2012-01-01T12:00:00Z",
"created_at": "2012-01-01T12:00:00Z",
"updated_at": "2012-01-01T12:00:00Z"
}
~~~
你可以提供可选的方式为客户端提供更详细可读的响应,使用查询参数(例如:`?pretty=true`)或者通过`Accept`头信息参数(例如:`Accept: application/vnd.heroku+json; version=3; indent=4;`)。