认识 HTTP

HTTP 是一种 超文本传输协议(Hypertext Transfer Protocol)

超文本传输协议可以进行文字分割:超文本(Hypertext)、传输(Transfer)、协议(Protocol),它们之间的关系如下

什么是超文本

什么是超文本

在互联网早期的时候,我们输入的信息只能保存在本地,无法和其他电脑进行交互。我们保存的信息通常都以文本即简单字符的形式存在,文本是一种能够被计算机解析的有意义的二进制数据包。而随着互联网的高速发展,两台电脑之间能够进行数据的传输后,人们不满足只能在两台电脑之间传输文字,还想要传输图片、音频、视频,甚至点击文字或图片能够进行超链接的跳转,那么文本的语义就被扩大了,这种语义扩大后的文本就被称为超文本(Hypertext)

什么是传输

那么我们上面说到,两台计算机之间会形成互联关系进行通信,我们存储的超文本会被解析成为二进制数据包,由传输载体(例如同轴电缆,电话线,光缆)负责把二进制数据包由计算机终端传输到另一个终端的过程,称为传输(transfer)

通常我们把传输数据包的一方称为请求方,把接到二进制数据包的一方称为应答方。请求方和应答方可以进行互换,请求方也可以作为应答方接受数据,应答方也可以作为请求方请求数据。

什么是协议

网络协议就是网络中(包括互联网)传递、管理信息的一些规范。如同人与人之间相互交流是需要遵循一定的规矩一样,计算机之间的相互通信需要共同遵守一定的规则,这些规则就称为网络协议。

网络模型

应用层

应用层是网络应用程序和网络协议存放的分层,因特网的应用层包括许多协议,例如我们学 web 离不开的 HTTP,电子邮件传送协议 SMTP、端系统文件上传协议 FTP、还有为我们进行域名解析的 DNS 协议。应用层协议分布在多个端系统上,一个端系统应用程序与另外一个端系统应用程序交换信息分组,我们把位于应用层的信息分组称为 报文(message)

运输层

因特网的运输层在应用程序断点之间传送应用程序报文,在这一层主要有两种传输协议 TCPUDP,利用这两者中的任何一个都能够传输报文,不过这两种协议有巨大的不同。

TCP 向它的应用程序提供了面向连接的服务,它能够控制并确认报文是否到达,并提供了拥塞机制来控制网络传输,因此当网络拥塞时,会抑制其传输速率。

UDP 协议向它的应用程序提供了无连接服务。它不具备可靠性的特征,没有流量控制,也没有拥塞控制。我们把运输层的分组称为 报文段(segment)

网络层

因特网的网络层负责将称为 数据报(datagram) 的网络分层从一台主机移动到另一台主机。网络层一个非常重要的协议是 IP 协议,所有具有网络层的因特网组件都必须运行 IP 协议,IP 协议是一种网际协议,除了 IP 协议外,网络层还包括一些其他网际协议和路由选择协议,一般把网络层就称为 IP 层,由此可知 IP 协议的重要性。

链路层

现在我们有应用程序通信的协议,有了给应用程序提供运输的协议,还有了用于约定发送位置的 IP 协议,那么如何才能真正的发送数据呢?为了将分组从一个节点(主机或路由器)运输到另一个节点,网络层必须依靠链路层提供服务。链路层的例子包括以太网、WiFi 和电缆接入的 DOCSIS 协议,因为数据从源目的地传送通常需要经过几条链路,一个数据包可能被沿途不同的链路层协议处理,我们把链路层的分组称为 帧(frame)

物理层

虽然链路层的作用是将帧从一个端系统运输到另一个端系统,而物理层的作用是将帧中的一个个 比特 从一个节点运输到另一个节点,物理层的协议仍然使用链路层协议,这些协议与实际的物理传输介质有关,例如,以太网有很多物理层协议:关于双绞铜线、关于同轴电缆、关于光纤等等。

五层网络协议的示意图如下:

OSI 模型

