解决跨域的三种方案
-
到目前为止,我们编写的 GET 和 POST 接口,存在一个很严重的问题:不支持跨域请求
-
解决接口跨域问题的方案主要有三种
-
CORS (主流的解决方案,推荐使用)
-
代理 (推荐使用)
-
JSONP (有缺陷的解决方案:只支持 GET 请求)
3.代码演示
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><script src="axios"></script>
</head>
<body><button class="btn1">get</button><button class="btn2">post</button>
<script>document.querySelector('.btn1').addEvnetListener('click',function () {axios({method:'get'url: 'http://localhost',params:{name:'tom',age:10}}) })document.querySelector('.btn2').addEvnetListener('click',function () {axios({method:'post'url: 'http://localhost',data:{bookname:'百年孤独',author:'加西亚·马尔克斯'}}) })</script>
</body>
</html>
4.报错提示
这个时候我们会发现出现报错,那么原因是什么呢,就是跨域问题,我们编写的 GET 和 POST 接口,不支持跨域请求
一、使用 cors 中间件解决跨域问题(主流的解决方案,推荐使用)
-
cors 是 Express 的一个第三方中间件。通过安装和配置 cors 中间件,可以很方便地解决跨域问题
-
底层原理:设置一个响应头,响应头部中携带一个 Access-Control-Allow-Origin 字段
-
使用步骤
-
安装中间件: npm install cors
-
导入中间件: const cors = require('cors')
-
配置中间件: 在路由之前调用app.use(cors())
-
-
代码演示
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 导入中间件
const cors = require('cors')
// 配置中间件
app.use(cors())
// 配置解析表单数据的中间件
app.use(express.urlencoded({ extended: false }))
// 导入路由模块
const router = require('./020-apiRouter')
// 把路由模块,注册到 app 上
app.use('/api', router)
// 调用 app.listen 方法,指定端口号并启动 web 服务器
app.listen(3000, () => {console.log('running……')
})
2.2 什么是 CORS 以及注意事项
-
CORS (跨域资源共享) 由一系列 HTTP响应头组成,这些 HTTP 响应头决定浏览器 是否阻止前端 JS 代码跨域获取资源
-
浏览器的同源安全策略默认会阻止网页“跨域”获取资源。但如果接口服务器配置了 CORS 相关的 HTTP 响应头,就可以解除浏览器端的跨域访问限制
-
CORS主要在服务器端进行配置。客户端浏览器无须做任何额外的配置,即可请求开启了CORS的接口
-
CORS 在浏览器中有兼容性。只有支持 XMLHttpRequest Level2 的浏览器,才能正常访问开启了 CORS 的服务端接口(例如:IE10+、Chrome4+、FireFox3.5+)
2.3 了解 Access-Control-Allow-Origin
响应头部中可以携带一个 Access-Control-Allow-Origin 字段
例如,下面的字段值将只允许来自 http://localhost 的请求
如果指定了 Access-Control-Allow-Origin 字段的值为通配符 *,表示允许来自任何域的请求
二、使用代理解决跨域问题(推荐使用)
原理: ajax跨域去第三方接口请求数据是拿不到的,因要遵循同源策略. 但是去自己的服务器要数据是不是就遵循同源策略了.再让自己的服务器去第三方的接口服务器取数据.最后再返回给ajax
跨域-Vue-Cli配置代理
在前端服务和后端接口服务之间 架设一个中间代理服务,它的地址保持和前端服务一致,那么:
1、代理服务和前端服务之间由于协议域名端口三者统一不存在跨域问题,可以直接发送请求
2、代理服务和后端服务之间由于并不经过浏览器没有同源策略的限制,可以直接发送请求
这样,我们就可以通过中间这台服务器做接口转发,在开发环境下解决跨域问题,并且在vue-cli已经为我们内置了该技术,我们只需要按照要求配置一下即可
vue-cli解决跨域配置说明:
分析前准备:
前端网站地址:http://localhost:8080
服务端网址:http://localhost:5000
有两个配置方法
方法一:
在vue.config.js中添加如下配置:
devServer:{proxy:'http://localhost:5000'
}
说明:
1.优点:配置简单,请求资源时直接发给前端(8080)即可。
2.缺点:不能配置多个代理,不能灵活的控制请求是否走代理。
3.工作方式:若按照上述配置代理,当请求了前端步存在的资源时,那么该请求会转发给服务器(优先匹配前端资源)
方法二(实际使用):
编写vue.config.js配置具体代理规则:
module.exports = {devServer:{proxy:{'/api1':{//匹配所有以'/api1'开头的请求路径target:'http://localhost:5000',//代理目标的基础路径changeOrigin:true, //设置changeOrigin的值为true可以代理反向的地址pathRewrite:{'^/api1':''} //重写路径,将api开头的路径改为''},'/api2':{//匹配所有以'/api2'开头的请求路径target:'http://localhost:5001',//代理目标的基础路径changeOrigin:true,//设置changeOrigin的值为true可以代理反向的地址pathRewrite:{'^/api2':''} //重写路径,将api开头的路径改为''},}}
}
说明:可以配置多个代理,且可以灵活的控制请求是否走代理
注意:请求资源时必须加前缀
强调:
vue-cli集成了跨域代理功能------ 只能用在开发阶段。
changeOrigin,pathRewrite是可选的,可写可不写
changeOrigin默认值为true。
changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000 changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:8080
ajax的基地址baseUrl必须是相对地址,而不能是绝对地址
修改了vue.config.js,必须要重启服务器
三、JSONP (有缺陷的解决方案:只支持 GET 请求)
JSONP的底层原理
总结:
动态的创建script标签,给script添加一个src属性,他会发送一次异步请求,请求发送了,数据回来了,但是我们获取不到这个数据,我们在这个地方有一个技巧,在我们当前的前端页面声明一个函数,从我的服务器发回来的是一个函数调用
完整的实现过程
JSONP 在底层,用到了 <script> 标签的 src 属性
原因:
<script> 标签的 src 属性,不受浏览器同源策略的限制
可以把非同源的 JavaScript 代码请求到本地,并执行
JSONP 的底层实现原理 - P1
把非同源的 JavaScript 代码请求到本地,并执行
结论: script的src属性值可以获取网络上任意的资源
JSONP 的底层实现原理 - P2
如果请求回来的 JavaScript 代码只包含函数的调用,则需要程序员手动定义 show 方法。示例代码如下:
缺点:服务器响应回来的代码中,调用的函数名是写死的
JSONP 的底层实现原理 - P3
在指定 <script> 标签的 src 属性时,可以通过查询参数中的 callback,自定义回调函数的名称:
JSONP 的底层实现原理 - P4
在指定 <script> 标签的 src 属性时,还可以通过查询参数的方式,指定要发送给服务器的数据:
代码演示:
<!-- script: src js文件link: href css文件a: href 链接img: src 图片以上的这些标签都可以去第三方找资源--><!-- <img src="./xxx.png" alt=""><img src="网络地址" alt=""> --><!-- <img src="https://img2.baidu.com/it/u=4052168682,2290396488&fm=253&fmt=auto&app=138&f=JPEG?w=211&h=252" alt=""> --><!-- <script src=""></script> --><!-- 1.script中的src 可以去第三方地址要资源(跨域) --><!-- <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script> --><!-- 2. src可以拿到服务器的数据 --><!-- <script src="http://localhost/get"></script> --><!-- 3. src可以传递参数的. 思考:能不能动态的传参 不能--><!-- <script src="http://localhost/get?num=100"></script> --><!-- <script src="http://localhost/get?num="+1000></script> --><script>// 在当前页面声明一个函数function show(data){console.log(data);}const num = 1000// 重点: 动态的创建标签 发送请求 返回的数据是"异步"的const script = document.createElement('script')script.src = "http://localhost/get?callback=show&num="+numdocument.body.appendChild(script)// 异步的打印不出来// console.log(abc);// 技巧: 从服务器返回的是函数调用// show(1000)</script>