关于跨域问题
在解释和如何解决这个问题之前,我们先来看为什么会出现这个问题。
浏览器的同源策略
浏览器处于安全考虑,
对 同源请求 放行,对 异源请求 限制
这些限制规则统称为 同源策略。
因此限制造成的开发问题,称之为 跨域(异源)问题
这里的关键就是 浏览器
-
什么是 同源 ?
源 = 协议 + 域名 + 端口
源1 源2 是否同源? http://a.com:81/a https://a.com:81/a ❌ http://a.com:81/a http://www.a.com:81/a ❌ http://a.com:81/a http://a.com:82/a ❌ http://a.com:81/a http://a.com:81/a/b ✔️ -
何为 同源请求 ?

-
浏览器如何 限制 ?
对 标签 发出的跨域请求 轻微限制
例如:link引入的css、script引入的js、img、video、audio
对 Ajax 发出的跨域请求 严厉限制


做个小测试,下面的说法正确吗?
-
我们的前端服务器请求后端服务器发生了跨域问题 ❌
跨域问题只发生在浏览器和服务器之间,而服务器与服务器之间是没有跨域问题的
-
当发生跨域时,服务器无法收到请求 ❌
可以收到,还会响应,只不过被浏览器拦截校验了
-
即便是发生跨域也不一定引发跨域问题 ✔️
-
跨域仅发生在AJAX过程中 ❌
只要是异源请求,都属于跨域。只不过浏览器对这种标签的跨域限制不多,平时开发的时候感受不到。
要解决跨域的问题,关键点就在于了解浏览器是如何进行校验的,它校验的规则是什么
CORS 规则(Cross-Origin Resource Sharing)
CORS 是一套机制,用于浏览器校验跨域请求
它的基本理念是:
只要 服务器 明确表示 允许 ,则校验 通过
服务器 明确 拒绝 或 没有表示 ,则校验 不通过
所以要用 CORS 来解决跨域问题的话,主要是靠 服务器

使用 CORS 方案的前提,必须保证服务器是 【自己人】
CORS 将请求分为两类:
- 简单请求 :
- 请求方法为:
GETHEADPOST - 头部字段满足 CORS 安全规范,详见 W3C ( 粗略地 来说不改动头部的就算简单请求)
- 请求头的 Content-Type 为:
text/plainmultipart/form-dataapplication/x-www-form-urlencoded
- 请求方法为:
- 预检请求
小练习,下面的跨域请求哪些是简单请求,哪些是预检请求:
// 1
fetch("https://douyin.com");
答:简单请求。请求方法:GET;请求头无更改。


// 2
fetch("https://douyin.com", {
headers: {
a: 1,
},
});
答:预检请求。请求方法 GET ,请求头有更改。


// 3
fetch("http://douyin.com", {
method: "POST",
body: JSON.stringify({ a: 1, b: 2 }),
});
答:简单请求。请求方法 POST,请求头无更改。


// 4
fetch("https://douyin.com", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ a: 1, b: 2 }),
});
答:预检请求。请求方法 POST ,请求头有更改。


具体是怎么做的
1. 简单请求
浏览器在发送请求的时候,发现跨域了,此时它会自动带上一个请求头:Origin: 当前的页面源 ,让服务器去验证。
当服务器验证这个页面源是【自己人】,会告诉浏览器放行。”告诉“的方法有两种:
第一种给它一个响应头,后面跟上页面源:Access-Control-Allow-Origin: 页面源;
第二种给它一个响应头,后面给一个*,意思是开放所有源

2. 预检请求
浏览器会先发送一段预检请求到服务器,询问是否要让 Origin 通过。只有当服务器说是自己人的时候,浏览器才会放行。在此前提下,浏览器才会按照简单请求的方式发送请求给服务器。
- 发送预检请求