我们上面讨论的计算网络协议模型不是唯一的 协议栈,ISO(国际标准化组织)提出来计算机网络应该按照7层来组织,那么7层网络协议栈与5层的区别在哪里?

从图中可以一眼看出,OSI 要比上面的网络模型多了 表示层会话层,其他层基本一致。表示层主要包括数据压缩和数据加密以及数据描述,数据描述使得应用程序不必担心计算机内部存储格式的问题,而会话层提供了数据交换的定界和同步功能,包括建立检查点和恢复方案。

 

与 HTTP 有关的协议

在互联网中,任何协议都不会单独的完成信息交换,HTTP 也一样。虽然 HTTP 属于应用层的协议,但是它仍然需要其他层次协议的配合完成信息的交换,那么在完成一次 HTTP 请求和响应的过程中,需要哪些协议的配合呢?一起来看一下

TCP/IP

TCP/IP 协议你一定听过,TCP/IP 我们一般称之为协议簇,什么意思呢?就是 TCP/IP 协议簇中不仅仅只有 TCP 协议和 IP 协议,它是一系列网络通信协议的统称。而其中最核心的两个协议就是 TCP / IP 协议,其他的还有 UDP、ICMP、ARP 等等,共同构成了一个复杂但有层次的协议栈。

TCP 协议的全称是 Transmission Control Protocol 的缩写,意思是传输控制协议,HTTP 使用 TCP 作为通信协议,这是因为 TCP 是一种可靠的协议,而可靠能保证数据不丢失。

IP 协议的全称是 Internet Protocol 的缩写,它主要解决的是通信双方寻址的问题。IP 协议使用 IP 地址 来标识互联网上的每一台计算机,可以把 IP 地址想象成为你手机的电话号码,你要与他人通话必须先要知道他人的手机号码,计算机网络中信息交换必须先要知道对方的 IP 地址。(关于 TCP 和 IP 更多的讨论我们会在后面详解)

DNS

你有没有想过为什么你可以通过键入 www.google.com 就能够获取你想要的网站?我们上面说到,计算机网络中的每个端系统都有一个 IP 地址存在,而把 IP 地址转换为便于人类记忆的协议就是 DNS 协议

DNS 的全称是域名系统(Domain Name System,缩写:DNS),它作为将域名和 IP 地址相互映射的一个分布式数据库,能够使人更方便地访问互联网。

URI / URL

我们上面提到,你可以通过输入 www.google.com 地址来访问谷歌的官网,那么这个地址有什么规定吗?我怎么输都可以?AAA.BBB.CCC 是不是也行?当然不是的,你输入的地址格式必须要满足 URI 的规范。

URI的全称是(Uniform Resource Identifier),中文名称是统一资源标识符,使用它就能够唯一地标记互联网上资源。

URL的全称是(Uniform Resource Locator),中文名称是统一资源定位符,也就是我们俗称的网址,它实际上是 URI 的一个子集。

URI 不仅包括 URL,还包括 URN(统一资源名称)

 

HTTP 请求响应过程

你是不是很好奇,当你在浏览器中输入网址后,到底发生了什么事情?你想要的内容是如何展现出来的?让我们通过一个例子来探讨一下,我们假设访问的 URL 地址为 http://www.someSchool.edu/someDepartment/home.index,当我们输入网址并点击回车时,浏览器内部会进行如下操作

  • DNS服务器会首先进行域名的映射,找到访问www.someSchool.edu所在的地址,然后HTTP 客户端进程在 80 端口发起一个到服务器 www.someSchool.edu 的 TCP 连接(80 端口是 HTTP 的默认端口)。在客户和服务器进程中都会有一个套接字与其相连。
  • HTTP 客户端通过它的套接字向服务器发送一个 HTTP 请求报文。该报文中包含了路径 someDepartment/home.index 的资源,我们后面会详细讨论 HTTP 请求报文。
  • HTTP 服务器通过它的套接字接受该报文,进行请求的解析工作,并从其存储器(RAM 或磁盘)中检索出对象 www.someSchool.edu/someDepartment/home.index,然后把检索出来的对象进行封装,封装到 HTTP 响应报文中,并通过套接字向客户进行发送。
  • HTTP 服务器随即通知 TCP 断开 TCP 连接,实际上是需要等到客户接受完响应报文后才会断开 TCP 连接。
  • HTTP 客户端接受完响应报文后,TCP 连接会关闭。HTTP 客户端从响应中提取出报文中是一个 HTML 响应文件,并检查该 HTML 文件,然后循环检查报文中其他内部对象。
  • 检查完成后,HTTP 客户端会把对应的资源通过显示器呈现给用户。

