在计算机网络中, 应用层是面向普通开发者的接口, 开发者基于应用层相关实现(基于对应的协议)开发各种各样的网络程序和服务. 而作为前端开发者(后端也是), HTTP无疑是接触最多的应用层协议. 技术是不断的发展的, HTTPS作为新一代的协议, 也在互联网领域普及.
HTTP协议(超文本传输协议|HyperText Transfer Protocol)
可用于计算机之间传输文本和二进制文件(比如图片、视频等等). Web开发必用的协议.
HTTP 协议是最重要的互联网基础协议之一,它从最初的仅为浏览网页的目的进化到现在,已经是短连接通信的事实工业标准,最新版本 HTTP/2 更是让它再次成为技术热点。
1.基础概念
这里使用的是通俗理解, 可能和概念的正规概念不一致, 也没那么严谨
- 网络节点: 通俗可以理解一台计算机被视为一个节点, 但是本质上网络节点是由网络适配器决定的(网卡),例如有的笔记本可以连有线网,也可以连无线网. 如果一台笔记本同时连接有线+无线,其实他是两个网络节点,也拥有两个IP. 当然一般连一个
- 服务器: 长期运行这个一个用于响应网络上的其他网络节点的程序(具体说是响应HTTP请求的程序)一种特殊的网络节点(通常是一台使用着Linux系统或者Window系统的计算机)
- 端口: 一台计算机运行同时运行多个程序, 同样的一台服务器也运行多个响应客户端的服务, 为了区分同一台服务器的多个服务, 就相应产生端口的概念用以区分, 可用0 到65535的端口区分最多65535个服务. (这是属于TCP/IP协议的内容,具体是属于传输层还是网络层我也还不晓得, 后面学到对应的位置就知道了,暂时先不管), HTTP绑定在80端口
- 客户端: 当有了服务器之后, 就运行其他网络节点访问访问对应的服务了. 用于访问服务的设备就被称为客户端(可以使计算机/手机/平板等可以上网的设备). 当然更明确一点,也不是说只要是这些设备就可以访问服务了, 还需要对应的软件(可以发出HTTP请求的程序). 最常用的就是浏览器, 电脑, 智能手机都带得有.有的老人机可能没有.
回过头来再解释一下HTTP协议: 用于在服务器和客户端之间通信的一种协议, 支持文本和二进制等内容, 支持双向通信(两者之间可以互相传), 短期回合制, 一般满足一问一答模式: 客户端发出请求, 服务器给出回答并断开.
HTTP协议基于TCP协议, TCP协议会在后续网络的传输层中展开
2.细节展开 HTTP1.1
背景补充, HTTP经历了多个版本的迭代, 功能越来越丰富,且还在不断的发展. 这里主要展开HTTP1.1
用户的某些行为会客户端发送请求, 比如在浏览器上输入一段网址并回车、打开淘宝软件(软件自动发出指向淘宝服务器的请求)、点击某个链接等等. 一会你就会看到服务器返回的内容了.
现在我们需要聊一下用户访问某个网页的到浏览器上展现服务器返回的内容的这个过程中发生的事情, 这里边大部分内容都涉及到HTTP协议
2.1报文
当访问某个网页时, 客户端会发出一个HTTP请求,具体来说是发出一段指定规范的文本, 里边会包含服务器地址,发送时间,请求什么内容等信息.这个指定规范的文本就称为报文, 同理当服务器返回内容给客户端时,也会按照指定的规范返回, 所以, 有两种报文:请求报文, 响应报文
请求报文图片来源: <图解HTTP>(豆瓣) (douban.com)
响应报文图片来源: <图解HTTP>
报文作为HTTP协议传输的载体, 内部几乎规定了服务器和客户端如何通信,如何交互的各种选项, 理解了报文内每个字段的含义, 也就理解了HTTP协议, 尤其是请求头部分(请求报文: 请求行+首部行)/(响应报文: 状态行+首部行)
2.1.1 请求报文
请求报文分4个部分: 请求行、首部行、空行(内容和首部行的分隔符)、实体体(entity body)
2.1.1 请求报文
2.1.1.1 请求行
请求行是HTTP请求报文的第一行,它包含了三个部分:请求方法、请求URI和HTTP协议版本。例如:
GET /path/pic.html HTTP/1.1
- 请求方法:指定了想要执行的动作,如GET、POST等。
- 请求URI:指定了请求的资源的位置。
- 协议版本:指定了使用的HTTP协议的版本,如HTTP/1.1或HTTP/2。
2.1.1.2 请求方法
HTTP/1.1定义了八种请求方法,以下是其中六种:
- GET:请求从服务器获取特定资源。GET请求不应包含请求体。
- POST:请求向服务器提交数据以创建新的资源或引发服务器上的某些行为。POST请求通常包含请求体。
- HEAD:与GET请求类似,但服务器在响应中只返回头部信息,不返回响应体。
- PUT:请求将指定的资源状态存储在请求URI的位置。PUT请求通常包含请求体。
- DELETE:请求删除指定的资源。
- OPTIONS:请求查询服务器的性能或能力,特别是与跨域请求相关的预检请求。
2.1.1.3 首部行
首部行包含了一系列的键值对,它们提供了关于请求或响应的附加信息。例如:
Host: www.baidu.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
...
每个首部行的键值对都以冒号分隔+空格(: ),冒号前是首部名称,冒号后是首部值。
具体参考: 2.2.1 首部行的字段说明
2.1.1.4 实体体
实体体是请求或响应报文的主体部分,包含了实际的资源内容。例如,如果请求方法是POST,实体体可能包含表单数据或文件上传的内容。
2.1.2 响应报文
2.1.2.1 状态行
状态行是HTTP响应报文的第一行,它包含了HTTP协议版本和状态码。例如:
HTTP/1.1 200 OK
- 版本:响应所使用的HTTP协议版本。
- 状态码:表示请求的结果,如200表示成功,404表示未找到资源等。
2.1.2.1.1 响应的状态码
以下是一些常见的HTTP状态码:
- 200 OK:请求成功,响应报文包含所请求的资源。
- 301 Moved Permanently:请求的资源已被永久移动到新位置,Location首部行包含新的URL。
- 400 Bad Request:服务器无法理解请求,通常是因为客户端错误。
- 404 Not Found:服务器上找不到请求的资源。
- 505 HTTP Version Not Supported:服务器不支持请求使用的HTTP协议版本。
2.1.2.2 首部行
响应的首部行与请求的首部行类似,提供了关于响应的附加信息。首部行的例子包括:
Content-Type: text/html; charset=UTF-8
Content-Length: 12345
...
具体参考: 2.2.1 首部行的字段说明
2.1.2.3 实体行
实体行是响应报文的主体部分,包含了服务器返回的资源内容。例如,如果请求的资源是一个HTML页面,实体行将包含该页面的HTML代码。
根据提供的搜索结果,我将详细阐述HTTP首部行中的一些字段及其意义和作用。
2.2首部行字段与HTTP协议的功能
2.2.1 首部行的字段说明
通用首部字段(General Header Fields)
这些字段既可用于请求报文,也可用于响应报文。
当然,以下是HTTP/1.1中各个首部字段的作用和意义的详细解释:
通用首部字段(General Header Fields)
- Cache-Control:控制缓存的行为,如指定缓存的有效期、是否允许缓存等。
- Connection:管理持久连接,如
keep-alive或close。 - Date:表示报文创建的日期和时间。
- Pragma:包含实现特定的指令,如
no-cache。 - Trailer:在报文末尾包含额外的首部字段。
- Transfer-Encoding:指定报文主体的传输编码方式,如
chunked。 - Upgrade:请求客户端和服务器端升级到更高级的协议。
- Via:列出了请求和响应经过的代理服务器。
- Warning:提供了可能影响报文的附加信息,如
199 Miscellaneous warning。
请求首部字段(Request Header Fields)
- Accept:指定客户端能够处理的媒体类型。
- Accept-Charset:指定客户端能够接受的字符集。
- Accept-Encoding:指定客户端能够接受的内容编码。
- Accept-Language:指定客户端偏好的语言。
- Authorization:包含客户端的认证信息。
- Cookie:包含客户端存储的由服务器设置的cookies。
- Expect:指定了服务器必须满足的预期行为。
- From:包含发送请求用户的电子邮件地址。
- Host:指定请求的服务器的域名和端口号。
- If-Match:如果实体标记与请求匹配,则执行请求。
- If-Modified-Since:如果资源在指定时间后被修改,则执行请求。
- If-None-Match:如果实体标记不匹配,则执行请求。
- If-Range:如果范围有效,则执行范围请求。
- If-Unmodified-Since:如果资源在指定时间后未被修改,则执行请求。
- Max-Forwards:限制请求可以经过的代理服务器数量。
- Proxy-Authorization:包含代理服务器的认证信息。
- Range:请求资源的特定范围。
- Referer:包含导致请求的页面的URL。
- TE:指定传输编码的偏好。
- User-Agent:包含发出请求的用户代理信息。
响应首部字段(Response Header Fields)
- Accept-Ranges:指定服务器接受的范围类型。
- Age:从源到代理的响应时间。
- ETag:资源的特定版本的标识符。
- Location:用于重定向的URL。
- Proxy-Authenticate:要求客户端提供代理认证信息。
- Retry-After:建议客户端在指定时间后再次发起请求。
- Server:包含服务器软件的信息。
- Set-Cookie:服务器向客户端设置cookies。
- Vary:决定代理服务器是否需要向源服务器转发请求。
- WWW-Authenticate:要求客户端提供认证信息。
实体首部字段(Entity Header Fields)
- Allow:列出了针对资源的有效请求方法。
- Content-Encoding:指定了实体内容的编码方式。
- Content-Language:指定了实体的自然语言。
- Content-Length:指定了实体主体的大小。
- Content-Location:指定了替代URI以访问实体。
- Content-MD5:提供了实体主体的MD5校验和,用于验证完整性。
- Content-Range:指定了实体主体的范围。
- Content-Type:指定了实体主体的媒体类型。
- Expires:指定了实体的过期时间。
- Last-Modified:指定了资源的最后修改时间。
这些首部字段共同构成了HTTP通信的基础,使得客户端和服务器能够交换必要的信息,以确保数据的正确传输和处理。
2.2.2 负载(报文的实体行)
对于请求类报文, 不是所有的类型的都存在负载的, 只有POST和PUT类型的存在负载. 负载的内容是客户端需要发送给服务器的信息. 这两种请求请求都支持上传哪些类型的数据呢?
请求的Content-Type与上传数据类型
上传数据的类型可以通过Content-Type请求首部字段来指定。以下是一些常见的Content-Type值,它们定义了不同的数据上传类型:
-
application/x-www-form-urlencoded
- 这是最常见的POST数据类型,通常用于表单数据。数据被编码为键值对,类似于查询字符串参数。例如,
key1=value1&key2=value2。 - 浏览器在发送表单时默认使用这种格式。
- 这是最常见的POST数据类型,通常用于表单数据。数据被编码为键值对,类似于查询字符串参数。例如,
-
multipart/form-data
- 用于上传文件和包含文件的表单数据。这种格式支持文件二进制数据的传输,并且可以包含多个文件。
- 它使用
boundary来分隔不同的部分,每个部分都有自己的Content-Disposition来描述数据。
-
text/plain
- 纯文本数据,不包含任何特殊编码或结构。通常用于发送简单的文本信息。
-
application/json
- JSON格式的数据,常用于API请求,其中客户端向服务器发送JSON对象。
- 需要在请求体中包含有效的JSON字符串。
-
application/xml
- XML格式的数据,用于需要XML格式交互的API。
- 需要在请求体中包含有效的XML文档。
-
text/xml
- 与
application/xml类似,但内容类型为纯文本,用于XML数据。
- 与
-
application/octet-stream
- 二进制流数据,用于非文本文件的传输,如图片、视频、PDF等。
-
application/soap+xml
- SOAP协议的数据,用于基于XML的Web服务通信。
-
application/atom+xml
- Atom XML格式的数据,用于RSS和Atom feeds。
-
application/ld+json
- JSON-LD格式的数据,用于链接数据。
-
application/hal+json
- HAL(Hypertext Application Language)是一种表达RESTful API的方式,使用JSON格式。
值得注意的是除了application/octet-stream用户上传二进制数据以外, multipart/form-data也支持上传二进制数据, 同时可通过加将二进制转为BASE64然后使用文本形式传输.
响应的数据类型
响应报文用于服务器回复客户端信息, 所以一般情况下, 响应报文都是有负载的; 个别情况会没有负载:
- 当条件GET请求的时效性满足时, 服务器会响应状态为
304的报文, 这里不会返回负载. head、options请求也是不返回负载的
那响应的负载有哪些类型呢?
以下是HTTP/1.1响应返回数据类型(Content-Type)的详细梳理:
- text/html
- 用于返回HTML文档。
- text/plain
- 用于返回纯文本数据。
- application/json
- 用于返回JSON格式的数据,常用于API响应。
- application/xml
- 用于返回XML格式的数据。
- image/jpeg
- 用于返回JPEG格式的图片。
- image/png
- 用于返回PNG格式的图片。
- audio/mpeg
- 用于返回MP3音频文件。
- video/mp4
- 用于返回MP4视频文件。
- application/pdf
- 用于返回PDF文档。
- application/zip
- 用于返回ZIP压缩文件。
- application/javascript
- 用于返回JavaScript代码。
- application/octet-stream
- 用于返回二进制流数据,常用于文件下载。
- text/csv
- 用于返回CSV格式的数据。
- application/rss+xml
- 用于返回RSS feeds。
- application/atom+xml
- 用于返回Atom feeds。
- text/javascript
- 另一种用于返回JavaScript代码的类型。
- application/vnd.ms-excel
- 用于返回Microsoft Excel文件(旧版)。
- application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
- 用于返回Microsoft Excel文件(新版)。
- application/vnd.ms-powerpoint
- 用于返回Microsoft PowerPoint文件。
这些是常见的HTTP响应数据类型,它们定义了服务器响应的内容类型,使得客户端能够正确地解析和显示响应内容。每种Content-Type都有其特定的用途和格式要求,服务器需要根据实际返回的数据类型设置正确的Content-Type值。
服务器响应返回的数据类型取决于现代操作系统, 这种应用软件所支持的类型. 不至于以上的内容, 具体类型可在使用过程中查询.
2.2.3 Web 缓存
缓存仅针对GET请求, 用于提高获取资源的响应速度. 注意这里提到的缓存, 存储的内容是整个HTTP请求, 和前端领域里的cookie, localStorage 这个由开发者手动控制的数据存储不是同一个概念.
从缓存存在的位置不同可以将缓存分为: 代理服务器缓存、客户端缓存
从缓存的用户所属上可将缓存分为:共享缓存、私有缓存: 如名, 共享缓存供多个用户重复使用, 私有缓存专属于单个用户(安全性更高,可存放一些独属于某个用户的信息)
一般情况下共享缓存存储在代理服务器, 而私有缓存存储在客户端(被部署在叫User Agent的一个组件)
代理服务器(proxy server) 正向代理
涉及以下两个请求头部标识, 具体请往下看
Last-Modified: Wed,9 Sep 2015 09:23:24
HTTP/1.1 304 Not Modified
图片来源:<计算机网络:自顶向下方法> (豆瓣) (douban.com) 2.2.5章节
如图, 代理服务器(一类特殊的服务器, 自己不处理具体业务,仅转发其他服务器的请求来响应客户)是一个在客户端与服务器之间的一个中间网络节点, 它能够代表初始服务器满足客户端发起的HTTP请求(它会存储最近请求过的对象),客户端(一般是浏览器)需要配置代理指向该节点(一定是用户信任的代理服务, 然后才去配置转发).
代理服务器的作用
代理服务器可用于Web缓存以提高服务响应速度, 但是不至于此. 比如VPN可用来直连内网、服务器上做负载均衡等.
衍生: 怎么区分正向代理和反向代理?
正向代理: 需要用户自己建一个代理服务器, 然后在客户端(浏览器等)做配置指向代理服务器; 一般可代理整个互联网, 即所有请求都可以通过代理服务转发.
反向代理: 不需要用户在客户端做任何操作(用户甚至不知道服务器有没有被反向代理过), 反向代理的服务器由服务商自己提供, 也仅代理自己的服务: 比如腾讯有很多服务,本来每一个服务都部署在不同的服务器上, 有自己独立的IP, 然后就可以通过反向代理的方式, 把所有服务聚合到同一台服务器上, 用户只需要知道一个域名就可以访问他家所有的服务.
条件GET方法
使用代理服务缓存请求对象可提高响应速度, 但也导致一个新的问题: 缓存资源和源服务器的资源可能不同步.为了解决不同步问题,条件GET请求出现了.
在代理场景中, 用户发起一个请求,首先会进入代理服务器, 原本的情况下, 如果发起请求命中缓存时会直接返回给用户,这可能会导致缓存资源和源资源不一致. 为了解决这个问题, 现在改成这样的交互: 即使用户请求命中缓存,代理服务器还是会再发一个验证请求给源服务器, 确认一下资源是不是最新的(一般用修改时间判断). 如果是最新的,源服务器只会返回一个确认信息, 如果不是最新的, 源服务器会把最新的资源一并返回给代理服务器. 代理服务器拿到源服务器给的结果后, 在返回给用户.
条件请求报文: 需要在首部行中添加以下字段Last-Modified: Wed,9 Sep 2015 09:23:24,其中的值为缓存请求的最新返回时间
条件请求的响应报文: 如果是最新的, 会在状态行中以状态码304体现, 不返回实体体. 状态行完整示例HTTP/1.1 304 Not Modified
怎么配置代理服务器
网络上有详细的教程, 这里就不展开讨论了. nginx--正向代理、反向代理及负载均衡(图解+配置)_nginx正向代理和反向代理图解-CSDN博客
浏览器缓存
除了使用中间服务器做代理以外, 浏览器自身也可以对请求做缓存, 这是速度最快的缓存(缓存数据就在本地).
私有缓存
私有缓存是绑定到特定客户端的缓存——通常是浏览器缓存。由于存储的响应不与其他客户端共享,因此私有缓存可以存储该用户的个性化响应。
如果你的响应包含个性化内容并且只想存储在私有存储中,则需要在响应的首部行中添加一下字段(虽然一般以cookie存储个性化内容, 但两者并没有必然联系)
Cache-Control: private
特例: 存在Authorization鉴权的请求, 不会被缓存存储, 除非显示指定Cache-Control: public.
共享缓存
共享缓存涵盖内容丰富, 用户处理不同场景的不同需求, 且还存在历史遗留方案. 限于篇幅原因, 这里不过多阐述(一句话,非私有的都属于共享的). 具体可参考HTTP 缓存 - HTTP | MDN (mozilla.org)
缓存总结
HTTP缓存作为提升WEB浏览体验的重要的方案. 整体上是非常灵活的, 且丰富的, 但这也对用开发者好缓存提高了难度, 特别在还有历史遗留方案的基础上.
但从一个使用者的角度, 怎么用好缓存肯定是一个不能回避的问题. 可以从以下角度考虑如何用好缓存.
- 更快的访问速度, 提速是缓存的核心目的
- 安全的使用体验, 针对特定的携带隐私请求, 需要考虑能不能缓存, 能存储在什么位置
- 正确的内容分发, 及时性和准确性, 不要响应给用户过期的内容, 这里本质上是考虑一个请求资源的更新频率
要达到这些目的, 总体方案是挺简单的, 但是细则会很复杂: 对待不同的资源, 需要采用相适应的缓存策略, 用不用缓存, 用那种缓存, 其实我们现在的服务器和客户端已经给出较为通用的缓存策略, 开发者在使用过程中, 是一个进一步调优的过程.
从刚提到的请求访问速度、安全、资源更新频率, 其中的访问速度是我们追求的结果, 安全和更新(修改)频率是资源的所受限制,
基于资源的这两种限制本质, 我们可以把做一个二维分布, 横坐标表示安全需求,纵坐标表示更新频率
现在Cache-Control提供的各种字段无非就是在满足我们精细化的缓存控制策略.
请求取值范围
| 类型 | 指令 | 参数 | 说明 |
|---|---|---|---|
| 表示是否能缓存 | no-cache | 无 | 强制向源服务器再次验证 |
| 控制可执行缓存对象 | no-store | 无 | 不缓存请求或响应的任何内容暗示请求中包含机密 |
| 指定缓存期限和认证 | max-age = [ 秒] | 必需 | 响应的最大Age值缓存资源时间 < 指定值,客户端用缓存值。为0, 不使用缓存。 |
| 指定缓存期限和认证 | max-stale( = [ 秒]) | 可省略 | 接收已过期的响应 |
| 指定缓存期限和认证 | min-fresh = [ 秒] | 必需 | 期望在指定时间内的响应仍有效 |
| 指定缓存期限和认证 | no-transform | 无 | 代理不可更改媒体类型 |
| 指定缓存期限和认证 | only-if-cached | 无 | 从缓存获取资源 |
| cache-control的扩展 | cache-extension | - | 新指令标记(token) |
响应取值范围
| 指令 | 参数 | 说明 | |
|---|---|---|---|
| 表示是否能缓存 | public | 无 | 可向任意方提供响应的缓存其他用户也可利用缓存 |
| 表示是否能缓存 | private | 可省略 | 仅向特定用户返回响应只对特定用户提供缓存 |
| 表示是否能缓存 | no-cache | 可省略Location | 缓存前必须先确认其有效性不缓存请求或响应的任何内容如果取值为Location,仅对Location不缓存,其他缓存。 |
| 控制可执行缓存对象 | no-store | 无 | 不缓存请求或响应的任何内容暗示响应中包含机密内容 |
| 指定缓存期限和认证 | no-transform | 无 | 代理不可更改媒体类型 |
| 指定缓存期限和认证 | must-revalidate | 无 | 可缓存但必须再向源服务器进行确认 |
| 指定缓存期限和认证 | proxy-revalidate | 无 | 要求中间缓存服务器对缓存的响应有效性再进行确认 |
| 指定缓存期限和认证 | max-age = [ 秒] | 必需 | 响应的最大Age值缓存资源时间 < 指定值,客户端用缓存值。 |
| 指定缓存期限和认证 | s-maxage = [ 秒] | 必需 | 公共缓存服务器响应的最大Age值 |
| cache-control的扩展 | cache-extension | - | 新指令标记(token) |
版本和兼容性问题
出于历史原因, 除了Cache-Control这个首部字段以外, 还有其他的字段可以控制缓存行为, 比如Expires或者别的, 他们是在HTTP1.1标准未出现以前, 浏览器软件企业等自发使用和约束的其他缓存方案. 当开发者需要使用这类方案时, 可以将cache-control 设置为no-store 禁用掉最新的默认缓存行为.
2.2.4 Cookie
前人好文章,这里就不赘述了, Cookie 详解HTTP 是无状态的协议,协议本身不保留之前的请求或相应报文的信息,而 Cookie 则是为了解决业务 - 掘金 (juejin.cn)
总结一下用法: Cookie分为服务端管理和客户端管理两种用法
服务端管理cookie 可设置为对前端可见或不可见,当为不可见时, 前端不可以从代码层面查看或修改内容.