跨域问题一直是面试中的经典问题,不管是前端老鸟还是新鸟都碰到过。其中针对跨源Ajax请求中有一个终极解决办法——CORS(跨源资源共享)大家肯定也不陌生,一说这个名词,我们就会哗啦哗啦说出来一套又一套的理论知识,但是这些理论知识很多我们做的仅仅是去背诵,很少去验证每一个理论点,本节我们将通过实验的方式去验证这些理论点,通过理论与实践相结合的方式彻底理解CORS。
既然是CORS,背背这些理论点肯定不为过吧,我就用三幅图对这个理论进行一些简单的总结
为实验做好前期准备工作,包含一个html页面和一个服务器程序,其中html访问网址为http://127.0.0.1:8009; 服务器监听端口为:8010.
<!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>
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')
});
实验目的:
非同源会产生跨域问题
跨域是浏览器对响应拦截造成的
控制台内容显示报错,报错内容是跨域,这是因为端口不同(一个8009一个8010),两者不同源,所以导致跨域,验证了实验目的1.
控制台内容打印了接收到了get请求,则证明浏览器的请求发出了并被服务器端正常接收,从侧面证明了跨域是浏览器对响应进行了拦截,从而验证了实验目的2.
实验目的
服务器配置
Access-Control-Allow-Origin会解决跨域问题浏览器通过响应头中是否包含
Access-Control-Allow-Origin这个响应头的值与请求头中Origin是否相等来确定是否能够进行跨域访问
按照理论来说,请求头中的Origin字段表示本次请求来自哪个源(协议+域名+端口),服务器根据这个值来决定是否同意这次请求。如果Origin指定的源不在许可范围内,服务器会返回一个正常的HTTP回应。目前并没有修改,还处于报错状态,该响应确实是一个正常响应,不包含
Access-Control-Allow-Origin字段,但是这也不足以说服我浏览器是通过验证该字段来确实是否允许跨域请求。所以紧接着需要做一个对比试验,通过修改服务端的代码后观察响应头内容。
服务端代码修改后内容
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 字段,再看看响应内容,确实有消息返回了,内容如下:
服务端代码进一步修改
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的值是否相等来确定是否允许跨域访问的。通过该实验达到了我们实验的目的。
实验目的 验证CORS请求默认不发送Cookie信息,如果要把Cookie发送到服务器,一方面要服务器同意(通过指定
Access-Control-Allow-Origin字段且Access-Control-Allow-Origin需要指定具体域名);另一方面浏览器请求中必须带上withCredentials字段。
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请求已经被处理');
})
按照上述进行配置发送请求过程中将会带着cookie信息,上一个配置将会报错(可以自行验证)
实验目的
验证非简单请求会增加一次预检请求
预检请求是Options请求
请求头中会携带非简单请求的请求方法(
Access-Control-Request-Methods)和头信息(Access-Control-Request-Headers),预检请求的响应头信息中Access-Control-Allow-Methods和Access-Control-Allow-Headers与上述请求头中的信息匹配才可以发送正常的CORS请求。
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请求已经被处理');
});
先看看浏览器是不是输出了内容,确实有内容输出了
再瞅瞅服务器输出了些什么内容
可以看到本来打算发一次请求,但实际上发了两条,第一条是Options请求,第二条请求才是post请求,上述打印内容验证了实验目的中的一和二。
请求头内容
响应头内容
上述
Access-Control-Request-Headers与Access-Control-Allow-Headers一样,而且内容也正常返回了(步骤二中已经进行了展示),但是这不足以证明实验目的三,下面我们认为增加一条头信息再来看结果。
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相等的时候,预检请求才会通过,后续请求才会发出,从而达到了该实验的实验目的三。