至此,键入网址再按下回车的全过程就结束了。上述过程描述的是一种简单的请求-响应全过程,真实的请求-响应情况可能要比上面描述的过程复杂很多。

 

HTTP 协议主要由三大部分组成:

  • 起始行(start line):描述请求或响应的基本信息;
  • 头部字段(header):使用 key-value 形式更详细地说明报文;
  • 消息正文(entity):实际传输的数据,它不一定是纯文本,可以是图片、视频等二进制数据。

其中起始行和头部字段并成为 请求头 或者 响应头,统称为 Header;消息正文也叫做实体,称为 body。HTTP 协议规定每次发送的报文必须要有 Header,但是可以没有 body,也就是说头信息是必须的,实体信息可以没有。而且在 header 和 body 之间必须要有一个空行(CRLF),如果用一幅图来表示一下的话,我觉得应该是下面这样

HTTP 请求方法

HTTP 请求方法一般分为 8 种,它们分别是

  • GET 获取资源,GET 方法用来请求访问已被 URI 识别的资源。指定的资源经服务器端解析后返回响应内容。也就是说,如果请求的资源是文本,那就保持原样返回;

  • POST 传输实体,虽然 GET 方法也可以传输主体信息,但是便于区分,我们一般不用 GET 传输实体信息,反而使用 POST 传输实体信息,

  • PUT 传输文件,PUT 方法用来传输文件。就像 FTP 协议的文件上传一样,要求在请求报文的主体中包含文件内容,然后保存到请求 URI 指定的位置。

    但是,鉴于 HTTP 的 PUT 方法自身不带验证机制,任何人都可以上传文件 , 存在安全性问题,因此一般的 W eb 网站不使用该方法。若配合 W eb 应用程序的验证机制,或架构设计采用REST(REpresentational State Transfer,表征状态转移)标准的同类 Web 网站,就可能会开放使用 PUT 方法。

  • HEAD 获得响应首部,HEAD 方法和 GET 方法一样,只是不返回报文主体部分。用于确认 URI 的有效性及资源更新的日期时间等。

  • DELETE 删除文件,DELETE 方法用来删除文件,是与 PUT 相反的方法。DELETE 方法按请求 URI 删除指定的资源。

  • OPTIONS 询问支持的方法,OPTIONS 方法用来查询针对请求 URI 指定的资源支持的方法。

  • TRACE 追踪路径,TRACE 方法是让 Web 服务器端将之前的请求通信环回给客户端的方法。

  • CONNECT 要求用隧道协议连接代理,CONNECT 方法要求在与代理服务器通信时建立隧道,实现用隧道协议进行 TCP 通信。主要使用 SSL(Secure Sockets Layer,安全套接层)和 TLS(Transport Layer Security,传输层安全)协议把通信内容加 密后经网络隧道传输。

HTTP 请求 URL

HTTP 协议使用 URI 定位互联网上的资源。正是因为 URI 的特定功能,在互联网上任意位置的资源都能访问到。URL 带有请求对象的标识符。在上面的例子中,浏览器正在请求对象 /somedir/page.html 的资源。

我们再通过一个完整的域名解析一下 URL

比如 http://www.example.com:80/path/to/myfile.html?key1=value1&key2=value2#SomewhereInTheDocument 这个 URL 比较繁琐了吧,你把这个 URL 搞懂了其他的 URL 也就不成问题了。

