RESTful 设计
-
- RESTful
-
-
- 1. HTTP可见性
-
-
- 1. 1 GET
- 1.2 POST
- 1.3 PUT
-
- 2. 识别资源
-
-
- 2.1 业务实体指定资源
- 2.2 如何选择资源粒度
- 2.3 将资源组织为集合
- 2.4 将资源合并为复合资源
-
- 3. HTTP实体头
-
-
- 3.1 实体头对应的表述
- 3.2 解释实体头
- 3.3 字符编码不匹配
- 3.4设计JSON类型
- 3.5 返回分页对象
-
-
- 注:
-
-
- 注1:Referer
-
基于《RESTful Web Service Cookbook》
RESTful
1. HTTP可见性
可见性是http的关键特性之一
-
众所周知,http是通讯协议,那么通讯的本质是什么呢?
答:通讯其实是获取消息(或者称为资源,数据等等)的手段,相当于要能够随时获取(或者称为看到)到其它程序(服务器,代理,检测软件等)的消息,这就是可见性。
-
如何设计统一标准,使得可见性比较好呢?
答:一旦设计完成消息样式,使用http提供的方法(如:get,post,本质是请求头上的一种标识符)来确定对消息的获取方式(如:增删改查)
-
我们可以通过可见性实现哪些功能?
答:
- 可以实现消息的缓存(不修改资源就使用缓存,修改资源就刷新缓存或使缓存失效)
- 乐观锁的并发控制(检测修改消息的请求,控制并发)
- 选择合适的消息表示方式
- 安全性和幂等性( 多次请求均与一次请求对消息的影响相同,可以确保重复请求不发生问题 )
保持可见性可以让我们使用现有HTTP软件的基础设施来实现之前必须自己实现的功能
1. 1 GET
HTTP严重依赖GET方法的安全性和幂等性,客户端希望能够重复发起GET请求,而不必担心造成副作用。
-
如何使用GET请求呢?
答:使用GET方法进行安全和幂等的消息的获取(就是查询消息,GET请求会被HTTP缓存)
1.2 POST
-
如何使用POST请求
答: HTTP不会缓存这一方法的响应,大部分HTTP工具不会自动重复提交POST请求
当我们需要 创建新的资源,执行需要大量数据输入的查询或通过控制器修改消息等情况下使用POST请求。
-
什么时候看上去可以使用GET请求,其实应该使用POST请求?
答:如果是通过链接发起请求获取资源时,HTTP协议会将当前页面的URL作为Referer头(注1)。这可能会把包含在URI中的敏感信息泄露,这种情况要使用传输层安全协议(TLS)或者使用POST请求。
当查询包含太多参数时,也应该使用POST。
1.3 PUT
我们可以使用POST或PUT创建新资源
-
什么时候使用POST,什么时候使用PUT?
答:只有客户端可以决定资源的URI时才使用PUT,否则使用POST。
就是服务器已经对资源设置了添加的根目录(例如:通过/user/{id}/{name}/home_address 路径访问服务器,服务器对资源路径指定了具体的用户,这样前端可以直接在URL上指定用户创建家庭地址)
2. 识别资源
2.1 业务实体指定资源
可以直接将数据库对象实体作为资源,可以通过GET,POST,PUT,DELETE进行CURD操作,但是对于其他类型接口,如:
- 生产随机数或单位转换
- 获取指定类型的用户集合,列出前10个用户
- 将钱从一个账号转到另一个账户
这些例子中,我们可以指定对于的名称对应为资源,但是相应的操作没办法映射到GET,POST,PUT,DELETE这些HTTP方法中,需要额外的资源处理这些用例。
2.2 如何选择资源粒度
-
选择资源的标准是什么?
答:可以通过网络效率(缓存),响应内容大小及客户端的易用程度来帮助确定资源的粒度
如:用户可能包含地址,电话,账号,好友关系等信息,是将/user直接作为请求资源还是将/user/mobiles,/user/address等作为资源,这些最好从客户端进行考虑。
2.3 将资源组织为集合
-
如何将一堆共用资源设计为一个资源来使用?
答: 对于共享同一数据的资源,有相同特性或属性的资源或者客户端看起来相似的资源封装为同一资源,提供给客户端。
如用户资源可以分类为好友关系,亲人关系,兴趣关系等,这些关系可以看做同一资源,可以讲URI设计为/user/{id}/friends和/user/{id}/interest/{interest_id}等
2.4 将资源合并为复合资源
-
如何提供一个又多条件组合而成的资源
答:从客户端考虑,创建一些新的资源类,类聚合其他资源。
# 获取用户数据 GET /customer/123 HTTP/1.1 # 获取最近10个购买订单 GET /orders?customerid=123&sortby=date_desc&limit=10 HTTP/1.1 # 获取最近10个待决定报价 GET /quotes?customerid=123&sortby=date_des&status=pending&$limit=10 HTTP/1.1
尽管这一系列GET请求能被服务器接收,但它们太过频繁,对客户端而言,如果只发送一条请求来获取页面所需的所有数据,可能效率更高一些。
针对同一用户的多个资源,可以设计一个“用户快照”,其中包含了所有信息
# 请求 用户快照 GET /customer/1234/snapshot HTTP/1.1 # 响应 包含了三个链接link和其对应的数据
3. HTTP实体头
实体头是HTTP请求头及HTTP响应头。
3.1 实体头对应的表述
-
请求和响应中发送哪些HTTP头?
答: 一下标记头来描述消息正文
- Content-Type:描述消息类型,包含字符集或消息对应的类型参数
- Content-Length:消息大小
- Content-Language:消息本地化语言标记
- Content-MD5:进行消息一致性校验,TCP使用校验和在传输层提供一致性校验
- Content-Encoding:使用gzip,compress或者deflate对消息进行压缩编码是,使用该标记头
- Last-Modified:服务器修改资源的最后修改时间
Content-Type:消息的类型,就是常说的media-type或者MIME,如text/html,image/png,application/xml,application/json和text/plain等,这些都是消息正文的编码格式标识符,就是数据传输过程中的类型,如xml或者json格式
3.2 解释实体头
-
如何解释消息的实体头和如何用实体头来处理消息?
Content-Type:没有该标签,服务器返回400 (Bad Reqeust)
一定要基于Content-Type,Content-Language,Content-Encoding的值来确定响应的描述,不能根据客户端发送了Accept:application/json或资源以.json结尾就确定响应格式是JSON。
3.3 字符编码不匹配
-
如何确保字符能被正确解释
答:发送时带上charset参数
3.4设计JSON类型
-
JSON类型的消息中要包含什么数据
答:{
“link”:{
“rel”:“self”,
“href” :“http://www.example.org/person/1234”
}
}
3.5 返回分页对象
-
分页对象的响应格式?
答: 包含 self:“当前链接”,prev:“上一页链接”,next:“下一页链接”,total:“总条数”
注:
注1:Referer
Referer是 HTTP请求
header
的一部分,当浏览器(或者模拟浏览器行为)向
web
服务器发送请求的时候,头信息里有包含 Referer。
表示从一个地方链接到当前网页, 如超链接。
当我们通过百度搜索后访问其他网站时(此时就是通过链接访问),Referer就指向百度URI。我们通过Referer来确定请求的访问来源(从哪个网站点进该请求的)。
如果直接在浏览器地址栏输入一个资源的URL地址,Referer为空