关注公众号“执鸢者”,回复“红宝书”获取“javaScript高级程序第四版(pdf)”及大量前端学习资料。

跨域问题一直是面试中的经典问题,不管是前端老鸟还是新鸟都碰到过。其中针对跨源Ajax请求中有一个终极解决办法——CORS(跨源资源共享)大家肯定也不陌生,一说这个名词,我们就会哗啦哗啦说出来一套又一套的理论知识,但是这些理论知识很多我们做的仅仅是去背诵,很少去验证每一个理论点,本节我们将通过实验的方式去验证这些理论点,通过理论与实践相结合的方式彻底理解CORS。

一、理论知识

既然是CORS,背背这些理论点肯定不为过吧,我就用三幅图对这个理论进行一些简单的总结

1.1 请求类型

1.1.1 简单请求

1.1.2 非简单请求

1.2 请求如何带上Cookie信息

二、实验

为实验做好前期准备工作,包含一个html页面和一个服务器程序,其中html访问网址为http://127.0.0.1:8009; 服务器监听端口为:8010.
  1. html页面初始代码
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>test CORS</title>
    </head>
    <body>
        CORS
        <script src="https://code.bdstatic.com/npm/axios@0.20.0/dist/axios.min.js"></script>
        <script>
            axios('http://127.0.0.1:8010', {
                method: 'get'
            }).then(console.log)
        </script>
    </body>
</html>
  1. 服务器端代码(用express框架)
const express = require('express');

const app = express();

app.get('/', (req, res) => {
    console.log('get请求收到了!!!');
    res.send('get请求已经被处理');
})
app.listen(8010, () => {
    console.log('8010 is listening')
});

2.1 实验一

实验目的:

  1. 非同源会产生跨域问题
  2. 跨域是浏览器对响应拦截造成的
  1. 首先来看看浏览器控制台内容

控制台内容显示报错,报错内容是跨域,这是因为端口不同(一个8009一个8010),两者不同源,所以导致跨域,验证了实验目的1.
  1. 紧接着来瞅瞅服务器控制台打印了啥内容

控制台内容打印了接收到了get请求,则证明浏览器的请求发出了并被服务器端正常接收,从侧面证明了跨域是浏览器对响应进行了拦截,从而验证了实验目的2.

2.2 实验二

实验目的

  1. 服务器配置Access-Control-Allow-Origin会解决跨域问题
  2. 浏览器通过响应头中是否包含Access-Control-Allow-Origin这个响应头的值与请求头中Origin是否相等来确定是否能够进行跨域访问
  1. 首先来搂一眼请求头

  1. 紧接着再来搂一眼响应头

按照理论来说,请求头中的Origin字段表示本次请求来自哪个源(协议+域名+端口),服务器根据这个值来决定是否同意这次请求。如果Origin指定的源不在许可范围内,服务器会返回一个正常的HTTP回应。目前并没有修改,还处于报错状态,该响应确实是一个正常响应,不包含Access-Control-Allow-Origin字段,但是这也不足以说服我浏览器是通过验证该字段来确实是否允许跨域请求。所以紧接着需要做一个对比试验,通过修改服务端的代码后观察响应头内容。
  1. 从最简单的修改开始,直接将响应头中加入Access-Control-Allow-Origin=“http://127.0.0.1:8009” 字段,理论上来说此时会允许所有的跨域请求。
服务端代码修改后内容
app.get('/', (req, res) => {
    console.log('get请求收到了!!!');
    res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:8009');
    res.send('get请求已经被处理');
})
响应头内容

观察到响应头中多了一行内容:Access-Control-Allow-Origin:http://127.0.0.1:8009 字段,在看看响应内容,确实有消息返回了,内容如下:

  1. 只验证了Origin和Access-Control-Allow-Origin中内容响应,若内容不同又会有什么现象呢?
服务端代码进一步修改
app.get('/', (req, res) => {
    console.log('get请求收到了!!!');
    res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:8008');
    res.send('get请求已经被处理');
})
响应头内容