首先出场的是 http

http://告诉浏览器使用何种协议。对于大部分 Web 资源,通常使用 HTTP 协议或其安全版本,HTTPS 协议。另外,浏览器也知道如何处理其他协议。例如, mailto: 协议指示浏览器打开邮件客户端;ftp:协议指示浏览器处理文件传输。

第二个出场的是 主机

www.example.com 既是一个域名,也代表管理该域名的机构。它指示了需要向网络上的哪一台主机发起请求。当然,也可以直接向主机的 IP address 地址发起请求。但直接使用 IP 地址的场景并不常见。

第三个出场的是 端口

我们前面说到,两个主机之间要发起 TCP 连接需要两个条件,主机 + 端口。它表示用于访问 Web 服务器上资源的入口。如果访问的该 Web 服务器使用HTTP协议的标准端口(HTTP为80,HTTPS为443)授予对其资源的访问权限,则通常省略此部分。否则端口就是 URI 必须的部分。

上面是请求 URL 所必须包含的部分,下面就是 URL 具体请求资源路径

第四个出场的是 路径

/path/to/myfile.html 是 Web 服务器上资源的路径。以端口后面的第一个 / 开始,到 ? 号之前结束,中间的 每一个/ 都代表了层级(上下级)关系。这个 URL 的请求资源是一个 html 页面。

紧跟着路径后面的是 查询参数

?key1=value1&key2=value2 是提供给 Web 服务器的额外参数。如果是 GET 请求,一般带有请求 URL 参数,如果是 POST 请求,则不会在路径后面直接加参数。这些参数是用 & 符号分隔的键/值对列表。key1 = value1 是第一对,key2 = value2 是第二对参数

紧跟着参数的是锚点

#SomewhereInTheDocument 是资源本身的某一部分的一个锚点。锚点代表资源内的一种“书签”,它给予浏览器显示位于该“加书签”点的内容的指示。 例如,在HTML文档上,浏览器将滚动到定义锚点的那个点上;在视频或音频文档上,浏览器将转到锚点代表的那个时间。值得注意的是 # 号后面的部分,也称为片段标识符,永远不会与请求一起发送到服务器。

 

响应状态码

首先出现的应该就是 200 OK,这是 HTTP 响应标头的状态码,它表示着响应成功完成。HTTP 响应标头的状态码有很多,并做了如下规定

2xx 为开头的都表示请求成功响应。

状态码含义
200 成功响应
204 请求处理成功,但是没有资源可以返回
206 对资源某一部分进行响应,由Content-Range 指定范围的实体内容。

3xx 为开头的都表示需要进行附加操作以完成请求

状态码含义
301 永久性重定向,该状态码表示请求的资源已经重新分配 URI,以后应该使用资源现有的 URI
302 临时性重定向。该状态码表示请求的资源已被分配了新的 URI,希望用户(本次)能使用新的 URI 访问。
303 该状态码表示由于请求对应的资源存在着另一个 URI,应使用 GET 方法定向获取请求的资源。
304 该状态码表示客户端发送附带条件的请求时,服务器端允许请求访问资源,但未满足条件的情况。
307 临时重定向。该状态码与 302 Found 有着相同的含义。

4xx 的响应结果表明客户端是发生错误的原因所在。

状态码含义
400 该状态码表示请求报文中存在语法错误。当错误发生时,需修改请求的内容后再次发送请求。
401 该状态码表示发送的请求需要有通过 HTTP 认证(BASIC 认证、DIGEST 认证)的认证信息。
403 该状态码表明对请求资源的访问被服务器拒绝了。
404 该状态码表明服务器上无法找到请求的资源。

5xx 为开头的响应标头都表示服务器本身发生错误

状态码含义
500 该状态码表明服务器端在执行请求时发生了错误。
503 该状态码表明服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。

 

 

 

HTTP 内容协商

