记录一下我学习 http 协议和 RESTful 最佳实践的学习过程。
HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。
换句话说,http 的表现是文本,所有的数据都通过文本来体现,各个控制传输的参数也是文本来提现。(ps:http2协议是二进制协议,但是核心思想没有变)
知识储备&预备
what happens after enter url
首先要对整个 web 栈有一个理解,最典型的问题就是问自己,输入 url 到页面完整的完成,过程中发生了什么:
https://github.com/skyline75489/what-happens-when-zh_CN
tcp 协议
http://www.jianshu.com/p/ef892323e68f
理解一下 http 为什么是可靠连接
osi 七层协议
http://www.jianshu.com/p/4b9d43c0571a
我们主要是在应用层使用 http 协议
http主要特点
基础知识在这:
http://www.jianshu.com/p/80e25cb1d81a
现在的 web 框架本质上是对 http 的Request && Response请求的封装。
http 协议的Request和Response由哪几个部分组成?服务器和客户端如何相互确定需要的信息?
https://developer.mozilla.org/en-US/docs/Web/HTTP/Overview
Request
http methed,请求方法。可以使动词方法 GET, POST,或者名词OPTIONS or HEAD,协议头。定义一次请求想干啥。
- 常用 header,常用 methed
- header 的内容包括不限于缓存控制,语言,数据格式,用户身份认证等
- header 里的重点 cookies,缓存控制策略相关 header,CORS 跨域相关的 header,注意Accept和Content-Type的不同,一个是来一个是回。
Path of resource,资源路径。 定义和标识需要操作的资源。包括一个协议头、地址、端口等。
- 路径的的表示方法分为url urn uri。
- 常用的协议和端口
HTTP version,协议版本.
Body,正文,比如 POST 请求,数据会放在 body 里。
- 常见的 POST 提交数据,
Content-Type
控制。
response
HTTP version,协议版本,同上。
Status code, 状态号,告知你请求是否成功,怎样成功或者失败。
Status message, 一段简单的文字状态描述。
HTTP headers,请求头,同上。
- header 进一步详细的的介绍可以看这个 mozilla docs
- 对 header 如果很熟悉就可以试着去玩一下 Nginx,有各种通过正则式改写 header 和 url 的技巧
- 写 header 可以完成某些转发、跨域、缓存、身份验证、文件下载等需求。
Body,应该返回的数据或者媒体资源文件。
- json 是 JavaScript 的经典数据描述,但是表示一个对象有更多的 序列化和反序列化 工具。
post 和 get 有什么区别?put 和 patch 有什么区别?
细节可以看这个post&get对比
post 和 get 设计上主要的区别是有没有幂等性,get执行一次和执行 n 次结果一样。符合范式的 get 应该是请求无数次后也不会改变服务器状态。其他的特性都是为了保障幂等性而做的限制:
- get 可以缓存,post 不能。因为执行 n 次对结果没有影响,那么只要记录一次,再执行 n 次结果也应该一样。
- get 只能 ASCII 字符编码,数据以 param 的形式被编码进URL,而且有长度限制,是为了对数据进行更好的标识,方便缓存等行为。而 post 可以提交的数据类型可以是 multipart/form-data 甚至是 file存贮在 body 中,数据不会被暴露在 url 里。url 在传输的时候是不会对 header 和 url 进行加密的。
- post 刷新之后数据会被重新提交,参数不会被保存在浏览器历史中,是为了防止 post被多次提交改变服务器状态。
patch 是新版 http 协议里支持的方法,在RESTful里面,put 是把应该对象完整的放到 uri,而 patch 是对一个对象的部分属性进行更新。举个例子:
服务器有对象 {a:1, b:2},
请求 PUT {a:2} 之后,服务器的对象变为{a:2}。
请求 PATCH {a:2} 之后,服务器的对象变为{a:2, b:2}
http协议栈如何分层,更高一层使用了哪些技术。
RESTful
what?
origin
REST这个词,是Roy Thomas Fielding在他2000年的博士论文中提出的。
Fielding是一个非常重要的人,他是HTTP协议(1.0版和1.1版)的主要设计者、Apache服务器软件的作者之一、Apache基金会的第一任主席。所以,他的这篇论文一经发表,就引起了关注,并且立即对互联网开发产生了深远的影响。
论文中给了很多的建议,以至于这些建议很多都已经成为开源世界的默认标准。
define
REST,是Representational State Transfer的缩写。我对这个词组的翻译是”表现层状态转化”。如果一个架构符合REST原则,就称它为RESTful架构。
details
REST的名称”表现层状态转化”中,省略了主语。”表现层”其实指的是”资源”(Resources)的”表现层”。所谓”资源”,就是网络上的一个实体,或者说是网络上的一个具体信息,它可以是一段文本、一张图片、一首歌曲、一种服务。
所谓”上网”,就是与互联网上一系列的”资源”互动,然后 RESTful 现在定义了 一组能唯一标识资源的 path 和对资源进行的操作的一种规范,RESTful 架构的意思就是,满足与网上一系列资源互动的一组规范的架构。
why?
scene
现在考虑这样的资源和操作,我们要分为两个不相关的步骤:
- 第一步,通过某些标识唯一指定某个资源
- 第二步,骤以及对这个资源进行操作,转换成我想要的样子。
define
前者叫做表现层RE(Representation),后者叫做状态转换ST(State Transfer)
具体来说,我要从服务器拿一段文本。可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式;它可以是不同的语言。
现在我们希望分成这两个要素 RE 和 ST
- RE: URI只代表资源的实体,不代表它的形式。比如网址最后的”.html”后缀名是不必要的,因为这个后缀名表示格式,属于 ST 范畴,而 URI 应该只代表”资源”的位置。
- ST: 它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对”表现层”的描述。它的语言应该在Accept-Language中指定
RESTful
互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生”状态转化”(State Transfer)。而这种转化是建立在表现层之上的,所以就是”表现层状态转化”。
客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面的操作方式动词:GET、POST、PUT、PATCH、DELETE。它们分别对应增删改查基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT、PATCH用来更新资源,DELETE用来删除资源。
1 | (1)每一个URI代表一种资源; |
how?
basic
常见增删改查,注意下安全性和幂等性。
- 安全性:不会改变资源状态,可以理解为只读的;
- 幂等性:执行1次和执行N次,对资源状态改变的效果是等价的。
安全性和幂等性均不保证反复请求能拿到相同的response。以 DELETE 为例,第一次DELETE返回200表示删除成功,第二次返回404提示资源不存在,这是允许的。
1 | GET /zoos:列出所有动物园 |
common
花式GET,并且所有请求都是可以缓存的,在浏览器,nginx,服务端代码里都应该可以缓存。
1 | GET /zoos |
花式POST:创建单个资源。POST一般向“资源集合”型uri发起
1 | POST /animals //新增动物 |
花式PUT:更新单个资源(全量),客户端提供完整的更新后的资源。与之对应的是 PATCH,PATCH 负责部分更新,客户端提供要更新的那些字段。PUT/PATCH一般向“单个资源”型uri发起
1 | PUT /animals/1 |
花式DELETE
1 | DELETE /zoos/1/employees/2 |
HEAD / OPTION 作为简单请求,在 RESTful 不重要,毕竟,RESTful 可以方便的一键生成单个资源的 CRUD,快速的生成资源之间的相互 join。
more
API的身份认证应该使用OAuth框架,增加一个 login、logout 接口,使用 token 来确认身份。
Hypermedia API
1
2
3
4
5
6
7
8//RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
//比如 angular ember react-redux 都对标准的 RESTful 有一定支持,而且倾向于拿单个的对象,然后每个对象单独更新,这样每个对象都容易更新。
//在某些情况下可以把下面 zoos 对应的 url 换成一个 list,tiger 换成一个 object,框架很容易完成这件事。
{
"zoos": "https://api.example.com/zoos",
"tiger": "https://api.example.com/zoos/1",
"title": "List of zoos",
}服务的 URL 设计
服务应该使用名词,比如计算应该使用calculation,不应该使用calc或者calculate。
1 | //应该根据幂等性来使用GET 和 Post,典型的就是一个函数式计算任务和一个队列提交任务。 |
设计的时候应该要保证服务可以单独部署,对多个资源操作的事务由单独部署的服务来管理。
各HTTP方法成功处理后Response的数据格式:
1
2
3
4
5response 格式
GET 单个对象、集合
POST 新增成功的对象
PUT/PATCH 更新成功的对象
DELETE 空, 通过状态码确认json格式的约定:
时间用长整形(毫秒数)URI规范
1
2
3
4不用大写;
用中杠-不用下杠_;
参数列表要encode;
URI中的名词表示资源集合,使用复数形式。避免层级过深的URI
一般根据id导航。过深的导航容易导致url膨胀,不易维护。尽量使用查询参数代替路径中的实体导航。1
2
3
4相对不好的设计
GET /zoos/1/areas/3/animals/4
相对好的设计
GET /animals?zoo=1&area=3;
对Composite资源的访问
1
它的生命周期完全依赖父实体,无法独立存在,不直接对应表,也无id。服务器端的组合实体必须在uri中通过父实体的id导航访问。一个常见的例子是 User — Address,Address是对User表中zipCode/country/city三个字段的简单抽象,无法独立于User存在。必须通过User索引到Address:GET /user/1/addresses
URI失效
应该添加301到新地址,或者有一个错误提示。版本
GITHUB 的处理方式是这样的:1
https://api.example.com/v1/
不要瞎JB搞,乱包东西很丑
1
2
3
4
5//H5前端工程师非常讨厌这种搞法,开源世界的库都不能愉快的使用。
{
"success":true,
"data":{"id":1,"name":"xiaotuan"},
}
So what?
正如前文所说,RESTful 几乎是开源世界里非rpc调用最通用的接口。很多的开源 web 框架都有对应的 RESTful 库类,可以用极少量的配置完成对对象的序列化。
典型的如 Django的django-restful-framework,100行代码可以完成一个支持邮件收发、注册改密码、身份验证、序列化反序列化、数据 schema,持久化增删改查等的 todoMVC项目。
符合规范可以可以极大的提高开发效率!