文章目录
一、HTTP简介
HTTP简介在前篇博客:Web三大技术要素中有介绍,三大技术要素之间相互关联依赖,比如HTML中的超链接使用URL,HTTP报文中的主体部分则是HTML文档或其内包含的MIME资源。
本文主要介绍HTTP协议部分,由于目前使用最广泛的仍然是HTTP/1.1 版本,截至2020年4月,W3Techs统计的前1000万网站中支持HTTP/2的大概占43.6%,支持QUIC的仅占4.2%。HTTP/2也是从HTTP/1.1演化升级而来,要了解HTTP/2同样离不开HTTP/1.1,因此本文先介绍主流的HTTP/1.1版本。
我们再看看HTTP协议在整个网络分层参考模型中的位置,HTTP属于应用层协议,底层依赖于TCP/IP协议完成数据报文的传输。如果对HTTP报文有加密传输的需求,在HTTP协议与TCP协议之间还可以增加SSL/TLS协议层,这就构成了HTTPS协议。
在介绍HTTP/1.1 报文结构前,再回顾下HTTP/1.1 解决了前代的哪些问题或者带来了哪些性能提升:
HTTP/1.0问题 | HTTP/1.1改进 |
---|---|
不能让多个请求共用一个连接 | 默认支持持久连接(keep-alive),管道化(pipelining)特性允许客户端在一次TCP连接中发送所有的请求,这对于提升性能和效率而言意义重大 |
缺少强制的 Host 首部 | 强制要求客户端提供 Host 首部,让虚拟主机托管(在一个 IP 上提供多个 Web 服务主机域名)成为可能 |
缓存控制相当简陋 | 扩展缓存相关首部以增强缓存控制,增加Range请求以支持断点续传,增加Upgrade 首部以支持升级到其它协议比如WebSocket |
仅支持基本的GET、POST 和 HEAD请求方法 | 增加了OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 等六种请求方法,扩展了功能 |
上面提到的最重要性能改进 — HTTP持久连接与管道化机制在前篇博客Web三大技术要素中也介绍过,这里再给出图示:
TCP三次握手与四次挥手的过程比较耗时,现在每个网页的平均资源数接近两百,如果每个资源请求都执行一次TCP连接/断开,对网络资源的浪费相当大。因此,HTTP/1.1 支持多个请求共用一次TCP连接,也即在请求网页时执行一次TCP连接,随后客户端向服务器发送该网页相关的所有资源请求,既节省了大量重复的TCP连接/断开开销,又不需要等收到资源响应后再发送下一个资源请求,极大提升了Web性能与效率。
HTTP/1.1 虽然支持以流水线的形式在一次TCP连接中发送指定网页的所有资源请求,但是服务器仍然只能按顺序响应请求,如果处理某个请求花了很长时间,那么队头阻塞(head of line blocking)会影响其他请求,这是HTTP/1.1 待改进的一个方面。
HTTP/1.1 诞生于1997年,到现在已经二十多年过去了,Web发展更是突飞猛进,HTTP/1.1 的性能瓶颈越来越多,比如每次资源请求都需要发送越来越大的的报文首部,每个网页中平均近两百个资源的报文首部大同小异,这又是对网络资源的极大浪费。随着对Web性能的需求越来越高,针对HTTP/1.1 的性能瓶颈出现了五花八门的优化方案,直到近20年后的2015年,HTTP/2才姗姗来迟,解决了HTTP/1.1 遗留的诸多瓶颈,跟上了目前跨平台多终端Web的性能需求。
二、HTTP报文
HTTP协议采用Client / Browser — Server 架构通信,当我们使用Client / Browser 访问Web时,在浏览器地址栏输入想获取资源页面的URL,Browser 向Server 发送 HTTP请求报文,告诉服务器想请求哪个资源,Server 找到或生成请求的资源后,通过HTTP响应报文将资源发送给Browser / Client,浏览器或客户端接收到HTML资源页面后,将其处理成更直观的形式展示给你,整个过程图示如下:
2.1 HTTP Request
HTTP 请求报文包括请求行、请求头部、空行、报文主体等部分构成,请求行包括请求方法、URL、协议版本三部分,请求首部主要包括请求的各种条件或属性,空行是为了分割首部与主体,报文主体则包含应被发送的数据。HTTP请求报文结构与示例如下:
HTTP 请求方法是客户端用来告知服务器自己意图的,HTTP/1.1支持的请求方法及功能描述如下表示(请求方法名要使用大写字母):
方法 | 描述 |
---|---|
GET | 用来请求访问已被 URL 指定的资源,请求条件或参数被包含在URL中,指定的资源经服务器端解析后返回响应内容,操作是安全且幂等的;如果请求的资源是静态文本则保持原样返回,如果是像 CGI / Servlet 那样的程序则返回经过执行后的输出结果 |
HEAD | 和 GET 方法一样,只是不返回报文主体部分,常用于确认URL 的有效性及资源更新的日期时间等 |
POST | 向指定的资源提交数据进行处理的请求,比如提交表单或上传文件,数据被包含在请求报文主体中,操作不是幂等的 |
PUT | 从客户端向服务器传送数据取代 URL 指定的资源内容,操作是幂等的(不管进行多少次相同的操作,结果都一样) |
PATCH | 是对 PUT 方法的补充,用来对已知资源进行局部更新 |
DELETE | 请求服务器删除 URL 指定的资源,操作是幂等的 |
OPTIONS | 用来查询服务器针对指定URL 支持的 HTTP 方法 |
TRACE | 让客户端可以看到中间服务器进行了哪些更改或添加,主要用于测试或诊断 |
CONNECT | 将请求连接转换为透明的TCP/IP 隧道,通常是为了通过未加密的 HTTP 代理促进TLS/SSL 加密通信 (HTTPS) |
HTTP/1.1 请求方法中比较基本的有四种:GET、POST、PUT、DELETE(HEAD与PATCH方法可以分别看作GET与PUT方法的补充),可以分别对应访问资源的四种基本操作:查、增、改、删。符合REST(Representational State Transfer,表现层状态转化)互联网软件架构原则的RESTful API 正式通过HTTP的这四种基本请求方法完成对资源或数据对象的增、删、改、查等操作的,关于RESTful 架构设计会在下文给出简单介绍。
在辨析HTTP的四种基本请求方法之前,需要先解释下上表提到的两个概念 — 安全和幂等:
- 安全性:对资源的操作不会影响该资源的状态,也就是说,它仅仅是获取资源信息,就像数据库查询一样,不会修改、增加、删除数据;
- 幂等性:原本是数学上的概念 — 使公式:f(x)=f(f(x)) 能够成立的数学性质。用在编程领域,则意为对同一个系统,使用同样的条件,一次请求和重复的多次请求对系统资源状态的影响是一致的。
HTTP提供的四种基本请求方法中,只有GET方法要求是安全的,POST、PUT、DELETE方法都不是安全的;GET、PUT、DELETE三个方法都是幂等的,只有POST方法不是幂等的。为什么专门区分幂等性呢?举个例子,我们购买商品后支付,支付扣款成功,但是返回结果的时候网络异常,此时钱已经扣了,我们再次点击付款按钮,如果付款请求不是幂等的,那么会进行第二次扣款,如果付款请求是幂等的,多次相同的付款请求跟一次付款请求的结果是一样的,不会进行多次重复扣款。
方法 | 是否安全 | 是否幂等 | RESTful API 操作对应HTTP请求方法 |
---|---|---|---|
GET | 是 | 是 | 查询操作 |
POST | 否 | 否 | 新增操作 |
PUT | 否 | 是 | 更新操作 |
DELETE | 否 | 是 | 删除操作 |
通过安全性与幂等性两个概念,应该可以清晰区分HTTP四种基本方法的使用了,比较容易混淆的是POST方法与PUT方法,这两个方法的本质区别在于是否满足幂等性,POST方法还可以提交URL 所指定资源的子资源(从属资源),比如分别用POST与PUT方法创建资源,POST方法可以只指定集合资源URL(也即只指定父资源URL,待创建子资源的名字可以由服务器分配),PUT方法则必须指定具体资源的URL(待创建子资源的名字需要客户端在URL中指定),很多时候我们要创建的子资源命名交由服务器分配更高效省事(比如可以避免URL重复等),所以在RESTful API 中常使用POST方法来创建资源;PUT方法本就是幂等性替换整个目标资源,因此在RESTful API 中常用PUT方法来更新资源(POST方法不是幂等操作,因此不适合用来更新资源)。
我们访问Web时,最常用的方法是GET和POST,GET方法常用来从服务器获取目标网页资源信息,POST方法常用来向服务器提交资源数据(比如创建新博客、发表新状态或者新评论、上传新文件等)。HTTP/1.1 的这些请求方法自身不带验证机制,存在安全性问题,一般需要配合Web应用程序的验证机制(比如注册并登录自己的账号),用户只能对部分拥有相应权限的资源使用PUT或DELETE方法(比如用户对自己创建的博客或评论才拥有更新或删除的权限)。
请求报文头部字段比较多,下文再详细介绍,请求报文主体一般使用HTML语法来描述资源信息,HTML语法在前篇博客:Web简史与三大技术要素中有过简介,这里也不再赘述了。
2.2 HTTP Response
HTTP响应报文包括状态行、响应头部、空行、报文主体等部分构成,响应行包括协议版本、状态码、状态原因短语三部分,响应头部主要包括响应的各种条件或属性,空行用于分割首部和主体,报文主体则包含应被发送的数据。HTTP响应报文结构与示例如下:
HTTP状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型,后两个数字没有分类的作用,HTTP状态码共分为5种类型:
状态码 | 类别 | 状态原因短语 | 常见状态码 |
---|---|---|---|
1XX | Informational(信息性状态码) | 服务器接收到请求,需要请求者继续执行操作 | 100、101 |
2XX | Success(成功状态码) | 请求已被正确处理完毕 | 200、204、206 |
3XX | Redirection(重定向状态码) | 资源位置已经变动,需要进行附加操作以完成请求 | 301、302、304 |
4XX | Client Error(客户端错误状态码) | 请求报文包含语法错误,服务器无法处理请求 | 400、401、403、404 |
5XX | Server Error(服务器错误状态码) |