RESTful设计规范
条评论RESTful设计规范
一、 摘要(Abstract)
RESTful API 已经非常成熟,也得到了大家的认可。我们按照 Richardson Maturity Model 对 REST 评价的模型,规范基于 level2 来设计
二、版本(Versioning)
API的版本号放入URL。例如:
1 | https://api.jiuyescm.com/v1/ |
三、资源、路径(Endpoint)
路径,API的具体地址。在REST中,每个地址都代表一个具体的资源(Resource
)约定如下:
路径仅表示资源的路径(位置),尽量不要有actions操作(一些特殊的
actions
操作除外)路径以 复数(名词) 进行命名资源,不管返回单个或者多个资源。
使用 小写字母、数字以及下划线(“_”) 。(下划线是为了区分多个单词,如user_name)
资源的路径从父到子依次如:
1
2/{resource}/{resource_id}/{sub_resource}/{sub_resource_id}/{sub_resource_property}
使用
?
来进行资源的过滤、搜索以及分页等使用版本号,且版本号在资源路径之前
优先使用内容协商来区分表述格式,而不是使用后缀来区分表述格式
应该放在一个专用的域名下,如:
http://api.jiuyescm.com
使用SSL
综上,一个API路径可能会是
1 | https://api.domain.com/v1/{resource}/{resource_id}/{sub_resource}/{sub_resource_id}/{sub_resource_property} |
四、操作(HTTP Actions)
用HTTP
动词(方法)表示对资源的具体操作。常用的HTTP
动词有:
1 | GET(SELECT):从服务器取出资源(一项或多项) |
下面是一些例子
1 | GET /users:列出所有用户 |
五、数据(Data Format)
数据是对资源的具体描述,分为请求数据和返回数据。约定如下:
- 查询,过滤条件使用query string,例如user?name=xxx
- Content body 仅仅用来传输数据
- 通过Content-Type指定请求与返回的数据格式。其中请求数据还要指定Accept。(我们暂时只使用Json)
- 数据应该拿来就能用,不应该还要进行转换操作
- 使用字符串(YYYY-MM-dd hh:mm:ss)格式表达时间字段,例如: 2017-02-20 16:00:00
- 数据采用UTF-8编码
- 返回的数据应该尽量简单,响应状态应该包含在响应头中
- 使用 小写字母、数字以及下划线(“_”) 描述字段,不使用大写描述字段(这个由于使用了一些开源的jar所以这个不强求,比如说pageinfo我们无法修改属性名称)
- 建议资源中的唯一标识命名为id(这个不强求,有的唯一标识名称确实比较复杂)
- 属性和字符串值必须使用双引号””(这个json转换默认规则)
- 建议对每个字段设置默认值(数组型可设置为[],字符串型可设置为””,数值可设置为0,对象可设置为{}),这一条是为了方便前端/客户端进行判断字段存不存在操作(这样json转换会自动转成相应的字符)
- POST操作应该返回新建的资源;PUT/PATCH操作返回更新后的完整的资源;DELETE返回一个空文档;GET返回资源数组或当个资源
- 为了方便以后的扩展兼容,如果返回的是数组,强烈建议用一个包含如items属性的对象进行包裹,如:
1 | {"items":[{},{}]} |
示例:
1 | POST https://api.domain.com/v1/users |
六、安全(Security)
调用限制
为了避免请求泛滥,给API设置速度限制很重要。入速度设置之后,可以在HTTP返回头上对返回的信息进行说明,下面是几个必须的返回头(依照twitter的命名规则)
1 | X-Rate-Limit-Limit :当前时间段允许的并发请求数 |
这个我们一般会在getway中实现
授权校验
RESTful API是无状态的也就是说用户请求的鉴权和cookie以及session无关,每一次请求都应该包含鉴权证明。 可以使用http请求头Authorization设置授权码; 必须使用User-Agent设置客户端信息, 无User-Agent请求头的请求应该被拒绝访问。具体的授权可以采用OAuth2,或者自己定义并实现相关的授权验证机制(基于token)。 这个我们一般会在getway中实现
错误
当API返回非2XX的HTTP响应时,应该采用统一的响应信息,格式如:
1 | HTTP/1.1 400 Bad Request |
- HTTP Header Code:符合HTTP响应的状态码。详细见以下的“状态码”节
- code:用来表示某类错误不是具体错误,比如缺少参数等。是对HTTP Header Code的补充,开发团队可以根据自己的需要自己定义
- message:错误信息的摘要,应该是对用户处理错误有用的信息
- request_id:请求的id,方便开发定位发生错误的请求(可选)
- code的定义约定:
- 采用 大写字母命名,字母与字母之间用下划线(”_”) 隔开
- code应该用来定义错误类别,而非定义具体的某个错误。
- 缺少参数使用:MISSING_X
- 无效参数使用:INVALID_X
- 逻辑验证错误使用:VALIDATION_X
- 不存在使用:NO_FOUND_X
七、状态码(Status Codes)
服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。
1 | 200 OK - [GET/PUT/PATCH/DELETE]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。 |
八、异常规范(Exceptions)
- Controller中try catch住service的异常,再转换为restful中需要抛出的异常
1 | try { |
- Controller中抛出的异常必须使用spring-mvc-rest包中的异常类,不允许自定义异常,选择需要返回的httpStatus对应的异常
1 | #403 [*]:表示得到授权(与401错误相对),但是访问是被禁止的。 |
- 抛出的异常中需要传入异常编码和异常信息,异常编码定义遵循上面 《安全中错误编码规范》
1 | "MESSING_ID", "缺少参数:id" |
- 常用的错误编码、异常、httpStatus对应关系
1 | "MESSING_ID", "缺少参数:id"、InvalidRequestException、400 |
九、示例(Example)
采用user提供的示例代码
POST /users
Resource POST /v1/users
POST Parameters Endpoint requires:
Name | Type | Description |
---|---|---|
name | String | 用户名称 |
address | String | 用户住址 |
and accepts a few other parameters listed below.
Name | Type | Description |
---|---|---|
remark | String | 描述信息 |
Example
1 | { |
Response Status-Code: 201 Created
1 | { |
Name | Type | Description |
---|---|---|
code | String | 错误编码 |
message | String | 错误描述 |
Items | Objec | t 返回结果 |
id | Long | 唯一标识 |
name | String | 用户名称 |
address | String | 家庭住址 |
remark | String | 描述信息 |
Error response Status-Code: 400 Bad Request
1 | { |
Name | Type | Description |
---|---|---|
code | String | 错误编码 |
message | String | 错误描述 |
Items | Object | 返回结果 |
HTTP Error Codes
HTTP Status | Code | Description |
---|---|---|
400 | MESSING_NAME | 缺少参数:name |
400 | MESSING_ADDRESS | 缺少参数:address |
422 | USER_NAME_EXIST | 用户名已存在 |
500 | INTERNAL_SERVER_ERROR | 未知的错误 |
DELETE /users/{user_id}
Resource DELETE /v1/users/{user_id}
Path Parameters
Name | Type | Description |
---|---|---|
user_id | Long | 用户唯一标识 |
Query Parameters None
Example Request
1 | curl –H ‘Content-Type: application/json’\ |
Response Status-Code: 204 No Content
HTTP Error Codes
HTTP Status | Code | Description |
---|---|---|
400 | MESSING_ID | 缺少参数:id |
404 | USER_NOT_FOUND | 用户不存在 |
PUT /users
Resource PUT /v1/users
PUT Body Parameters Endpoint requires:
Name | Type | Description |
---|---|---|
user_id | Long | 用户唯一标识 |
user_name | String | 用户名称 |
address | String | 用户住址 |
and accepts a few other parameters listed below..
Name | Type | Description |
---|---|---|
remark | String | 描述信息 |
Example
1 | { |
Response Status-Code: 200 OK
1 | { |
Name | Type | Description |
---|---|---|
code | String | 错误编码 |
message | String | 错误描述 |
Items | Object | 返回结果 |
id | Long | 唯一标识 |
name | String | 用户名称 |
address | String | 家庭住址 |
remark | String | 描述信息 |
Error response Status-Code: 400 Bad Request
1 | { |
Name | Type | Description |
---|---|---|
code | String | 错误编码 |
message | String | 错误描述 |
HTTP Error Codes
HTTP Status | Code | Description |
---|---|---|
code | String | 错误编码 |
400 | MESSING_ID | 缺少参数:id |
400 | MESSING_NAME | 缺少参数:name |
400 | MESSING_ADDRESS | 缺少参数:address |
422 | USER_NAME_EXIST | 用户名已存在 |
500 | INTERNAL_SERVER_ERROR | 未知的错误 |
GET /users/{user_id}
Resource GET /v1/users/{user_id}
Path Parameters
Name | Type | Description |
---|---|---|
user_id | Long | 用户唯一标识 |
Example Request
1 | Curl –H 'Content-Type: application/json' \ |
Response Status-Code: 200 OK
1 | { |
Name | Type | Description |
---|---|---|
code | String | 错误编码 |
message | String | 错误描述 |
Items | Object | 返回结果 |
id | String | 唯一标识 |
name | String | 用户名称 |
address | String | 家庭住址 |
remark | String | 描述信息 |
Error response Status-Code: 404 Bad Request
1 | { |
Name | Type | Description |
---|---|---|
code | String | 错误编码 |
message | String | 错误描述 |
HTTP Error Codes
HTTP Status | Code | Description |
---|---|---|
400 | MESSING_ID | 缺少参数:id |
404 | USER_NOT_FOUND | 用户不存在 |
GET /users
Resource GET /v1/users
Query Parameters
Name | Type | Description |
---|---|---|
name | String | 根据用户名称进行查询 |
page | int | 第几页,不传入默认1 |
page_size | int | 每页返回多少条结果,不传入默认20 |
Example Request
1 | Curl –H 'Content-Type: application/json' \ |
Response Status-Code: 200 Success
1 | { |
Name | Type | Description |
---|---|---|
code | String | 错误编码 |
message | String | 错误描述 |
Items | Object | 返回结果 |
id | Long | 唯一标识 |
name | String | 用户名称 |
address | String | 家庭住址 |
remark | String | 描述信息 |