-
发送真实请求
同 简单请求
来做几个小练习,回答下面的问题:
-
领导让前端小王使用 CORS 在页面上自行解决跨域问题。前端小王可以完成要求吗?
答:因为 CORS 是要动服务器的,除非小王自己写个前端服务器,否则解决不了。
-
前端小王在页面中想使用 AJAX 调用抖音的 API 接口,却发生了跨域问题。小王想通过 CORS 来解决这个问题。前端小王可以解决吗?
答:小王可以打电话给抖音服务器负责人,让ta帮忙开通下权限。如果不行,小王解决不了。
-
跨域上传图片没有问题,但是提交普通表单却遇到了跨域问题。前端小王自己分析后,认为是服务器不支持预检请求导致的。前端小王分析的结果有可能发生吗?
答:有可能。上传图片用的请求方法是:
POST,用的请求头是:Content-Type: multipart/form-data,所以很有可能上传图片用的是简单请求,而且很可能服务器是支持简单请求的。当我们在提交普通表单的时候,可能会使用 AJAX 以 json 的形式提交表单,那么这里就涉及到要改动请求头了,也就涉及到预检请求了,而题目说提交表单的时候遇到跨域问题,所以很有可能是服务器不支持预检请求导致的。
JSONP (JSON with Padding)实现跨域
在没有 CORS 的年代,又是怎么通过跨域请求的呢?

JSONP 是解决跨域问题的古老方案。
同源策略中,对标签的跨域请求限制较小 。
JSONP 利用了这一点。
那么具体又是怎么来操作的呢?
JSONP.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<button>点击获取用户</button>
<script>
// 准备一个回调函数
// 服务器响应后会运行该函数
// 并传递响应数据给参数
function callback(resp) {
console.log(resp);
}
function request(url) {
const script = document.createElement("script");
script.src = url;
script.onload = function () {
// 当执行完毕后,删除掉这个元素
script.remove();
};
document.body.appendChild(script);
}
document.querySelector("button").onclick = function () {
request("https://xxxx.com/yyyy");
};
</script>
</body>
</html>
当服务器的响应结果是 callback(data) 的时候,就会触发回调函数 callback ,此时 data 会放入到 resp 当中,届时我们就能拿到响应的数据了。
注意哦,这里不是 Ajax 请求,而是 JS 请求
so smart~
Proxy代理服务器



示例
前端小王想要请求获取王者荣耀英雄数据
但直接请求腾讯服务器会造成跨域

由于小马不接电话
前端小王决定使用代理服务器解决问题,下面是他的解决步骤
-
前端小王使用 node 搭建了一个服务器充当代理。他首先测试了浏览器和代理服务器的连通情况
使用 node 搭建服务器
index.js
const express = require("express"); const app = express(); //创建服务器 // 接受对路径 /hero 的 GET 请求 app.get("/hero", async (req, res) => { // 使用 CORS 解决对代理服务器的跨域 res.header("access-control-allow-origin", "*"); // 响应一段测试文本 res.send("你好,我是你忠实的代理"); }); // 监听9527端口 app.listen(9527, () => { console.log("服务器已启动"); });npm run start(node index.js)接受 9527 端口请求 hero 路径,返回的响应结果:
使用别的端口访问试试看:
fetch("http://localhost:9527/hero").then(resp=>resp.text()).then(resp=>console.log(resp))
没有问题,说明浏览器和我们的代理服务器之间是连通的

-
前端小王在代理服务器中加入了逻辑:当用户请求 /hero 的时候,转发请求到腾讯服务器获取数据
index.js
const express = require("express"); const app = express(); //创建服务器 // 接受对路径 /hero 的 GET 请求 app.get("/hero", async (req, res) => { // 帮忙去请求王者荣耀的服务器 const axios = require("axios"); const resp = await axios.get("https://pvp.qq.com/web201605/js/herolist.json"); // 使用 CORS 解决对代理服务器的跨域 res.header("access-control-allow-origin", "*"); // 将返回的数据响应给浏览器 res.send(resp.data); }); // 监听9527端口 app.listen(9527, () => { console.log("服务器已启动"); });Terminal:
npm run start(node index.js)使用别的端口访问试试看:
fetch("http://localhost:9527/hero").then(resp=>resp.json()).then(resp=>console.log(resp))

