系列文章:
- 【小哥哥, 跨域要不要了解下】JSONP
- 【小哥哥, 跨域要不要了解下】CORS 基础篇
- 【小哥哥, 跨域要不要了解下】CORS 进阶篇
- 【小哥哥, 跨域要不要了解下】NGINX 反向代理
- 【小哥哥, 跨域要不要了解下】ServerProxy
在前一篇文章中, 我们一起学习了第一种跨域处理方案
JSONP
. 这种方法相对比较原始, 优点是兼容性好, 就连现代前端没怎么听说过的IE 6
上跑起来都是妥妥的. 然鹅, 它也就这一点优点了. 其缺点有: 只支持 GET 请求, 配置繁琐(前后端都需要调整代码), 在 window 上注册各种回调函数, 开发体验差....
CORS
由于 JSONP 的方案存在诸多缺点且老旧, 这里我们一起学习一种比较现代的跨域问题解决方案---CORS
兼容性
从 mdn 官网粘来的兼容性列表如下:
ie 10 都可以跑, 足以满足现代前端开发者的需求了.
概念 😳
概念性的东西在这儿 MDN 偶尔需要梯子, 自备哈.
搭建跨域的环境
我们先创建一个跨域的环境, 代码基于我们 jsonp 时候的示例项目 cross-domain, 首先, 在 fe 和 be 目录下创建 cors 目录. 其次, 分别添加 index.html
和 index.js
. 修改以后的项目目录如下图.
编写前端代码 fe/cors/index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>CORS 实现跨域</title>
</head>
<body>
<h3>CORS 实现跨域</h3>
<script>
var xhr = new XMLHttpRequest()
xhr.open('GET', 'http://localhost:8888')
xhr.onreadystatechange = function() {
if(xhr.readyState === 4 && xhr.status === 200) {
console.log(xhr.responseText)
}
}
xhr.send()
</script>
</body>
</html>
一个灰常简单的 ajax 请求, 有木有.
后端代码 be/cors/index.js
const http = require('http');
const PORT = 8888;
// 创建一个 http 服务
const server = http.createServer((request, response) => {
response.end("{name: 'quanquan', friend: 'guiling'}");
});
// 启动服务, 监听端口
server.listen(PORT, () => {
console.log('服务启动成功, 正在监听: ', PORT);
});
此时的项目代码
找点苗头
代码环境准备完成后
- 首先启动后端代码
node ./be/cors/index.js
- 其次启动前端 web 容器
live-server ./fe/cors
- 打开浏览器, 访问 http://localhost:8080/
- 打开控制台, 切换到 Console tab
- 刷新浏览器
我们细细分析这个熟悉的报错, 前一段告诉我们我们的请求被 block 了. 后边居然直接告诉我们解决方案了, 方案了
'Access-Control-Allow-Origin' header is present on the requested resource.
, 这么明显的暗示, 难道我们就不试试???
响应头添加 Access-Control-Allow-Origin
针对浏览器的报错, 我们分析出他是要我们在响应头上添加Access-Control-Allow-Origin
这个字段,
那么我们修改我们的后端代码.
const http = require('http');
const PORT = 8888;
// 创建一个 http 服务
const server = http.createServer((request, response) => {
response.setHeader('Access-Control-Allow-Origin', '*');
response.end("{name: 'quanquan', friend: 'guiling'}");
});
// 启动服务, 监听端口
server.listen(PORT, () => {
console.log('服务启动成功, 正在监听: ', PORT);
});
改动后的代码.
浏览器刷新一下, 我了个乖乖. 好了 😄
开心过后, 我们想一下, jsonp 的缺点是只能支持 GET 请求, 作为现代
的跨域请求方式. cors 能不能支持其他的请求方式呢?
其他请求方式的支持
作为现代
的跨域问题解决方案, 应该是能解决多种请求方式的. 光说不练假把式. 咱们试试 😄
改动前端代码
// 改动前
xhr.open('GET', 'http://localhost:8888')
// 改动后
xhr.open('POST', 'http://localhost:8888')
修改后代码来浏览器上看一下?
木有任何问题, 返回的数据顺利的打印. 没有任何的报错.
趁着兴头试试PUT
请求
再次改动前端代码
// 改动前
xhr.open('POST', 'http://localhost:8888')
// 改动后
xhr.open('PUT', 'http://localhost:8888')
修改后代码来浏览器上看一下?
哎呀我滴妈? 很眼熟的错误, 但是不要认错人哈, 这次的报错和之前的报错长的很像, 但是关键词不一样了. 根据之前的经验, 后端添加 Access-Control-Allow-Methods
响应头应该好使.
后端代码
const http = require('http');
const PORT = 8888;
// 创建一个 http 服务
const server = http.createServer((request, response) => {
response.setHeader('Access-Control-Allow-Origin', '*');
response.setHeader('Access-Control-Allow-Methods', 'PUT');
response.end("{name: 'quanquan', friend: 'guiling'}");
});
// 启动服务, 监听端口
server.listen(PORT, () => {
console.log('服务启动成功, 正在监听: ', PORT);
});
修改后代码来浏览器上看一下?
成功了 😄
其他的 http 方法和 PUT
方法处理的方式是一样的. 举一反三即可.
后端说, 你的请求要加一个 token 呀
既然是现代的开发, 那么会话的管理一般是会用 jwt(后续可能会写相关的文章), jwt 一个闪耀的标志就是请求头添加了 jwt token. 明人不说暗话.
修改前端代码:
// 添加了一行
xhr.setRequestHeader('token', 'quanquanbunengshuo')
修改后代码来浏览器上看一下?
相信大家已经摸清了我的套路, 闲话不扯.
后端代码
// 添加了一行
response.setHeader('Access-Control-Allow-Headers', 'token');
修改后代码来浏览器上看一下?
目前为止, 跨域请求成功了, 请求方式兼容了, 自定义请求头好使了. 是不是大吉大利, 可以吃鸡了呢?
致, 被打入冷宫的 Network tab
我们自始至终都在查看浏览器的 Console tab, 作为一个通信性质的文章, 不看一下 Network 明显有点说不过去辣.
既然存在, 那肯定是要看看的, 我们把 tab 切换到 Network.
哎呦喂? 两个请求, 一个 OPTIONS 一个 PUT, 这是什么鬼?
下集预告: 刚刚看到了 Network 就出现了血案. 当然如果仅仅是停留在会用 CORS 实现跨域上, 到目前为止已经没有什么问题了, 用来面试也是杠杠滴. 下一步, 我们一起探讨 CORS 条件下, 预检请求
和 Cookie
携带那些事儿. 周五码字感觉好累...... 约小姐姐去辣 😄