什么是内容协商

在 HTTP 中,内容协商是一种用于在同一 URL 上提供资源的不同表示形式的机制。内容协商机制是指客户端和服务器端就响应的资源内容进行交涉,然后提供给客户端最为适合的资源。内容协商会以响应资源的语言、字符集、编码方式等作为判断的标准。

内容协商的种类

内容协商主要有以下3种类型:

  • 服务器驱动协商(Server-driven Negotiation)

这种协商方式是由服务器端进行内容协商。服务器端会根据请求首部字段进行自动处理

  • 客户端驱动协商(Agent-driven Negotiation)

这种协商方式是由客户端来进行内容协商。

  • 透明协商(Transparent Negotiation)

是服务器驱动和客户端驱动的结合体,是由服务器端和客户端各自进行内容协商的一种方法。

内容协商的分类有很多种,主要的几种类型是 Accept、Accept-Charset、Accept-Encoding、Accept-Language、Content-Language

一般来说,客户端用 Accept 头告诉服务器希望接收什么样的数据,而服务器用 Content 头告诉客户端实际发送了什么样的数据。

为什么需要内容协商

我们为什么需要内容协商呢?在回答这个问题前我们先来看一下 TCP 和 HTTP 的不同。

在 TCP / IP 协议栈里,传输数据基本上都是 header+body 的格式。但 TCP、UDP 因为是传输层的协议,它们不会关心 body 数据是什么,只要把数据发送到对方就算是完成了任务。

而 HTTP 协议则不同,它是应用层的协议,数据到达之后需要告诉应用程序这是什么数据。当然不告诉应用这是哪种类型的数据,应用也可以通过不断尝试来判断,但这种方式无疑十分低效,而且有很大几率会检查不出来文件类型。

所以鉴于此,浏览器和服务器需要就数据的传输达成一致,浏览器需要告诉服务器自己希望能够接收什么样的数据,需要什么样的压缩格式,什么语言,哪种字符集等;而服务器需要告诉客户端自己能够提供的服务是什么。

所以我们就引出了内容协商的几种概念,下面依次来进行探讨

内容协商标头

Accept

接受请求 HTTP 标头会通告客户端自己能够接受的 MIME 类型

那么什么是 MIME 类型呢?在回答这个问题前你应该先了解一下什么是 MIME

MIME: MIME (Multipurpose Internet Mail Extensions) 是描述消息内容类型的因特网标准。MIME 消息能包含文本、图像、音频、视频以及其他应用程序专用的数据。

也就是说,MIME 类型其实就是一系列消息内容类型的集合。那么 MIME 类型都有哪些呢?

文本文件: text/html、text/plain、text/css、application/xhtml+xml、application/xml

图片文件: image/jpeg、image/gif、image/png

视频文件: video/mpeg、video/quicktime

应用程序二进制文件: application/octet-stream、application/zip

比如,如果浏览器不支持 PNG 图片的显示,那 Accept 就不指定image/png,而指定可处理的 image/gif 和 image/jpeg 等图片类型。

一般 MIME 类型也会和 q 这个属性一起使用,q 是什么?q 表示的是权重,来看一个例子

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

这是什么意思呢?若想要给显示的媒体类型增加优先级,则使用 q= 来额外表示权重值,没有显示权重的时候默认值是1.0 ,我给你列个表格你就明白了

qMIME
1.0 text/html
1.0 application/xhtml+xml
0.9 application/xml
0.8 * / *

也就是说,这是一个放置顺序,权重高的在前,低的在后,application/xml;q=0.9 是不可分割的整体。

Accept-Charset

Accept-charset 属性规定服务器处理表单数据所接受的字符编码;Accept-charset 属性允许你指定一系列字符集,服务器必须支持这些字符集,从而得以正确解释表单中的数据。

Accept-Charset 没有对应的标头,服务器会把这个值放在 Content-Type中用 charset=xxx来表示,

例如,浏览器请求 GBK 或 UTF-8 的字符集,然后服务器返回的是 UTF-8 编码,就是下面这样

