这里是《写给前端工程师的 HTTP 系列》,记得有位大佬曾经说过:“大厂前端面试对 HTTP 的要求比 CSS 还要高”,因而可知 HTTP 的重要程度不可小视。文章写做计划以下,视状况可能有必定的删减,本篇是该系列的第 2 篇 —— 《HTTP 协议那些事》。这篇文章会涉及到 HTTP 协议,cookie 和 session,HTTP 首部/方法/状态码等。javascript
更多文章能够关注个人 interview 系列。html
HTTP 协议那些事java
HTTPS / SPDY / HTTP/2 / Websocketgit
JWTgithub
网络安全web
跨域面试
缓存机制算法
浏览器原理express
终章:从输入 url 到页面呈现发生了什么
超文本传输协议(HyperText Transfer Protocol)是基于 TCP/IP 协议,用于分布式、协做式和超媒体信息系统的应用层协议。HTTP 是万维网的数据通讯的基础,它是 无状态
的协议,默认端口为 80。HTTP 在 TCP 的基础上,规定了 Request-Response 的模式,这个模式决定了通信一定由浏览器首先发起。
抛去一些复杂的层面,浏览器开发者只须要一个 TCP 库就能够搞定浏览器的网络通信部分。咱们能够用 telnet
来作个实验。
首先链接到 yanceyleo.com
的主机。
telnet yanceyleo.com 80
复制代码
此时,三次握手完成,TCP 链接已经创建。输入下面内容,并 双击回车,就能够获得服务端响应的内容。下面的报文中,第一行的开头 GET
为请求访问服务器的类型,称为 方法 (method)
;后面的 /
指明了请求访问的资源对象,也叫作请求 URI (request-URI);最后为 HTTP 版本号,用来表示客户端使用的 HTTP 版本。第二行则是请求的主机名。
GET / HTTP/1.1
Host: yanceyleo.com
复制代码
HTTP 是无状态 (stateless) 协议,它不会对请求和响应之间通讯状态进行保存,也就是说 HTTP 协议不会对发送过的请求或响应作持久化处理。使用 HTTP 协议,每当有新的请求发送时,就会有对应的新响应产生。协议自己并不保留以前一切的请求或响应报文信息。这是为了更快地处理大量事务,确保协议的可伸缩性。
无链接
每次链接只处理一个请求,服务端处理完客户端一次请求,等到客户端做出回应以后便断开链接。
无状态
是指服务端对于客户端每次发送的请求都认为它是一个新的请求,上一次会话和下一次会话没有联系。
何为 cookie 呢?咱们在上面了解到 HTTP 是无状态的,但随着 Web 的不断发展,这种 无状态 的特性出现了弊端。当你登陆到一家购物网站,在跳转到该站的其余页面时也应该继续保持登陆状态。可是由于 HTTP 是无状态的,因此必须得在浏览器端存储一些信息来标识当前用户,所以 cookie 应运而生,它一种浏览器管理状态的文件。
浏览器第一次发出请求,服务器会将 cookie 放入到响应请求中,在浏览器第二次发请求的时候,会把 cookie 带过去,因而服务端就会辨别用户身份。注意:单个 cookie 保存的数据不能超过 4K,不少浏览器都限制一个站点最多保存 20 个 cookie。
cookie 在请求头中有一个 cookie
字段,在响应头里有一个 set-cookie
字段。
cookie 自己就是用来保存一些隐私性的字段,基于安全性的考量,必需要保证它是 不可跨域的。咱们能够作个实验:先打开 https://google.com
,而后在开发者工具中输入如下代码:
document.cookie = 'hello=world;path=/;domain=.baidu.com';
document.cookie = 'world=hello;path=/;domain=.google.com';
复制代码
打开 Application 选项卡,在侧边栏找到 Cookies,能够发现只有 domain 为 .google.com
的被成功添加。
咱们经过一个登陆的小例子来了解服务端设置 cookie。首先经过 express application generator 生成一个 Express 工程。本示例的源码请访问 express-cookies。
接着在 index.html 文件中输入如下代码,咱们建立一个输入用户名和密码的界面,在点击按钮的时候,经过 fetch 将输入的值发送给后端。
<fieldset>
<legend>Login</legend>
<input id="userName" type="text" placeholder="请输入用户名" />
<input id="userPwd" type="password" placeholder="请输入密码" />
<button id="loginBtn">登陆</button>
</fieldset>
<p>登陆状态: <span id="result"></span></p>
<script> const userName = document.getElementById('userName'); const userPwd = document.getElementById('userPwd'); const loginBtn = document.getElementById('loginBtn'); const result = document.getElementById('result'); loginBtn.addEventListener('click', function() { const data = { userName: userName.value, userPwd: userPwd.value }; fetch('/login', { method: 'POST', headers: new Headers({ 'Content-Type': 'application/json' }), body: JSON.stringify(data) }) .then(res => { return res.json(); }) .then(json => { result.innerHTML = json.msg; }); }); </script>
复制代码
当用户名和密码匹配时 (假设用户名和密码都是 yancey
),返回给客户端一个 cookie 以及登陆成功的 json;不然返回登陆失败的 json。下面是模拟服务端登陆的接口。
router.post('/login', (req, res, next) => {
const body = req.body;
if (body.userName === 'yancey' && body.userPwd === 'yancey') {
// 设置 cookie
res.cookie('yancey', 'success');
res.json({
success: true,
msg: '登陆成功'
});
} else {
res.status(401).json({
success: false,
msg: '用户名或密码错误'
});
}
});
复制代码
经过这个例子能够看到,在 express 中,setCookie 的方式为:第一个参数传递 name
,第二个参数传递 value
,注意浏览器会将元字符和语义字符以外的字符进行转义。打开 Chrome 的开发者工具,就能够看到该 cookie 被添加到浏览器上了。或者你在控制台输入 document.cookie
,一样能够看到 cookie 字符串。
这只是一个设置 cookie 的简单例子,cookie 有 7 种属性可供使用,咱们一一来了解。
该属性给 cookie 设置 域名
,默认为当前网站的域名,下面的例子将 domain 设为 yanceyleo.com,因为前端页面是 127.0.0.1
,根据同源策略,该条 cookie 不会生效。
res.cookie('domain', 'domian', { domain: 'yanceyleo.com' });
复制代码
这两个属性都是设置 cookie 的 过时时间
。不一样的是,expires
接收一个 Date 格式的时间,而 maxAge
接收一个 毫秒时间戳
。由于后者更加直观和简便,因此建议使用 maxAge
。
两个属性均可以传递一个 负值
或者 0
,若是浏览器已存在同名 cookie,则会清除此 cookie,不然该条 cookie 不会被建立。
下面这个例子是建立一条 cookie,并将该 cookie 的过时时间设为一天后。
res.cookie('expires', 'expires', {
expires: new Date(Date.now() + 24 * 60 * 60 * 1000)
});
复制代码
接着给该条 cookie 设置一个 “负数”,那么这条 cookie 就被清除了。
res.cookie('expires', 'expires', {
expires: new Date(Date.now() - 8 * 60 * 60 * 1000)
});
复制代码
maxAge 的用法同理,它直接传递一个 过时时间
的毫秒数便可。下面的例子是将该条 cookie 的过时时间设为 7 天后。
res.cookie('maxAge', 'maxAge', {
maxAge: 7 * 24 * 60 * 60 * 1000
});
复制代码
那么不设置过时时间的 cookie 会怎样呢?当你关闭该网站的时候,这些没有被设置过时时间的 cookie 就死翘翘了 (这种状况的 cookie 就好像是 session)。
当该属性设为 true 时,document.cookie
将没法获取该条 cookie,但服务端能够照常得到。该属性能够有效的避免跨站脚本攻击 (XSS)。关于网络安全方面的话题,后面会专门写一篇文章去讲。
res.cookie('httpOnly', 'httpOnly', {
// 只能被 web server 访问到,也就是说在浏览器输入 document.cookie 没法取到该条 cookie,目的是防止 xss
httpOnly: true
});
复制代码
该属性给 指定的路径
添加此 cookie,默认为 /
。以下代码就是给 users
这个路由设置 cookie (即使在服务端该路径不存在也会被添加上)。
res.cookie('path', 'path', {
path: '/users'
});
复制代码
只有当链接是 HTTPS 协议,该 cookie 才会被添加。该属性默认为 fasle。由于我本地的 express 是 HTTP 协议,所以该条 cookie 不会生效。
res.cookie('secure', 'secure', {
secure: true
});
复制代码
该属性是给浏览器发送一个加密的 cookie,该属性默认为 false。在 express 中,咱们可使用 cookie-parser
插件来建立一个加密后的 cookie。服务端经过该 cookie 的内容和签名来检验它是否 被篡改
首先给 cookieParser
传入一个 secret。
app.use(cookieParser('forcabarca'));
复制代码
而后返回一个 sign 后的 cookie。
res.cookie('signed', 'signed', {
signed: true
});
复制代码
在 express 中,咱们可使用 req.cookies
来得到 未加密
的 cookie 对象,能够经过 req.signedCookies
来得到 已加密
的 cookie 对象。
console.log(req.cookies); // { httpOnly: 'httpOnly' }
console.log(req.signedCookies); // { signed: 'signed' }
复制代码
document.cookie
字符串转对象的函数关于 cookie 就说这么多,最后附赠一个 document.cookie
字符串转对象的函数,若是你有更好的实现方式,请在下面留言。
const formatCookie = cookies => {
const o = {};
cookies
.split(';')
.forEach(value => (o[value.split('=')[0]] = value.split('=')[1]));
return o;
};
复制代码
session 是服务端使用的一种记录客户端状态的机制,与 cookie 不一样的是,session 保存在 服务端。当客户端初次发送请求时 (好比登陆成功),服务端会将用户信息以某种形式保存在服务端,当再次访问时只需从该 session 中找到该客户的状态便可。
所以,cookie 机制就是经过检查客户身上的 “通行证” 来肯定客户身份,而 session 则是经过检查服务器上的 “客户明细表” 来确认客户身份。session 至关于程序在服务器上创建的一份客户档案,客户来访的时候只须要查询客户档案表就能够了。
由于 HTTP 是无状态的,因此单纯的 session 仍不能判断是否为究竟是哪一个用户。所以服务端仍要向客户端发送一个 maxAge 为 -1
的 cookie 来做为不一样用户的惟一标识。
固然你也能够不使用 cookie,你能够经过重写 URL 地址的方式来实现。它的原理是将用户的 seesion id 写入到 URL 中,当浏览器解析新的 URL 时就能够定位到是哪位用户。
万变不离其宗,两种方式都是要保证用户信息以某种形式保存到客户端。更先进的 localStorage,sessionStorage,IndexedDB 也是一样的道理,这里不去细说。
用于 HTTP 协议交互的信息被称为 HTTP 报文。客户端的报文叫作请求报文,服务端的报文叫作响应报文。HTTP 报文自己是有多行数据构成的字符串文本。
上面这张图清晰地展现了请求报文和响应报文的格式,用文字描述大体以下。
CR (Carriage Return,回车符:16 进制 0x0d)
LF (Line Feed,换行符:16 进制 0x0a)
<!--请求报文-->
<method>空格<request-url>空格<version>
<headers>
空行 (CR + LF)
<entity-body>
<!--响应报文-->
<version>空格<status>空格<reason-phrase>
<headers>
空行 (CR + LF)
<entity-body>
复制代码
HTTP 协议中有一种被称为 内容编码
的功能,能够有效的压缩报文的体积。内容编码指明应用在实体内容上的编码格式,并保持实体信息原样压缩。内容编码后的实体由客户端接收并负责解码。常见的内容编码有如下几种:
identity (不作压缩)
compress (UNIX 系统的标准压缩)
gzip (GNU zip, 最多见)
deflate (zlib)
brotli (Google 出品,必属精品。比 gzip 的压缩率还要高 37%+,个人网站已使用 brotli,看下图)
从 HTTP 请求回来,就产生了流式的数据,后续的 DOM 树构建、CSS 计算、渲染、合成、绘制,都是尽量地流式处理前一步的产出:即不须要等到上一步骤彻底结束,就开始处理上一步的输出,这样咱们在浏览网页时,才会看到逐步出现的页面。
本质上来讲,在 HTTP 通讯过程当中,请求的编码实体资源还没有所有传输完成以前,浏览器没法显示请求页面。在传输大容量数据时,经过把数据分割成多块,能让浏览器逐步显示页面。这种把实体主体分块的功能称为分块传输编码 (Chunked Transfer Code)。
分块传输编码会将实体主体分为多个块,每一个块都会使用十六进制来标记大小,而实体主体的最后一块会使用 0 (CR+LF)
来标记。使用分块传输编码的实体主体会由接收的客户端负责解码,恢复到编码前的实体主体。
上面的章节咱们说到了 HTTP 的报文,它由三部分组成,分别是:报文首部
、空行
、报文主体
。
对于请求报文,它的首部由方法、URL、HTTP 版本、HTTP 首部字段等部分构成。
对于响应报文,它的首部分别由 HTTP 版本、状态码、HTTP 首部字段等部分构成。
通用首部字段 (General Header Field) 请求报文和响应报文两方都会使用的首部。
请求首部字段 (Request Header Field) 从客户端向服务端发送请求报文时使用的首部。补充了请求的附加内容、客户端信息、响应内容相关优先级等信息。
响应首部字段 (Response Header Field) 从服务端向客户端返回响应报文时使用的首部。补充了响应的附加内容,也会要求客户端附加额外的内容信息。
实体首部字段 (Entity Header Field) 针对请求报文和响应报文的实体部分使用的首部。补充了资源内容更新时间等与实体有关的信息。
HTTP 首部字段将定义成缓存代理和非缓存代理的行为,分红 端到端首部
和 逐条首部
。
分到 端到端首部
的首部会转发给请求/响应对应的最终接收目标,且必须保存在由缓存生成的响应中,而且它必须被转发。
分到 逐跳首部
的首部只对单次转发有效,会因经过缓存或代理而再也不转发。在 HTTP/1.1 以后的版本,若是使用逐跳首部,则须要提供 Connection 首部字段。其中 Connection、Keep-Alive、Proxy-Authenticate、、Proxy-Authorization、Trailer、TE、Transfer-Encoding、Upgrade 这 8 个为逐跳首部,其他都为端到端首部。
该字段用于控制缓存的工做机制,它接受多个参数,中间用逗号隔开。
指令 | 参数 | 类型 | 说明 |
---|---|---|---|
no-cache | 无 | 请求/响应都有该字段 | 若请求中包含该字段,则表示客户端不接受缓存;若服务端包含该字段,缓存前必须先确认其有效性 |
no-store | 无 | 请求/响应都有该字段 | 不缓存请求或相应的任何内容。no-cache 响应其实是能够存储到本地缓存区中的,而 no-store 才是本地完全不缓存 |
max-age | 单位为秒,必需 | 请求/响应都有该字段 | 当缓存时间小于该值时,客户端接受缓存的资源,不然请求源服务器,该指令的优先级高于 Expires |
max-state | 单位为秒,可省略参数 | 只有请求拥有该字段 | 只要有该字段,客户端就能够接受过时的缓存 |
min-fresh | 单位为秒,必需 | 只有请求拥有该字段 | 该指令要求缓存服务器返回至少还未过指定时间的缓存资源 |
no-transform | 无 | 请求/响应都有该字段 | 不管在请求仍是响应中,都不容许缓存改变实体主体的媒体类型 |
only-if-cached | 无 | 只有请求拥有该字段 | 表示客户端仅在缓存服务器本地缓存目标资源的状况下才会要求去返回 |
cache-extension | - | 请求/响应都有该字段 | 新指令扩展 |
public | 无 | 只有响应拥有该字段 | 可向任意客户端提供相应的缓存 |
private | 可省略 | 只有响应拥有该字段 | 仅向特定用户返回响应 |
must-revalidate | 无 | 只有响应拥有该字段 | 可缓存,但必须再向源服务器进行一次验证 |
proxy-revalidate | 无 | 只有响应拥有该字段 | 要求中间缓存服务器对缓存的响应有效性再进行确认 |
s-maxage | 单位为秒,必需 | 只有响应拥有该字段 | 与 max-age 相比,该指令仅适用于公共服务器 |
Connection 用于控制再也不转发给代理的首部字段,还能够管理持久链接。HTTP/1.1 默认是持久链接,当服务端明确表示断开链接时,则将 Connection 设为 Close
。
Date 表示建立报文的日期和时间,它的格式以下。
date: Sun, 05 May 2019 02:05:37 GMT
复制代码
该字段会事先说明在报文主体后记录了哪些首部字段,可应用于 HTTP/1.1 分块传输编码。
该字段规定了传输报文主体时采用的编码方式,HTTP/1.1 的传输编码方式仅对分块传输编码有效。
该字段用于检测 HTTP 协议或者其余协议是否可使用更高的版本通讯,该字段要和 Connection 字段一块儿使用。下面的例子是询问是否可使用 TLS/1.0 协议。对于附有 Upgrade 字段的请求,服务端可返回 101 的状态码。
connection: upgrade
upgrade: TLS/1.0
复制代码
该字段用于追踪客户端与服务器之间请求和响应报文的传输路径。
该字段通知服务器,用户代理可以处理的媒体类型及媒体类型的相对优先级。其中用 q 表示权重。下面的例子表示客户端能够接受纯文本类型或者 HTML 类型,而且接收纯文本类型的意愿 (权重)为 0.3。
Accept: text/plain; q=0.3, text/html
复制代码
该字段通知服务器,用户代理支持的字符集及字符集的相对优先级。该字段应用于内容协商机制的服务器驱动协商。若是服务器不能提供该字段的任何字符集,会报 406 错误,所以尽可能不去使用该字段 (我试验了几个网站,都没有此字段)。下面的例子表示客户端支持 utf-8 和 iso-8859-1,且优先使用 utf-8。
Accept-Charset: utf-8, iso-8859-1;q=0.5
复制代码
该字段告知服务端,客户端可以使用的头部压缩算法。上面 压缩报文
已经介绍了几种压缩方式,这里不在赘述。
Accept-Encoding: gzip, deflate, br
复制代码
该字段用于告知服务器,用户代理的认证信息。下面是我博客后台管理系统的一个场景,在请求一个须要认证的接口时,须要在请求头上附带认证信息。
Authorization: Bearer JWT_TOKEN
复制代码
客户端使用 Expect 来告知服务器,指望出现的某种特定行为。当服务器没法理解客户端的指望而发生错误时,会返回 417 状态码。
该字段跟状态码 100 息息相关,等待状态码 100 响应的客户端在发生请求时,须要指定 Expext: 100-continue
。该状态码的用途主要是容许客户端发送带请求体的请求前,判断服务器是否愿意接收请求。
Expect: 100-continue
复制代码
该字段用来告知服务器使用用户代理的用户的 Email。
当以单台服务器分配多个域名的虚拟主机时,Host 字段就能够用来肯定相应的主机。
Host: www.abc.com
复制代码
形如 If-xxx
的请求字段均可称为条件请求。服务器在收到该类请求后,只有判断条件为真时才会执行请求。
服务器会比对 If-Match 的字段值和资源的 ETag 值,仅当二者一致时,才会执行请求,不然返回 412 状态码。当 If-Match 的字段值为 *
时,服务器会忽略 ETag 值,只要资源存在就处理请求。
If-Match: W/"pqxe5g29m4"
复制代码
与 If-Match 相反,服务器会比对 If-None-Match 的字段值和资源的 ETag 值,仅当二者 不一致
时,才会执行请求。在 GET 和 HEAD 方法中使用该字段会获取最新资源。
若是在 If-Modified-Since 字段指定的日期时间后,资源发生了更新,服务器会接受请求。若是资源没更新过,则返回 304 状态码。
该字段值和响应首部字段的 Last-Modifie 字段作比较,下面的例子中显然最后修改时间要新于 If-Modified-Since 的时间,所以会响应新的资源。
// 请求首部字段
If-Modified-Since: Fri, 01 May 2019 11:20:04 GMT
// 响应首部字段
Last-Modified: Fri, 03 May 2019 11:20:04 GMT
复制代码
若是在 If-Modified-Since 字段指定的日期时间后,资源 未发生
更新,服务器才会接受请求。若是资源在此以后发生了更新,则报 412 错误。
该字段值跟 相应头中的 ETag 或 Date 进行比较,若一致,就做为范围请求处理,并返回状态码 206,不然直接返回所有资源。
对于只需获取部分资源的范围请求,包含首部字段 Range 便可告知服务器资源的指定范围。接收到附带 Range 字段的请求的服务器,会在处理请求以后返回状态码为 206 的响应。当没法处理该范围请求时,返回 200 状态码及所有资源。
Range: bytes=5001-10000
复制代码
该字段用于告知代理服务器,用户代理的认证信息。
告知服务器请求的 URI 是从哪儿发起的。好比在个人博客 www.yanceyleo.com 请求了 AliOSS 上的一张图片,那么请求 AliOSS 服务器的那个请求头就会附上:
Referer: https://www.yanceyleo.com
复制代码
固然该单词正确的拼写应该是 referrer
,但 referer
却沿用至今。想起一句歌词:“在漫天风沙里,望着你远去,我竟悲伤的不能本身 (已)。”
该字段会告知服务端,客户端可以处理响应的传输编码方式及相对优先级。它相似于 Accept-Encoding,但用于传输编码。除了指定传输编码,还能够指定伴随 trailer 字段的分块传输编码方式。
TE: gzip, delate;q=0.5
TE: trailers 复制代码
这个字段再不认识直接回炉重造吧,这里不去赘述,直接看例子。
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36
复制代码
该字段用于告知客户端,服务器是否能处理范围请求,可处理时指定为 bytes
,不然为 none
。
Accept-Ranges: bytes
复制代码
该字段用于告知客户端,源服务器在多久前建立了响应,字段值的单位为秒。若建立该响应的服务器是缓存服务器,Age 值指的是缓存后的响应再次发起认证到认证完成的时间值 (CDN)。
Age: 500
复制代码
ETag 是将资源以字符串的形式作惟一性标识,服务器会为每份资源分配对应的 ETag 值。当资源更新时,ETag 值也须要更新。
ETag 有 强 Etag 值
和 弱 Etag 值
之分。前者是指不管实体发生多么细微的变化都会改变其值。而弱 ETag 只用于提示资源是否相同。只有资源发生了根本变化,产生差别时才会改变 ETag 值,弱 ETag 字段值前面会有 W
标识。前者就比如使用了 {deep: true}
同样。
下面的代码是一张图片的 ETag,显然一张图片改变意味着资源的完全改变,所以使用了强 ETag。
ETag: "F8F155B13C6DA43723EEDE3EDBBB4D28"
复制代码
下面的代码是请求一个数据接口的 ETag,大多数状况不会发生根本性的改变,所以使用弱 ETag。
etag: W/"300af-7JrdwEcHHeXMqn/UCrMO0zsO0SY"
复制代码
Location 字段能够将响应接收方引导至某个与请求 URI 位置不一样的资源,该字段通常会配合 3xx 的状态码使用。
Location: https://yanceyleo.com
复制代码
该字段会把由代理服务器所要求的认证信息发送给客户端。
该字段告知客户端应该在多久以后再次发送请求。当服务器出错报 503 时,若是服务端知道何时能够恢复,那么就应该经过该字段告知客户端。该字段的字段值能够是具体的日期时间,也能够是建立响应后的秒数。
Retry-After: Sat, 04 May 2019 11:26:52 GMT
复制代码
该字段也是一个常见字段,用于告知客户端,Web 服务器的名称。好比我使用了 cloudflare 的 CDN,所以服务器以下所示。
server: cloudflare
复制代码
该字段可用于对缓存进行控制,它的字段值接收一系列其余首部字段名。
vary: Accept-Encoding,Cookie
复制代码
上面这个例子中,源服务器向代理服务器发送了 vary 字段,代理服务器若要进行缓存,只能对 Accept-Encoding 和 Cookie 进行缓存。
该字段告知客户端适用于访问请求 URI 所指定资源的认证方案和带参数提示的质询。
该字段会告知客户端所支持的全部 HTTP 请求方法,当服务端接收到不支持的 HTTP 方法时,会返回 405 状态码,并将全部能支持的 HTTP 方法写入首部字段。
Allow: GET, PUT
复制代码
告知客户端服务器使用的内容编码方式。
content-encoding: br
复制代码
告知客户端实体主体使用的天然语言。
content-language: zh-CN
复制代码
该字段代表了实体主体部分的大小,单位是字节。
Content-Length: 4871261
复制代码
该字段用于检查报文主体在传输过程当中是否保持完整性,以及确认传输到达。服务端对报文主体执行 MD5 算法,获取一个 128 位的二进制数,再经过 base64 编码后将结果写入 Content-MD5 字段值。由于 HTTP 首部没法记录二进制值,所以须要经过 Base64 进行处理。客户端在接收到响应后再对报文主体执行一次相同的 MD5 算法。将计算值于该字段值比较,便可判断出报文主体的准确性。
Content-MD5: +PFVsTxtpDcj7t4+27tNKA==
复制代码
该字段告知客户端做为响应返回的实体的哪一个部分符合范围请求,字段值以字节为单位。
很是常见的字段,用来讲明实体主体内对象的媒体类型。
content-type: application/json; charset=utf-8
复制代码
该字段将资源失败的日期告诉客户端,在 Expires 指定的时间以前,响应的副本会一直被保存。当超过指定的时间后,缓存服务器在请求发送过来时,转向源服务器请求资源。当首部字段 Cache-Control 有指定的 max-age 时,会优先处理 max-age。
关于缓存机制下一章会详细去讲。
该字段指明资源的最终修改时间,通常来说,该值就是 Request-URI 指定资源的被修改的时间。
方法名 | 描述 |
---|---|
GET | GET 请求会显示请求指定的资源。通常来讲 GET 方法应该只用于数据的读取,而不该当用于会产生反作用的非幂等的操做中。它指望的应该是并且应该是安全的和幂等的。这里的安全指的是,请求不会影响到资源的状态。 |
HEAD | HEAD 方法与 GET 方法同样,都是向服务器发出指定资源的请求。可是,服务器在响应 HEAD 请求时不会回传资源的内容部分,即:响应主体。这样,咱们能够不传输所有内容的状况下,就能够获取服务器的响应头信息。HEAD 方法常被用于客户端查看服务器的性能。 |
PUT | PUT 请求会身向指定资源位置上传其最新内容,PUT 方法是幂等的方法。经过该方法客户端能够将指定资源的最新数据传送给服务器取代指定的资源的内容。 |
POST | POST 请求会 向指定资源提交数据,请求服务器进行处理,如:表单数据提交、文件上传等,请求数据会被包含在请求体中。POST 方法是非幂等的方法,由于这个请求可能会建立新的资源或/和修改现有资源。 |
TRACE | TRACE 请求服务器回显其收到的请求信息,该方法主要用于 HTTP 请求的测试或诊断。 |
OPTIONS | OPTIONS 请求与 HEAD 相似,通常也是用于客户端查看服务器的性能。 这个方法会请求服务器返回该资源所支持的全部 HTTP 请求方法,该方法会用'*'来代替资源名称,向服务器发送 OPTIONS 请求,能够测试服务器功能是否正常。JavaScript 的 XMLHttpRequest 对象进行 CORS 跨域资源共享时,就是使用 OPTIONS 方法发送嗅探请求,以判断是否有对指定资源的访问权限。 |
DELETE | DELETE 请求用于请求服务器删除所请求 URI(统一资源标识符,Uniform Resource Identifier)所标识的资源。DELETE 请求后指定资源会被删除,DELETE 方法也是幂等的。 |
PATCH | PATCH 方法出现的较晚,它在 2010 年的 RFC 5789 标准中被定义。PATCH 请求与 PUT 请求相似,一样用于资源的更新。两者有如下两点不一样:1.PATCH 通常用于资源的部分更新,而 PUT 通常用于资源的总体更新。2.当资源不存在时,PATCH 会建立一个新的资源,而 PUT 只会对已在资源进行更新。 |
CONNECT | CONNECT 方法是 HTTP/1.1 协议预留的,可以将链接改成管道方式的代理服务器。一般用于 SSL 加密服务器的连接与非加密的 HTTP 代理服务器的通讯。 |
GET,HEAD,PUT 和 DELETE 是幂等方法,而 POST 不是幂等的。
HTTP 状态码负责表示客户端 HTTP 请求的返回结果、标记服务器端的处理是否正常、通知出现的错误等工做。
状态码 | 状态码英文名称 | 描述 |
---|---|---|
100 | Continue | 服务器收到请求的初始部分,请客户端继续。 |
101 | Switching Protocols | 服务器根据客户端请求切换协议 |
1xx 的状态码表示一个临时的响应,仅由状态行和可选头构成,由空行结尾。对该类状态码,不须要头部。该类状态码在 HTTP/1.1 引入,所以服务器禁止向 HTTP1.0 的客户端响应 1xx 状态码。
对于 100 (Continue) 状态码,客户端应该继续它的请求。这个过渡的响应用于告知客户端,请求的初始部分已经被服务器收到,而且没有被服务器拒绝。客户端应该继续发送剩余的请求,若是请求已经完成,就忽略这个响应。服务器必须在请求完成后发送一个最终的响应。
100 状态码的用途主要是,容许客户端发送带请求体的请求前,判断服务器是否愿意接收请求 (经过请求头)。在某些状况下,若是服务器在不看请求体的状况下就拒绝请求时,客户端仍然发送请求体是不恰当的或低效的。
状态码 | 状态码英文名称 | 描述 |
---|---|---|
200 | OK | 请求成功,响应主体包含了具体的数据。最多见,通常 GET 和 POST 请求会返回此状态码。 |
201 | Created | 已建立,通常 PUT 请求会返回此状态码。 |
202 | Accepted | 服务器已接收到请求,但还未处理完成。 |
203 | Non-Authoritative Information | 非受权信息。请求成功,但元信息不在原始服务器上,而是资源的一个副本。若中间节点上有一份资源副本,但没法或没有对它发出的与资源有关的元信息进行验证,就会出现这种状况。 |
204 | No Content | 响应报文中无主体部分。通常 DELETE 请求会返回此状态码。 |
205 | Reset Content | 负责告知浏览器清除当前页面中全部 HTML 元素。 |
206 | Partial Content | 成功执行一个部分或 Range 请求。客户端能够在首部中指定请求某个范围内的文件。该状态响应头部必须包含 Content-Range、Date、以及 ETag 或 Content-Location。 |
206 状态码通常是在下载大文件时会遇到,它表示请求已成功,而且主体包含所请求的数据区间,该数据区间是在请求的 Range 首部指定。下图中,个人博客在获取音频文件时返回了 206 状态码。
状态码 | 状态码英文名称 | 描述 |
---|---|---|
300 | Multiple Choices | 客户端请求实际指向多个资源的 URL。客户端能够在响应中找到资源列表。 |
301 | Moved Permanently | 请求的 URL 已被移除。响应的 Location 首部包含如今所处的位置。 |
302 | Found | 与 301 相似,客户端本次应使用响应中的临时 URL,未来的请求任使用之前的 URL。 |
303 | See Other | 告知客户端使用另外一个 URL 来获取资源。其主要目的是,容许 POST 请求的响应将客户端定向的某一个资源上去。 |
304 | Not Modified | 若客户端发起一个有条件的 GET 请求,而资源未被修改,可使用该状态码说明资源未被修改。 |
305 | Use Proxy | 必须经过代理来访问这一资源,代理有 Location 首部给出。须要知道的是,客户端接收到这一状态时,不该该假定全部请求都通过代理。 |
307 | Temporary Redirect | 和 302 相同。 |
状态码 | 状态码英文名称 | 描述 |
---|---|---|
400 | Bad Request | 告知客户端它发送了一个错误的请求。 |
401 | Unauthorized | 与适当首部一同返回,告知客户端在请求以前先进行认证。 |
403 | Forbidden | 请求被拒绝。 |
404 | Not Found | 服务器没法找到请求的 URL。 |
405 | Method Not Allowed | 客户端使用不支持的方法请求 URL。应该在首部使用 Allow 告知客户端正确的方法。 |
406 | Not Acceptable | 服务器端没法提供与 Accept-Charset 以及 Accept-Language 消息头指定的值相匹配的响应 |
407 | Proxy Authentication Required | 代理服务器要求客户端验证。 |
408 | Request Timeout | 客户端完成请求时间过长,服务器能够关闭连接。 |
409 | Conflict | 服务器认为该请求可能引发冲突。响应主体中应包含冲突的主体的描述。 |
410 | Gone | 与 404 相似,只是服务器曾经拥有此资源,后来被移除。 |
411 | Length Required | 服务器要求请求报文中包含 Content-Length 首部。 |
412 | Precondition Failed | 客户端发起条件请求,其中有条件失败。 |
413 | Request Entity Too LargeRequest Entity Too Large | 客户端发送的主体部分比服务器可以活但愿处理的要大。 |
414 | Request URI Too Long | URL 过长。 |
415 | Unsupported Media Type | 服务器没法理解或没法支持客户端发送的内容类型。 |
416 | Requested Range Not Satisfiable | 请求范围无效或没法知足。 |
417 | Expectation Failed | 请求首部包含 Expect 指望,但服务器没法知足。 |
429 | Too Many Requests | 短期内发送了太多请求 |
431 | Request Header Fields Too Large | 请求头太大 |
状态码 | 状态码英文名称 | 描述 |
---|---|---|
500 | Internal Server Error | 服务器遇到一个妨碍它提供服务的错误。 |
501 | Not Implemented | 客户端发起的请求超出服务器能力范围,如使用了不支持的方法。 |
502 | Bad Gateway | 无效网关。一般不是这上游服务器关闭,而是使用了上游服务器不一样意协议交换数据。 |
503 | Service Unavailable | 服务器暂时没法提供服务。若服务器知道服务什么时间可使用,能够在响应头中加入 Retry-After 首部说明。 |
504 | Gateway Timeout | 于 408 相似,只是这里的响应来自一个网关或代理,它们在等待另外一个服务器响应对其请求响应时超时。 |
505 | HTTP Version Not Support | 服务器收到的请求使用了它没法支持的协议版本。 |
这一篇文章主要探讨了 HTTP 协议以及它的 无链接、无状态
性,从而引出了 cookie 和 session。接着介绍了 HTTP 的头部、方法、状态码。下一篇会着重讲解 HTTP 协议的缓存,敬请期待。
欢迎关注个人微信公众号:进击的前端
《图解 HTTP》 -- 上野 宣