该仓库整理了目前比较流行的 RESTful api
设计规范,为了方便讨论规范带来的问题及争议,现把该文档托管于 Github,欢迎你们补充!!php
Versioninghtml
Response前端
为了不歧义,文档大量使用了「能愿动词」,对应的解释以下:nginx
必须 (MUST)
:绝对,严格遵循,请照作,无条件遵照;必定不可 (MUST NOT)
:禁令,严令禁止;应该 (SHOULD)
:强烈建议这样作,可是不强求;不应 (SHOULD NOT)
:强烈不建议这样作,可是不强求;能够 (MAY)
和 可选 (OPTIONAL)
:选择性高一点,在这个文档内,此词语使用较少;参见: RFC 2119
客户端在经过 API
与后端服务通讯的过程当中,应该
使用 HTTPS
协议。git
API
的根入口点应尽量保持足够简单,这里有两个常见的 URL
根例子:github
若是你的应用很庞大或者你预计它将会变的很庞大,那应该
将API
放到子域下(api.example.com
)。这种作法能够保持某些规模化上的灵活性。
全部的 API
必须保持向后兼容,你 必须
在引入新版本 API
的同时确保旧版本 API
仍然可用。因此 应该
为其提供版本支持。json
目前比较常见的两种版本号形式:后端
api.example.com/v1/*
这种作法是版本号直观、易于调试;另外一种作法是,将版本号放在 HTTP Header
头中:api
Accept: application/vnd.example.com.v1+json
其中 vnd
表示 Standards Tree
标准树类型,有三个不一样的树: x
,prs
和 vnd
。你使用的标准树须要取决于你开发的项目bash
x
)主要表示本地和私有环境prs
)主要表示没有商业发布的项目vnd
)主要表示公开发布的项目后面几个参数依次为应用名称(通常为应用域名)、版本号、指望的返回格式。
至于具体把版本号放在什么地方,这个问题一直存在很大的争议,但因为咱们大多数时间都在使用 Laravel
开发,应该
使用 dingo/api 来快速构建应用,它采用第二种方式来管理 API
版本,而且已集成了标准的 HTTP Response
。
端点就是指向特定资源或资源集合的 URL
。在端点的设计中,你 必须
遵照下列约定:
必须
所有小写resource
)的命名 必须
是名词,而且 必须
是复数形式必须
优先使用 Restful
类型的 URL必须
是易读的必定不可
暴露服务器架构至于 URL 是否必须使用连字符(-
) 或下划线(_
),不作硬性规定,但必须
根据团队状况统一一种风格。
来看一个反例
再来看一个正列
对于资源的具体操做类型,由 HTTP
动词表示。经常使用的 HTTP
动词有下面五个(括号里是对应的 SQL
命令)。
其中
1 删除资源 必须
用 DELETE
方法
2 建立新的资源 必须
使用 POST
方法
3 更新资源 应该
使用 PUT
方法
4 获取资源信息 必须
使用 GET
方法
针对每个端点来讲,下面列出全部可行的 HTTP
动词和端点的组合
请求方法 | URL | 描述 |
---|---|---|
GET | /zoos | 列出全部的动物园(ID和名称,不要太详细) |
POST | /zoos | 新增一个新的动物园 |
GET | /zoos/{zoo} | 获取指定动物园详情 |
PUT | /zoos/{zoo} | 更新指定动物园(整个对象) |
PATCH | /zoos/{zoo} | 更新动物园(部分对象) |
DELETE | /zoos/{zoo} | 删除指定动物园 |
GET | /zoos/{zoo}/animals | 检索指定动物园下的动物列表(ID和名称,不要太详细) |
GET | /animals | 列出全部动物(ID和名称)。 |
POST | /animals | 新增新的动物 |
GET | /animals/{animal} | 获取指定的动物详情 |
PUT | /animals/{animal} | 更新指定的动物(整个对象) |
PATCH | /animals/{animal} | 更新指定的动物(部分对象) |
GET | /animal_types | 获取全部动物类型(ID和名称,不要太详细) |
GET | /animal_types/{type} | 获取指定的动物类型详情 |
GET | /employees | 检索整个雇员列表 |
GET | /employees/{employee} | 检索指定特定的员工 |
GET | /zoos/{zoo}/employees | 检索在这个动物园工做的雇员的名单(身份证和姓名) |
POST | /employees | 新增指定新员工 |
POST | /zoos/{zoo}/employees | 在特定的动物园雇佣一名员工 |
DELETE | /zoos/{zoo}/employees/{employee} | 从某个动物园解雇一名员工 |
超出Restful
端点的,应该
模仿上表的方式来定义端点。
若是记录数量不少,服务器不可能都将它们返回给用户。API
应该
提供参数,过滤返回结果。下面是一些常见的参数。
全部 URL
参数 必须
是全小写,必须
使用下划线类型的参数形式。
分页参数必须
固定为page
、per_page
常用的、复杂的查询 应该
标签化,下降维护成本。如
GET /trades?status=closed&sort=sortby=name&order=asc # 可为其定制快捷方式 GET /trades/recently_closed
应该
使用 OAuth2.0
的方式为 API 调用者提供登陆认证。必须
先经过登陆接口获取 Access Token
后再经过该 token
调用须要身份认证的 API
。
Oauth 的端点设计示列
客户端在得到 access token
的同时 必须
在响应中包含一个名为 expires_in
的数据,它表示当前得到的 token
会在多少 秒
后失效。
{ "access_token": "token....", "token_type": "Bearer", "expires_in": 3600 }
客户端在请求须要认证的 API
时,必须
在请求头 Authorization
中带上 access_token
。
Authorization: Bearer token...
当超过指定的秒数后,access token
就会过时,再次用过时/或无效的 token
访问时,服务端 应该
返回 invalid_token
的错误或 401
错误码。
HTTP/1.1 401 Unauthorized Content-Type: application/json Cache-Control: no-store Pragma: no-cache { "error": "invalid_token" }
Laravel 开发中,应该
使用 JWT 来为管理你的 Token,而且必定不可
在api
中间件中开启请求session
。
全部的 API
响应,必须
遵照 HTTP
设计规范,必须
选择合适的 HTTP
状态码。必定不可
全部接口都返回状态码为 200
的 HTTP
响应,如:
HTTP/1.1 200 ok Content-Type: application/json Server: example.com { "code": 0, "msg": "success", "data": { "username": "username" } }
或
HTTP/1.1 200 ok Content-Type: application/json Server: example.com { "code": -1, "msg": "该活动不存在", }
下表列举了常见的 HTTP
状态码
状态码 | 描述 |
---|---|
1xx | 表明请求已被接受,须要继续处理 |
2xx | 请求已成功,请求所但愿的响应头或数据体将随此响应返回 |
3xx | 重定向 |
4xx | 客户端缘由引发的错误 |
5xx | 服务端缘由引发的错误 |
只有来自客户端的请求被正确的处理后才能返回2xx
的响应,因此当 API 返回2xx
类型的状态码时,前端必须
认定该请求已处理成功。
必须强调的是,全部 API
必定不可
返回 1xx
类型的状态码。当 API
发生错误时,必须
返回出错时的详细信息。目前常见返回错误信息的方法有两种:
一、将错误详细放入 HTTP
响应首部;
X-MYNAME-ERROR-CODE: 4001 X-MYNAME-ERROR-MESSAGE: Bad authentication token X-MYNAME-ERROR-INFO: http://docs.example.com/api/v1/authentication
二、直接放入响应实体中;
HTTP/1.1 401 Unauthorized Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 10:02:59 GMT Connection: keep-alive {"error_code":40100,"message":"Unauthorized"}
考虑到易读性和客户端的易处理性,咱们 必须
把错误信息直接放到响应实体中,而且错误格式 应该
知足以下格式:
{ "message": "您查找的资源不存在", "error_code": 404001 }
其中错误码(error_code
)必须
和 HTTP
状态码对应,也方便错误码归类,如:
HTTP/1.1 429 Too Many Requests Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 10:15:52 GMT Connection: keep-alive {"error_code":429001,"message":"你操做太频繁了"}
HTTP/1.1 403 Forbidden Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 10:19:27 GMT Connection: keep-alive {"error_code":403002,"message":"用户已禁用"}
应该
在返回的错误信息中,同时包含面向开发者和面向用户的提示信息,前者可方便开发人员调试,后者可直接展现给终端用户查看如:
{ "message": "直接展现给终端用户的错误信息", "error_code": "业务错误码", "error": "供开发者查看的错误信息", "debug": [ "错误堆栈,必须开启 debug 才存在" ] }
下面详细列举了各类状况 API 的返回说明。
200
状态码是最多见的 HTTP
状态码,在全部 成功 的 GET
请求中,必须
返回此状态码。HTTP
响应实体部分 必须
直接就是数据,不要作多余的包装。
错误示例:
HTTP/1.1 200 ok Content-Type: application/json Server: example.com { "user": { "id":1, "nickname":"fwest", "username": "example" } }
正确示例:
一、获取单个资源详情
{ "id": 1, "username": "godruoyi", "age": 88, }
二、获取资源集合
[ { "id": 1, "username": "godruoyi", "age": 88, }, { "id": 2, "username": "foo", "age": 88, } ]
三、额外的媒体信息
{ "data": [ { "id": 1, "avatar": "https://lorempixel.com/640/480/?32556", "nickname": "fwest", "last_logined_time": "2018-05-29 04:56:43", "has_registed": true }, { "id": 2, "avatar": "https://lorempixel.com/640/480/?86144", "nickname": "zschowalter", "last_logined_time": "2018-06-16 15:18:34", "has_registed": true } ], "meta": { "pagination": { "total": 101, "count": 2, "per_page": 2, "current_page": 1, "total_pages": 51, "links": { "next": "http://api.example.com?page=2" } } } }
其中,分页和其余额外的媒体信息,必须放到
meta
字段中。
当服务器建立数据成功时,应该
返回此状态码。常见的应用场景是使用 POST
提交用户信息,如:
等,均可以返回 201
状态码。须要注意的是,你能够选择在用户建立成功后返回新用户的数据
HTTP/1.1 201 Created Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked Date: Sun, 24 Jun 2018 09:13:40 GMT Connection: keep-alive { "id": 1, "avatar": "https:\/\/lorempixel.com\/640\/480\/?32556", "nickname": "fwest", "last_logined_time": "2018-05-29 04:56:43", "created_at": "2018-06-16 17:55:55", "updated_at": "2018-06-16 17:55:55" }
也能够返回一个响应实体为空的 HTTP Response
如:
HTTP/1.1 201 Created Server: nginx/1.11.9 Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Date: Sun, 24 Jun 2018 09:12:20 GMT Connection: keep-alive
这里咱们
应该
采用第二种方式,由于大多数状况下,客户端只须要知道该请求操做成功与否,并不须要返回新资源的信息。
该状态码表示服务器已经接受到了来自客户端的请求,但还未开始处理。经常使用短信发送、邮件通知、模板消息推送等这类很耗时须要队列支持的场景中;
返回该状态码时,响应实体
必须
为空。
HTTP/1.1 202 Accepted Server: nginx/1.11.9 Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Date: Sun, 24 Jun 2018 09:25:15 GMT Connection: keep-alive
该状态码表示响应实体不包含任何数据,其中:
DELETE
方法删除资源 成功 时,必须
返回该状态码PUT
、PATCH
方法更新数据 成功 时,也 应该
返回此状态码HTTP/1.1 204 No Content Server: nginx/1.11.9 Date: Sun, 24 Jun 2018 09:29:12 GMT Connection: keep-alive
全部 API
不应
返回 3xx
类型的状态码。由于 3xx
类型的响应格式通常为下列格式:
HTTP/1.1 302 Found Server: nginx/1.11.9 Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 09:41:50 GMT Location: https://example.com Connection: keep-alive <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <meta http-equiv="refresh" content="0;url=https://example.com" /> <title>Redirecting to https://example.com</title> </head> <body> Redirecting to <a href="https://example.com">https://example.com</a>. </body> </html>
全部 API
必定不可
返回纯 HTML
结构的响应;若必定要使用重定向功能,能够
返回一个响应实体为空的 3xx
响应,并在响应头中加上 Location
字段:
HTTP/1.1 302 Found Server: nginx/1.11.9 Content-Type: text/html; charset=UTF-8 Transfer-Encoding: chunked Date: Sun, 24 Jun 2018 09:52:50 GMT Location: https://godruoyi.com Connection: keep-alive
因为明显的客户端错误(例如,请求语法格式错误、无效的请求、无效的签名等),服务器 应该
放弃该请求。
当服务器没法从其余 4xx 类型的状态码中找出合适的来表示错误类型时,都
必须
返回该状态码。
HTTP/1.1 400 Bad Request Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 13:22:36 GMT Connection: keep-alive {"error_code":40000,"message":"无效的签名"}
该状态码表示当前请求须要身份认证,如下状况都 必须
返回该状态码。
客户端在收到401
响应后,都应该
提示用户进行下一步的登陆操做。
HTTP/1.1 401 Unauthorized Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked WWW-Authenticate: JWTAuth Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 13:17:02 GMT Connection: keep-alive {"message":"Token Signature could not be verified.","error_code": "40100"}
该状态码能够简单的理解为没有权限访问该请求,服务器收到请求但拒绝提供服务。
如当普通用户请求操做管理员用户时,必须
返回该状态码。
HTTP/1.1 403 Forbidden Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 13:05:34 GMT Connection: keep-alive {"error_code":40301,"message":"权限不足"}
该状态码表示用户请求的资源不存在,如
都 必须
返回该状态码,若该资源已永久不存在,则 应该
返回 410
响应。
当客户端使用的 HTTP
请求方法不被服务器容许时,必须
返回该状态码。
如客户端调用了
POST
方法来访问只支持 GET 方法的 API
该响应 必须
返回一个 Allow
头信息用以表示出当前资源可以接受的请求方法的列表。
HTTP/1.1 405 Method Not Allowed Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked Allow: GET, HEAD Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 12:30:57 GMT Connection: keep-alive {"message":"405 Method Not Allowed","error_code": 40500}
API
在不支持客户端指定的数据格式时,应该返回此状态码。如支持 JSON
和 XML
输出的 API
被指定返回 YAML
格式的数据时。
Http 协议通常经过请求首部的 Accept 来指定数据格式
客户端请求超时时 必须
返回该状态码,须要注意的时,该状态码表示 客户端请求超时,在涉及第三方 API
调用超时时,必定不可
返回该状态码。
该状态码表示由于请求存在冲突没法处理。如经过手机号码提供注册功能的 API
,当用户提交的手机号已存在时,必须
返回此状态码。
HTTP/1.1 409 Conflict Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 12:19:04 GMT Connection: keep-alive {"error_code":40900,"message":"手机号已存在"}
和 404
相似,该状态码也表示请求的资源不存在,只是 410
状态码进一步表示所请求的资源已不存在,而且将来也不会存在。在收到 410
状态码后,客户端 应该
中止再次请求该资源。
该状态码表示服务器拒绝处理当前请求,由于该请求提交的实体数据大小超过了服务器愿意或者可以处理的范围。
此种状况下,服务器能够关闭链接以避免客户端继续发送此请求。
若是这个情况是临时的,服务器 应该
返回一个 Retry-After
的响应头,以告知客户端能够在多少时间之后从新尝试。
该状态码表示请求的 URI
长度超过了服务器可以解释的长度,所以服务器拒绝对该请求提供服务。
一般表示服务器不支持客户端请求首部 Content-Type
指定的数据格式。如在只接受 JSON
格式的 API
中放入 XML
类型的数据并向服务器发送,都 应该
返回该状态码。
该状态码也可用于如:只容许上传图片格式的文件,可是客户端提交媒体文件非法或不是图片类型,这时 应该
返回该状态码:
HTTP/1.1 415 Unsupported Media Type Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 12:09:40 GMT Connection: keep-alive {"error_code":41500,"message":"不容许上传的图片格式"}
该状态码表示用户请求次数超过容许范围。如 API
设定为 60次/分钟
,当用户在一分钟内请求次数超过 60 次后,都 应该
返回该状态码。而且也 应该
在响应首部中加上下列头部:
X-RateLimit-Limit: 10 请求速率(由应用设定,其单位通常为小时/分钟等,这里是 10次/5分钟) X-RateLimit-Remaining: 0 当前剩余的请求数量 X-RateLimit-Reset: 1529839462 重置时间 Retry-After: 120 下一次访问应该等待的时间(秒)
列子
HTTP/1.1 429 Too Many Requests Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked X-RateLimit-Limit: 10 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1529839462 Retry-After: 290 Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 11:19:32 GMT Connection: keep-alive {"message":"You have exceeded your rate limit.","error_code":42900}
必须
为全部的 API 设置 Rate Limit 支持。
该状态码 必须
在服务器出错时抛出,对于全部的 500
错误,都 应该
提供完整的错误信息支持,也方便跟踪调试。
该状态码表示服务器暂时处理不可用状态,当服务器须要维护或第三方 API
请求超时/不可达时,都 应该
返回该状态码,其中如果主动关闭 API 服务,应该
在返回的响应首部加上 Retry-After
头部,表示多少秒后能够再次访问。
HTTP/1.1 503 Service Unavailable Server: nginx/1.11.9 Content-Type: application/json Transfer-Encoding: chunked Cache-Control: no-cache, private Date: Sun, 24 Jun 2018 10:56:20 GMT Retry-After: 60 Connection: keep-alive {"error_code":50300,"message":"服务维护中"}
其余 HTTP
状态码请参考 HTTP 状态码- 维基百科。
版权声明:自由转载-非商用-非衍生-保持署名( 创意共享3.0许可证)
Principles of good RESTful API Design(译)
MIT License
Copyright (c) 2018 godruoyi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS ORIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THEAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHERLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THESOFTWARE.