Accept-Charset: gbk, utf-8
Content-Type: text/html; charset=utf-8

Accept-Language

首部字段 Accept-Language 用来告知服务器用户代理能够处理的自然语言集(指中文或英文等),以及自然语言集的相对优先级。可一次指定多种自然语言集。和 Accept 首部字段一样,按权重值 q= 来表示相对优先级。

Accept-Language: en-US,en;q=0.5

Accept-Encoding

表示 HTTP 标头会标明客户端希望服务端返回的内容编码,这通常是一种压缩算法。Accept-Encoding 也是属于内容协商 的一部分,使用并通过客户端选择 Content-Encoding 内容进行返回。

即使客户端和服务器都能够支持相同的压缩算法,服务器也可能选择不压缩并返回,这种情况可能是由于这两种情况造成的:

  • 要发送的数据已经被压缩了一次,第二次压缩并不会导致发送的数据更小
  • 服务器过载,无法承受压缩带来的性能开销,通常,如果服务器使用 CPU 超过 80% ,Microsoft 则建议不要使用压缩

下面是 Accept-Encoding 的使用方式

Accept-Encoding: gzip
Accept-Encoding: compress
Accept-Encoding: deflate
Accept-Encoding: br
Accept-Encoding: identity
Accept-Encoding: *
Accept-Encoding: deflate, gzip;q=1.0, *;q=0.5

