详解前端中的跨域及解决措施
针对浏览器的"同源策略"进行了详细的解释,文章中还包含了一些关于解决跨域的具体措施及代码示例
·
1、跨越介绍
1.1、概念
概念
:跨域是浏览器
的同源策略
产生的一个限制
同源策略
:- 浏览器制定的一个
安全策略
,这个安全策略
的主要目标是:不让我们向别人的服务器
发起请求 同源策略
要求:同域名
、同端口号
、同协议
,不符合同源策略的,浏览器为了安全会阻止这个请求
- 浏览器制定的一个
1.2、如何界定服务器是自己的还是别人的
- 查看请求的
"协议/端口/域名
" 这3个内容和请求源
(当前打开页面的"协议/端口/域名")是否相同 - 如果三者有一者不同,那么就会触发
跨域错误
1.3、常见源
- 常见源1:
file:///E:/
——没有域名/端口 => 本地磁盘的路径 - 常见源2:
http://127.0.0.1:5500/
——协议是:http,域名是:127.0.0.1,端口是:5500 => 在服务器打开的路径
1.4、如何解决跨域错误
- 注意:如果
服务端
不想给跨域请求数据
,那么我们解决不了跨域问题 跨域
这件事其实主导者
还是在服务端
,如果服务端允许我们的跨域请求,那么就可以发起跨域请求,反之就不能发起请求
1.5、示例跨域错误
let btn = document.querySelector("#btn");
btn.addEventListener( "click" , function(){
// 演示:向百度发起一个ajax请求 => 这里的演示只要是和当前源的协议、端口、域名不一致的即可
fetch("https://www.baidu.com")
// 报错:报错信息中的No 'Access-Control-Allow-Origin'这段话表示ajax请求是违背了同源策略的,此时不可以发送ajax请求
})
2、解决跨域——JSONP
2.1、解释
把原本的
ajax请求
——替换成:在"script"标签
的"src"属性
发起请求
示例
:<script src="https://www.baidu.com"><script>
——向百度发起请求
解释
:- 如果我们定义了一个带有src属性的script标签,那么浏览器就会根据script标签的src属性发起请求
- 注意1:我们当前发起的请求,它的响应数据会被当成js来执行!要求响应数据必须符合js代码执行标准,是有意义的js代码 => 告知服务端给我们返回的响应数据必须是有意义的js代码
- 注意2:我们当前发起的请求,请求方式只能是get => 因为script标签发出去的请求没有什么危害性,也算是ajax请求的"阉割版"
和ajax请求的区别
:- 发起的请求响应会被当作js代码立即执行,而ajax返回的请求响应是被放在对象中的一条属性
- ajax可以任意使用请求方式
查看请求是否发送
:- 在网络请求部分将过滤分页调成js
- 左侧的请求图标是黄色的——把百度的代码当成js来解释
2.2、注意
- 当前的
script标签请求
会在页面打开后就立即发送 - 这会导致有了响应数据后,如果没有全局函数会报错,所以这种请求一定要放在全局函数创建后,再进行请求发起!
2.3、面试
问题
:jsonp原理回答
:动态创建script标签,src属性指向没有跨域限制
2.4、示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>demo-1</title>
</head>
<body>
<!-- 1、先设置好回调函数 -->
<script>
function callback(data){
console.log(data);
}
</script>
<!-- 2、发送jsonp请求并携带数据 -->
<script src="https://www.baidu.com/sugrec?prod=pc&from=pc_web&wd=你好&cb=callback"></script>
</body>
</html>
3、解决跨域——CORS跨域
3.1、简述概念
CORS跨域
:- CORS是在服务端返回响应的时候,向响应头中添加可以跨域访问的响应头信息实现的 => 在服务端添加响应头实现的跨域方案
- 在条件允许的情况下,尽可能的使用CORS的跨域会比较好
问题
:向目标服务器发起请求:http://localhost:8889报错- 解释:如果用当前页面(open in default browser / live server)发起请求就会产生跨域问题,因为和目标服务器的路径不一致
- 解决:
- 注意1:要先开启测试服务器,再通过当前页面向测试服务器发起请求 => 另外:使用CORS跨域的时候,服务端必须是我们自己写的 (或者跟后端沟通,让他改一下响应头的配置项),因为要能改服务端的代码才可以,用别人的端口实现CORS跨域不太可能
- 我们需要配置服务器响应头,才可以实现跨域的数据访问
- 注意2:如果服务端启用了CORS跨域,我们可以使用所有前端的ajax技术
3.2、示例客户端代码
// 1、获取页面元素的dom对象
let btn = document.getElementById("btn");
// 3、编写事件处理函数
async function sendRequest(){
let response = await fetch("http://localhost:8889");
let data = await response.json();
console.log( data ); // 在没有设置响应头时,点击发送请求是报错的
}
// 2、给元素添加事件
btn.addEventListener("click" , sendRequest)
// 4、设置服务器响应头步骤——这里可以用小伙伴们自己得服务端代码,写入这行代码即可
// 写入:res.setHeader("Access-Control-Allow-Origin" , "*")即可
3.3、示例服务端代码
let http = require("http");
let chalk = require("chalk");
let server = http.createServer( ( req , res ) => {
// 注意:在这里使用响应头添加工具,添加CORS跨域响应头
// res.setHeader( 响应头key"Access-Control-Allow-Origin" , 响应头value"*" );
// 配置项 => "Access...:允许哪一个地址去进行跨域请求"
// 地址信息 => "*":所有地址都行;也可以写 http://127.0.0.1:5500/这种,但是这种完整的路径需要携带协议(http/其它协议)
res.setHeader("Access-Control-Allow-Origin" , "*");
// 响应数据
let data = {
"message": "我已经接受到了你的请求, 这是我给你的回应, 我是一个 json 格式",
"tips": "后端返回给前端的数据",
"code": 1,
}
// 响应头设置(否则无法正常显示中文)
res.setHeader("Content-Type" , "text/html;charset=utf8");
// 将数据写入响应体
res.write( JSON.stringify(data) );
// 结束响应
res.end();
})
server.listen(8889 , ()=>{
const port = server.address().port
const text =
`
恭喜你,服务器启动成功啦 ${ chalk.cyan('^_^') }!
目前正在监听 ${ chalk.red(port) } 端口号!
基准地址: ${ chalk.red('http://localhost:' + port) }
`
console.log(text)
});
4、解决跨域——服务器代理跨域
4.1、简述概念
服务器代理跨域
就是在本地开启一个服务器,代理发送我们的请求- 可以实现跨域的原因是:因为同源策略是给浏览器设置的,对服务器不生效,所以我们的服务器代理跨域是不受同源策略限制的
4.2、示例客户端代码
<body>
<button id="btn">向百度接口发起请求</button>
<button id="btn2">向代理服务器发起请求</button>
<script>
// 测试是否可以直接向百度发起请求 => 不能,会报错
let btn = document.getElementById("btn");
function sendRequest(){
fetch("https://www.baidu.com/sugrec")
}
btn.addEventListener("click" , sendRequest )
// 根据代理服务器(proxy-server.js)——测试代理请求
let btn2 = document.getElementById("btn2");
async function sendProxyRequest(){
// 1、此时我们的代理请求就是 => 给本地的代理服务器发请求,具体服务器怎么做那是服务器的事了
let response = await fetch("http://localhost:8890"); // 2、给我们自己配置的本地服务器发送请求,让它替我们向百度发请求
// 3、注意:如果本地服务器没设置cors跨域的话,一样会报错,所以不要忘记在proxy-server.js配置响应头,先实现能给自己的服务器发送请求,再让它代替我们发请求
let data = await response.json();
console.log( data );
}
btn2.addEventListener("click" , sendProxyRequest )
</script>
</body>
4.3、示例服务端代码
let http = require("http");
let chalk = require("chalk");
let axios = require("axios");
let server = http.createServer(async ( req , res ) => {
// 代理服务器配置cors跨域
res.setHeader("Access-Control-Allow-Origin" , "*");
// 配置请求信息
let options = {
params : {
prod : "pc",
from : "pc_web",
wd : "hello world" ,
// !发起的请求可以不带cb => 因为这是服务器端
}
}
// 使用axios根据路径进行请求配置:
let { data } = await axios("https://www.baidu.com/sugrec" , options )
// 响应头设置 => 否则无法正常显示中文
// 响应头设置 => 否则无法正常显示中文
res.setHeader("Content-Type" , "text/html;charset=utf8");
// 将数据写入响应体
res.write( JSON.stringify(data) );
// 结束响应
res.end();
})
server.listen(8890 , ()=>{
const port = server.address().port
const text = `
恭喜你, 服务器启动成功啦 ${ chalk.cyan('^_^') } !
目前正在监听 ${ chalk.red(port) } 端口号 !
基准地址: ${ chalk.red('http://localhost:' + port) }
`
console.log(text)
});
4.4、服务器代理原理示例图
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
已为社区贡献1条内容
所有评论(0)