同源:两个页面的协议、域名、端口号都相同
同源策略:是浏览器提供的一个安全功能(浏览器规定,非同源网站JS不能进行资源交互)
浏览器允许发起跨域请求,但是跨域请求回来的数据会被浏览器拦截
由于浏览器同源策略的限制,网页无法通过Ajax请求非同源的接口数据,但是等标签不受浏览器同源策略的影响,可以通过src属性,请求非同源的js脚本。
不受同源策略影响的标签script
、img
、link
、iframe
…
script
标签
<script src="./1.jsonp.js">script>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.1/jquery.min.js">script>
可以看出无论是同源下的1.jsonp.js文件还是非同源的内容都能正确请求到服务器内容
<script>
function fn(result) {
console.log(result);
}
script>
<script src="https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=走出自闭的鸟儿&cb=fn">script>
上面的例子可以看出,我们没有主动执行fn()函数,就实现了获取百度的数据并打印。
从上图可以看出JSONP需要服务器端支持,因为服务端要接收callback且返回对应格式执行
JSONP不属于ajax,因为没有使用xhr
因为在类似url资源请求中,所以JSONP只支持get请求,且不安全
我们创建一个server.js表示当前目录的http服务,端口号为90
var express = require('express')
// 90端口的服务,将当前目录作为http服务
var app = express()
app.use(express.static(__dirname))
app.listen(90)
再创建一个index.html显示hello
此时90端口的服务会自动调用当前目录的index.html,显示hello
接下来我们在server.js添加一个91端口的服务,它支持get请求返回’你好’
// 91端口的服务,返回数据
var app2 = express()
app2.get('/', function (req, res) {
res.send('你好')
})
app2.listen(91)
我们在90端口向91端口发起fetch请求
<script>
fetch("http://localhost:91").then(res => res.text()).then(data => { alert(data) })
script>
此时产生了跨域,报错
我们采用JSONP解决
服务端
// 91端口的服务,返回数据
var app2 = express()
app2.get('/', function (req, res) {
var funcname = req.query.callback
res.send(funcname + "('你好')")
// 相当于返回fn('你好')
})
app2.listen(91)
客户端
<script>
function fn(data) {
alert(data)
}
script>
<script src="http://localhost:91?callback=fn">script>
由服务端来配置响应头
header(“Access-Control-Allow-Origin:*”)
// 91端口的服务,返回数据
var app2 = express()
app2.get('/', function (req, res) {
res.header('Access-Control-Allow-Origin', '*')
res.send('你好')
})
app2.listen(91)
客户端直接发请求即可
<script>
fetch("http://localhost:91").then(res => res.text()).then(data => { alert(data) })
script>
上面的配置是最基础的,实际项目中我们还有更加细化的配置
// ALLOW_ORIGIN要么是一个*,表示接受任意域名的请求;要么就设置具体域名
res.header('Access-Control-Allow-Origin', ALLOW_ORIGIN)
// ALLOW_ORIGIN是一个布尔值,表示是否允许发送cookie;设置为允许时,ALLOW_ORIGIN要设为具体域名
res.header('Access-Control-Allow-Credentials', ALLOW_ORIGIN)
// 支持返回除6个基本字段外的其他字段
res.header('Access-Control-Expose-Headers', ALLOW_ORIGIN)
// ...
有时还会配置一个options用来先判断一次是否允许跨域,这就是发起请求会返回一个options和一份数据的原因;options请求是跨域请求之前的预检查,会返回服务端支持的请求方法(get、post…)
代理服务器
因为浏览器同源策略只针对于ajax,并不限制服务器之间的通信传输,我们在客户端和服务器中间使用一个代理服务器,代理服务器和客户端同源,代理服务器和服务器进行数据交互,这样就实现了跨域。
在webpack.config.js中配置
module.exports = defineConfig({
transpileDependencies: true,
devServer:{
proxy:{
// 请求为/api,就会触发代理
'/api':{
target:'http://localhost:91', // 服务端接口地址
}
}
}
})
客户端
fetch("/api").then(res => res.text()).then(data => { alert(data) })
其他代理总体思路相同,涉及服务器部署问题
具体使用方法可以参考MDN
原理是一个窗口(客户端)可以获得另一个窗口(服务端)的引用,对获取的引用使用postMessage()
方法分发消息,而接收消息的窗口可以获取消息并经过处理后返回
webcoket作为一种常用于实时聊天的协议,本身就不存在跨域问题,利用websocket的api创建一个socket实例,利用open方法向后台发送数据,利用message方法接收后台的数据。