此时浏览器控制台报错了,出现了跨域问题

通过该实验可以验证通过配置Access-Control-Allow-Origin字段可以解决跨域问题;此外,浏览器是通过检查响应头中Access-Control-Allow-Origin字段的值与Origin的值是否相等来确定是否允许跨域访问的。通过该实验达到了我们实验的目的。

2.3 实验三

实验目的
验证CORS请求默认不发送Cookie信息,如果要把Cookie发送到服务器,一方面要服务器同意(通过指定Access-Control-Allow-Origin字段且Access-Control-Allow-Origin需要指定具体域名);另一方面浏览器请求中必须带上withCredentials字段。
  1. 通过观察请求头(看实验一中请求头),并不包含Cookie信息
  2. 代码修改
index.html页面进行修改
axios('http://127.0.0.1:8010', {
    method: 'get',
    withCredentials: true
}).then(console.log)
服务器端代码修改
app.get('/', (req, res) => {
    console.log('get请求收到了!!!');
    console.log('cookie 内容为', req.headers.cookie);
    res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:8009');
    res.setHeader('Access-Control-Allow-Credentials', true);
    res.cookie('test', 'test', {expires: new Date(Date.now() + 900000)});
    res.send('get请求已经被处理');
})
  1. 再次观察请求头内容,带上了cookie

  1. 看看服务器端有没有接收到cookie信息,控制台信息如下,确实收到了cookie信息

按照上述进行配置发送请求过程中将会带着cookie信息,上一个配置将会报错(可以自行验证)

2.4 实验四

实验目的

  1. 验证非简单请求会增加一次预检请求
  2. 预检请求是Options请求
  3. 请求头中会携带非简单请求的请求方法(Access-Control-Request-Methods)和头信息(Access-Control-Request-Headers),预检请求的响应头信息中Access-Control-Allow-Methods和Access-Control-Allow-Headers与上述请求头中的信息匹配才可以发送正常的CORS请求。
  1. 第一步肯定是要修改代码了
index.html代码
axios('http://127.0.0.1:8010', {
    method: 'post',
    headers: {
        'Content-Type': 'application/json'
    },
    data: {
        name: 'dog'
    }
}).then(console.log)
服务器代码修改如下
app.options('/', (req, res) => {
    console.log('options请求收到了!!!');
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
    res.setHeader('Access-Control-Max-Age', 10000);
    res.send('options请求已经被处理');
});

app.post('/', (req, res) => {
    console.log('post请求收到了!!!');
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.send('post请求已经被处理');
});
  1. 修改完了代码是不是要瞅瞅结果呢?
先看看浏览器是不是输出了内容,确实有内容输出了

再瞅瞅服务器输出了些什么内容

可以看到本来打算发一次请求,但实际上发了两条,第一条是Options请求,第二条请求才是post请求,上述打印内容验证了实验目的中的一和二。

  1. 下面继续深入思考,来看看预检请求的请求头和响应头
请求头内容

响应头内容

上述Access-Control-Request-Headers与Access-Control-Allow-Headers一样,而且内容也正常返回了(步骤二中已经进行了展示),但是这不足以证明实验目的三,下面我们认为增加一条头信息再来看结果。

  1. 人为增加一条请求头信息
index.html页面修改后如下
axios('http://127.0.0.1:8010', {
    method: 'post',
    headers: {
        'Content-Type': 'application/json',
        'Test': 'test'
    },
    data: {
        name: 'dog'
    }
}).then(console.log)
此时浏览器控制台报错了

服务端只接收到了options请求

请求头信息为

响应头信息为

通过该实验证明只有Access-Control-Request-Headers与Access-Control-Allow-Headers相等的时候,预检请求才会通过,后续请求才会发出,从而达到了该实验的实验目的三。

1.如果觉得这篇文章还不错,来个分享、点赞吧,让更多的人也看到

2.关注公众号执鸢者,领取学习资料,定期为你推送原创深度好文

image

image.png


执鸢者
1.7k 声望2.5k 粉丝

摸摸头,编程使我快乐。