跨域方案经典场景
解决方案决策图

经典场景1
前端小王希望通过 AJAX 访问 【豆瓣电影】 的 API 接口,将它的电影数据展现到自己的网站上。
https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&page_start=0
小王遇到了跨域问题
该跨域问题应该用哪种方式解决最合适?
- CORS
- JSONP
- 代理 ✔️
- 在地上打滚
无法动服务器
经典场景2
公司的后端服务器是支持 CORS 跨域的,但最近前端小王在开发时发现一个奇怪的现象:
- 当本地使用
localhost:5000打开页面后,请求服务器不会引发跨域问题- 当本地使用
127.0.0.1:5000打开页面后,请求服务器出现了跨域错误
前端小王要做的事情是:
- 提着棍子找后端
- 怒砸公司电脑
- 吐口水
- 对比前后服务器的响应头 ✔️
打开 Network 面板,查看响应头中是否有包含 CORS 里需要包含的内容
通过上面的步骤,前端小王确定了,当本地源是 127.0.0.1 时,服务器不产生 Access-Control-Allow-Origin 响应头
前端小王接下来做什么事可能解决该问题:
- 继续使用
localhost:5000打开页面 ✔️ - 提着棍子找后端 ✔️
- 端着茶找后端 ✔️
- 用头撞墙
经典场景3
前端小王所在的项目组维护者一个老项目,老项目中使用的是 JSONP 处理的跨域。
目前需要新增一个功能,新功能中要用到 AJAX 跨域提交 POST 请求。
继续使用 JSONP 能办到吗?
- 能
- 不能 ✔️ JSONP的一个很大的缺陷就是不能发送 POST 请求
- 需要试一试才知道
应该如何才能完成这个新功能的跨域请求
- CORS ✔️
- JSONP
- 代理 ✔️
经典场景4
前端小王入职后,项目负责人给了他一张公司服务器的部署图,
同时告知了小王,为了安全,nginx服务器不提供跨域解决方案

生产环境(用户使用):
http://a.com/index.html 访问静态资源
http://a.com/api/movie 访问 api
开发环境(开发人员使用):
http://localhost:8080/index.html 访问静态资源
http://test.a.com/api/movie 访问 api
以上信息意味着:
- 生产环境会发生跨域问题
- 生产环境不会发生跨域问题 ✔️
- 开发环境会发生跨域问题 ✔️
- 开发环境不会发生跨域问题
这同时意味着,小王需要解决:
- 生产环境的跨域问题
- 开发环境的跨域问题 ✔️
解决的方式是:
-
CORS
-
JSONP
-
代理 ✔️ 在开发环境下,不要直接去请求 nginx 服务器了,直接去请求 dev-server ,让它把请求转发到 nginx 服务器,然后 nginx 服务器去拿数据,拿完数据再返回给 dev-server,然后它再把返回的数据返回给浏览器。
开发环境(开发人员使用):
http://localhost:8080/index.html 访问静态资源
http://localhost:8080/api/movie =====转发=====> http://test.a.com/api/movie 访问 api
参考资料:
https://www.bilibili.com/video/BV13F411y7fy 浏览器的同源策略 【渡一教育】
https://www.bilibili.com/video/BV1rp4y1K7nU CORS跨域请求 【渡一教育】
https://www.bilibili.com/video/BV1SN411B7LX JSONP的原理是什么?它是如何实现跨域的?【渡一教育】
https://www.bilibili.com/video/BV1dh4y1K7Fm 跨域问题解决方案之代理 【渡一教育】
https://www.bilibili.com/video/BV1Km4y1u7Sd 前端常见跨域问题及解决方案 【渡一教育】
评论区