Representational State Transfer 简称 REST 描述了一个架构样式的网络系统。REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful。
概念:
URI中尽量使用连字符”-“代替下划线”_”的使用
URI中统一使用小写字母
URI中不要包含文件(脚本)的扩展名
文档(Document)
文档是资源的单一表现形式,可以理解为一个对象,或者数据库中的一条记录。在请求文档时,
要么返回文档对应的数据,要么会返回一个指向另外一个资源(文档)的链接。
以下是几个基于文档定义的URI例子:
https://api.example.com/users/will
https://api.example.com/posts/1
https://api.example.com/posts/1/comments/1
集合(Collection)
集合可以理解为是资源的一个容器(目录),我们可以向里面添加资源(文档)。例如:
https://api.example.com/users
https://api.example.com/posts
https://api.example.com/posts/1/comments
仓库(Store)
仓库是客户端来管理的一个资源库,客户端可以向仓库中新增资源或者删除资源。客户端也可以批量获取到某个仓库下的所有资源。仓库中的资源对外的访问不会提供单独URI的, 客户端在创建资源时候的URI除外。例如:
PUT /users/1234/favorites/posts/1
上面的例子我们可以理解为,我们向一个id是1234的用户的仓库(收藏夹)中,添加了一个id为1的post资源。通俗点儿说:就是用户收藏了一个自己喜爱的id为1的文章
控制器(Controller)
控制器资源模型,可以执行一个方法,支持参数输入,结果返回。 是为了除了标准操作:
增删改查(CRUD)以外的一些逻辑操作。控制器(方法)一般定义子URI中末尾,
并且不会有子资源(控制器)。例如:
向用户重发ID为245743的消息
POST /alerts/245743/resend
发布ID为1的文章
POST /posts/1/publish
把动作转换成资源
把动作转换成可以执行 CRUD 操作的资源, github 就是用了这种方法。
比如“喜欢”一个 gist,就增加一个 /gists/:id/star 子资源,
然后对其进行操作:“喜欢”使用 PUT /gists/:id/star,
“取消喜欢”使用 DELETE /gists/:id/star
或者使用 POST /gists/:id/unstar
另外一个例子是 Fork,这也是一个动作,但是在 gist 下面增加 forks资源,
就能把动作变成 CRUD 兼容的:POST /gists/:id/forks 可以执行用户 fork 的动作。
例如一个资源URI可以这样定义:
https://api.example.com/posts/{postId}/comments/{commentId}
postId,commentId 是变量(数字,字符串都类型都可以)。
CRUD是创建,读取,更新,删除这四个经典操作的简称
例如删除的操作用REST规范执行的话,应该是这个样子:
DELETE /users/1234
以下是几个错误的示例:
GET /deleteUser?id=1234
GET /deleteUser/1234
DELETE /deleteUser/1234
POST /users/1234/delete
在REST中,query字段一般作为查询的参数补充,也可以帮助标示一个唯一的资源。但需要注意的是,
作为一个提供查询功能的URI,无论是否有query条件,我们都应该保证结果的唯一性,
一个URI对应的返回数据是不应该被改变的(在资源没有修改的情况下)。
HTTP中的缓存也可能缓存查询结果。
GET /users //返回所有用户列表
GET /users?role=admin //返回权限为admin的用户列表
GET /search/users?q={query}{&page,per_page,sort,order} //根据多条件查询用户
如果是一个简单的列表操作,可以这样设计:
GET /users?pageSize=25&pageStartIndex=50
如果是一个复杂的列表或查询操作的话,我们可以为资源设计一个Collection,
因为复杂查询可能会涉及比较多的参数,建议使用Post的方式传入,例如这样:
POST /users/search
相关的分页信息还可以存放到 Link 头部,这样客户端可以直接得到诸如下一页、最后一页、上一页
等内容的 url 地址
Status: 200 OK
Link: <https://api.github.com/resource?page=2>; rel="previous",
<https://api.github.com/resource?page=2>; rel="next",
<https://api.github.com/resource?page=5>; rel="last"
X-RateLimit-Limit: 20
X-RateLimit-Remaining: 19
假设有两个客户端client#1/#2都向一个Store资源提交PUT请求,服务端是无法清楚的判断是要insert还是要update的,所以我们要在header中加入条件标示if-Match,If-Unmodified-Since
来明确是本次调用API的意图。例如:client#1第一次向服务端发起一个请求 PUT /objects/2113 此时2113资源还不存在,那服务端会认为本次请求是一个insert操作,完成后,会返回 201 (“Created”) client#2再一次向服务端发起同一个请求 PUT /objects/2113 时,因2113资源已存在,服务端会返回 409 (“Conflict”)为了能让client#2的请求成功,或者说我们要清楚的表明本次操作是一次update操作,我们必须在header中加入一些条件标示,例如 if-Match。我们需要给出资源的ETag(if-Match:Etag),来表明我们希望更新资源的版本,如果服务端版本一致,会返回200 (“OK”) 或者 204 (“No Content”)。如果服务端发现指定的版本与当前资源版本不一致,会返回 412 (“Precondition Failed”)
在 url 中指定 API 的版本是个很好地做法。如果 API 变化比较大,可以把 API 设计为子域名,
比如 https://api.github.com/v3;也可以简单地把版本放在路径中,比如 https://example.com/api/v1。
另一种做法是,将版本号放在HTTP头信息中。
如果对访问的次数不加控制,很可能会造成 API 被滥用,甚至被 DDos 攻击。根据使用者不同的身
份对其进行限流,可以防止这些情况,减少服务器的压力。
对用户的请求限流之后,要有方法告诉用户它的请求使用情况,Github API 使用的三个相关的头部:
原文:https://www.cnblogs.com/fby698/p/12698549.html