上面的几种表述方式就已经把 Accept-Encoding 的属性列全了

  • gzip: 由文件压缩程序 gzip 生成的编码格式,使用 Lempel-Ziv编码(LZ77)和32位CRC的压缩格式,感兴趣的同学可以读一下 (https://en.wikipedia.org/wiki/LZ77_and_LZ78#LZ77)

  • compress: 使用Lempel-Ziv-Welch(LZW)算法的压缩格式,有兴趣的同学可以读 (https://en.wikipedia.org/wiki/LZW)

  • deflate: 使用 zlib 结构和 deflate 压缩算法的压缩格式,参考 (https://en.wikipedia.org/wiki/Zlib) 和 (https://en.wikipedia.org/wiki/DEFLATE)

  • br: 使用 Brotli 算法的压缩格式,参考 (https://en.wikipedia.org/wiki/Brotli)

  • 不执行压缩或不会变化的默认编码格式

  • * : 匹配标头中未列出的任何内容编码,如果没有列出 Accept-Encoding ,这就是默认值,并不意味着支

    持任何算法,只是表示没有偏好

  • ;q= 采用权重 q 值来表示相对优先级,这点与首部字段 Accept 相同。

Content-Type

Content-Type 实体标头用于指示资源的 MIME 类型。作为响应,Content-Type 标头告诉客户端返回的内容的内容类型实际上是什么。Content-type 有两种值 : MIME 类型和字符集编码,例如

Content-Type: text/html; charset=UTF-8

在某些情况下,浏览器将执行 MIME 嗅探,并且不一定遵循此标头的值;为防止此行为,可以将标头 X-Content-Type-Options 设置为 nosniff。

Content-Encoding

Content-Encoding 实体标头用于压缩媒体类型,它让客户端知道如何进行解码操作,从而使客户端获得 Content-Type 标头引用的 MIME 类型。表示如下

Content-Encoding: gzip
Content-Encoding: compress
Content-Encoding: deflate
Content-Encoding: identity
Content-Encoding: br
Content-Encoding: gzip, identity
Content-Encoding: deflate, gzip

Content-Language

Content-Language 实体标头用于描述面向受众的语言,以便使用户根据用户自己的首选语言进行区分。例如

Content-Language: de-DE
Content-Language: en-US
Content-Language: de-DE, en-CA

下面根据内容协商对应的请求/响应标头,我列了一张图供你参考,注意其中 Accept-Charset 没有对应的 Content-Charset ,而是通过 Content-Type 来表示。

HTTP CROS 跨域

CROS 的全称是 Cross-Origin Resource Sharing(CROS),中文译为 跨域资源共享,它是一种机制。是一种什么机制呢?它是一种让运行在一个域(origin)上的 Web 应用被准许访问来自不同源服务器上指定资源的机制。在搞懂这个机制前,你需要线了解什么是 域(origin)

Origin

Web 概念中域(Origin) 的内容由scheme(protocol) - 协议host(domain) - 主机和用于访问它的 URL port - 端口定义。仅仅当 scheme 、host、port 都匹配时,两个对象才有相同的来源。这种协议相同,域名相同,端口相同的安全策略也被称为 同源策略(Same Origin Policy)。某些操作仅限于具有相同来源的内容,可以使用 CORS 取消此限制。

跨域的特点

  • 下面是跨域问题的例子,看看你是否清楚什么是跨域了
(1) http://example.com/app1/index.html
(2) http://example.com/app2/index.html

上面这两个 URL 是否具有跨域问题呢?

上面两个 URL 是不具有跨域问题的,因为这两个 URL 具有相同的协议(scheme)主机(host)

  • 那么下面这两个是否具有跨域问题呢?
http://Example.com:80
http://example.com

这两个 URL 也不具有跨域问题,为什么不具有,端口不一样啊。其实它们两个端口是一样的。

或许你会认为这两个 URL 是不一样的,放心,关于一样不一样的论据我给你抛出来了

协议和域名部分是不区分大小写的,但是路径部分则根据服务器平台而定。Windows 和 Mac OS X 系统是不区分大小写的,而采用UNIX和Linux系的服务器系统是区分大小写的,

也就是说上面的 Example.comexample.com 其实是一个网址,并且由于两个地址具有相同的 scheme 和 host ,默认情况下服务器通过端口80传递 HTTP 内容,所以上面这两个地址也是相同的。

  • 下面这两个 URL 地址是否具有跨域问题?
http://example.com/app1
https://example.com/app2

这两个 URL 的 scheme 不同,所以这两个 URL 具有跨域问题

  • 再看下面这三个 URL 是否具有跨域问题
http://example.com
http://www.example.com
http://myapp.example.com

这三个 URL 也是具有跨域问题的,因为它们隶属于不通服务器的主机 host。

  • 下面这两个 URL 是否具有跨域问题
http://example.com
http://example.com:8080

这两个 URL 也是具有跨域问题,因为这两个 URL 的默认端口不一样。

同源策略

处于安全的因素,浏览器限制了从脚本发起跨域的 HTTP 请求。 XMLHttpRequest 和其他 Fetch 接口 会遵循 同源策略(same-origin policy)。也就是说使用这些 API 的应用程序想要请求相同的资源,那么他们应该具有相同的来源,除非来自其他来源的响应包括正确的 CORS 标头也可以。

同源策略是一种很重要的安全策略,它限制了从一个来源加载的文档或脚本如何与另一个来源的资源进行交互。 它有助于隔离潜在的恶意文档,减少可能的攻击媒介。

我们上面提到,如果两个 URL 具有相同的协议、主机和端口号(如果指定)的话,那么两个 URL 具有相同的来源。下面有一些实例,你判断一下是不是具有相同的来源

目标来源 http://store.company.com/dir/page.html

URLOutcomeReason
http://store.company.com/dir2/other.html 相同来源 只有path不同
http://store.company.com/dir/inner/another.html 相同来源 只有path不同
https://store.company.com/page.html 不同来源 协议不通
http://store.company.com:81/dir/page.html 不同来源 默认端口不同
http://news.company.com/dir/page.html 不同来源 主机不同

现在我带你认识了两遍不同的源,现在你应该知道如何区分两个 URL 是否属于同一来源了吧!

好,你现在知道了什么是跨域问题,现在我要问你,哪些请求会产生跨域请求呢?这是我们下面要讨论的问题

跨域请求

跨域请求可能会从下面这几种请求中发出:

  1. 调用 XMLHttpRequest 或者 Fetch api。

XMLHttpRequest 是什么?(我是后端程序员,前端不太懂,简单解释下,如果解释的不好,还请前端大佬们不要胖揍我)

所有的现代浏览器都有一个内置的 XMLHttpReqeust 对象,这个对象可以用于从服务器请求数据。

XMLHttpReqeust 对于开发人员来说很重要,XMLHttpReqeust 对象可以用来做下面这些事情

  • 更新网页无需重新刷新页面
  • 页面加载后从服务器请求数据
  • 页面加载后从服务端获取数据
  • 在后台将数据发送到服务器

使用 XMLHttpRequest(XHR) 对象与服务器进行交互,你可以从 URL 检索数据从而不必刷新整个页面,这使网页可以更新页面的一部分,而不会中断用户的操作。XMLHttpRequest 在 AJAX 异步编程中使用很广泛。

再来说一下 Fetch API 是什么,Fetch 提供了请求和响应对象(以及其他网络请求)的通用定义。它还提供了相关概念的定义,例如 CORS 和 HTTP Origin 头语义,并在其他地方取代了它们各自的定义。

  1. Web 字体(用于 CSS 中@ font-face中的跨域字体使用),以便服务器可以部署 TrueType 字体,这些字体只能由允许跨站点加载和使用的网站使用。
  2. WebGL 纹理
  3. 使用 drawImage() 绘制到画布上的图像/视频帧
  4. 图片的 CSS 形状

跨域功能概述

跨域资源共享标准通过添加新的 HTTP 标头来工作,这些标头允许服务器描述允许哪些来源从 Web 浏览器读取信息。另外,对于可能导致服务器数据产生副作用的 HTTP 请求方法(尤其是 GET 或者具有某些 MIME 类型 POST 方法以外 HTTP 方法),该规范要求浏览器预检请求,使用 HTTP OPTIONS 请求方法从服务器请求受支持的方法,然后在服务器批准后发送实际请求。服务器还可以通知客户端是否应与请求一起发送凭据(例如 Cookies 和 HTTP 身份验证)。

注意:CORS 故障会导致错误,但是出于安全原因,该错误的详细信息不适用于 JavaScript。 所有代码都知道发生了错误。 确定具体出问题的唯一方法是查看浏览器的控制台以获取详细信息。

访问控制

下面我会和大家探讨三种方案,这些方案都演示了跨域资源共享的工作方式。所有这些示例都使用XMLHttpRequest,它可以在任何支持的浏览器中发出跨站点请求。

简单请求

一些请求不会触发 CORS预检(关于预检我们后面再介绍)。简单请求是满足一下所有条件的请求

  • 允许以下的方法:GETHEADPOST

  • 除了由用户代理自动设置的标头(例如 Connection、User-Agent 或者在 Fetch 规范中定义为禁止标头名称的其他标头)外,唯一允许手动设置的标头是那些 Fetch 规范将其定义为 CORS安全列出的请求标头 ,它们是:

    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type(下面会介绍)
    • DPR
    • Downlink
    • Save-Data
    • Viewport-Width
    • Width
  • Content-Type 标头的唯一允许的值是

    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain
  • 没有在请求中使用的任何 XMLHttpRequestUpload 对象上注册事件侦听器;这些可以使用XMLHttpRequest.upload 属性进行访问。

  • 请求中未使用 ReadableStream对象。

    例如,假定 web 内容 https://foo.example 想要获取 https://bar.other 域的资源,那么 JavaScript 中的代码可能会像下面这样写

    const xhr = new XMLHttpRequest();
    const url = 'https://bar.other/resources/public-data/';
    
    xhr.open('GET', url);
    xhr.onreadystatechange = someHandler;
    xhr.send(); 

这使用 CORS 标头来处理特权,从而在客户端和服务器之间执行某种转换。

posted on 2020-02-04 18:20  印记XP  阅读(263)  评论(0编辑  收藏  举报