restful规范
GET /tickets - 获取 tickets 列表
GET /tickets/12 - 获取一个单独的 ticket
POST /tickets - 创建一个新的 ticket
PUT /tickets/12 - 更新 ticket #12
DELETE /tickets/12 - 删除 ticket #12
GET /collection:返回资源对象的列表(数组)
GET /collection/resource:返回单个资源对象
POST /collection:返回新生成的资源对象
PUT /collection/resource:返回完整的资源对象
PATCH /collection/resource:返回完整的资源对象
DELETE /collection/resource:返回一个空文档
接入点的名称应该选择单数还是复数呢?keep-it-simple原则可以在此应用。虽然你内在的语法知识会告诉你用复数形式描述单一资源实例是错误的,但实用主义的答案是保持URL格式一致并且始终使用复数形式。不用处理各种奇形怪状的复数形式(比如person/people,goose/geese)可以让API消费者的生活更加美好,也让API提供者更容易实现API(因为大多数现代框架天然地将/tickets和/tickets/12放在同一个控制器下处理)。
但是你该如何处理(资源的)关系呢?如果关系依托于另外一个资源,Restful原则提供了很好的指导原则。让我们来看一个例子。一个ticket包含许多消息(message)。这些消息逻辑上与/tickets接入点的映射关系如下:
GET /tickets/12/messages - 获取ticket #12下的消息列表
GET /tickets/12/messages/5 - 获取ticket #12下的编号为5的消息
POST /tickets/12/messages - 为ticket #12创建一个新消息
PUT /tickets/12/messages/5 - 更新ticket #12下的编号为5的消息
PATCH /tickets/12/messages/5 - 部分更新ticket #12下的编号为5的消息
DELETE /tickets/12/messages/5 - 删除ticket #12下的编号为5的消息
应该将API的版本号放入URL。
https://api.example.com/v1/zoos
https://api.example.com/v1/animals
https://api.example.com/v1/employees
结果过滤,排序和搜索
最好是尽量保持基本资源URL的简洁性。 复杂结果过滤器、排序需求和高级搜索 (当限定在单一类型的资源时) ,都能够作为在基本URL之上的查询参数来轻松实现。下面让我们更详细的看一下:
过滤: 对每一个字段使用一个唯一查询参数,就可以实现过滤。 例如,当通过“/tickets”终端来请求一个票据列表时,你可能想要限定只要那些在售的票。这可以通过一个像 GET /tickets?state=open 这样的请求来实现。这里“state”是一个实现了过滤功能的查询参数。
排序: 跟过滤类似, 一个泛型参数排序可以被用来描述排序的规则. 为适应复杂排序需求,让排序参数采取逗号分隔的字段列表的形式,每一个字段前都可能有一个负号来表示按降序排序。我们看几个例子:
GET /tickets?sort=-priority - 获取票据列表,按优先级字段降序排序
GET /tickets?sort=-priority,created_at - 获取票据列表,按“priority”字段降序排序。在一个特定的优先级内,较早的票排在前面。
搜索: 有时基本的过滤不能满足需求,这时你就需要全文检索的力量。或许你已经在使用 ElasticSearch或者其它基于 Lucene的搜索技术。当全文检索被用作获取某种特定资源的资源实例的机制时, 它可以被暴露在API中,作为资源终端的查询参数,我们叫它“q”。搜索类查询应当被直接交给搜索引擎,并且API的产出物应当具有同样的格式,以一个普通列表作为结果。
把这些组合在一起,我们可以创建以下一些查询:
GET /tickets?sort=-updated_at - 获取最近更新的票
GET /tickets?state=closed&sort=-updated_at - 获取最近更新并且状态为关闭的票。
GET /tickets?q=return&state=open&sort=-priority,created_at - 获取优先级最高、最先创建的、状态为开放的票,并且票上有 'return' 字样。
一般查询的别名
为了使普通用户的API使用体验更加愉快, 考虑把条件集合包装进容易访问的RESTful 路径中。比如上面的,最近关闭的票的查询可以被包装成GET /tickets/recently_closed
限制哪些字段由API返回
API的使用者并不总是需要一个资源的完整表示。选择返回字段的功能由来已久,它使得API使用者能够最小化网络阻塞,并加速他们对API的调用。
使用一个字段查询参数,它包含一个用逗号隔开的字段列表。例如,下列请求获得的信息将刚刚足够展示一个在售票的有序列表:
GET /tickets?fields=id,subject,customer_name,updated_at&state=open&sort=-updated_at
更新和创建应该返回一个资源描述
一个 PUT, POST 或者 PATCH 调用可能会对指定资源的某些字段造成更改,而这些字段本不在提供的参数之列 (例如: created_at 或 updated_at 这两个时间戳)。 为了防止API使用者为了获取更新后的资源而再次调用该API,应当使API把更新(或创建)后的资源作为response的一部分来返回。
以一个产生创建活动的 POST 操作为例, 使用一个HTTP 201 状态代码然后包含一个Location header来指向新生资源的URL。
200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE]:用户删除数据成功。
400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
无效数据不返回,必要数据返回
什么叫无效数据不返回,必要数据返回呢?
无效数据清理:对于json响应接口,我们需要遵守对所有值为null的字段不做返回,对前端不关心的数据不做返回(合理的定义VO是很有必要的)。
对于spring boot 我们可以用下配置,实现字段值为null时不做返回。
spring.jackson.date-format=yyyy-MM-ddHH:mm:ss
spring.jackson.time-zone=Asia/Shanghai
spring.jackson.default-property-inclusion=non_null
必要数据返回:对于添加(POST)、修改(PUT | PATCH)这类方法我们需要立即返回添加或更新后的数据以备前端使用(这是一个约定需要遵守)。
关于RESTFul API设计时,URL路径中不可以使用下划线吗
如果网站的链接包含hello-world,搜索引擎收录的索引为hello 和 world;
如果网站的链接包含hello_world,搜索引擎收录的索引为hello_world。