首先概括一下跨域,就是请求时要保证 同协议,同域名,同端口,三者有一个不同即为跨域,跨域导致的常见问题就是ajax请求无法发送,下面总结一下解决跨域的常见方法
demo使用技术:
前端:原生js
后端:express
项目结构
jsonp跨域
script标签的src允许跨域,因此可以动态创建一个script标签,并将src指向特地址,浏览器会将返回的字符串作为js进行执行,就可以拿到我们需要的数据了
直接贴demo代码,demo向服务端请求hello world字符串,请求到以后输出到控制台
我们在本地的8080端口部署前端文件,3000端口端口部署后端服务,根据同源协议,不同端口就会产生跨域问题
jsonp前端代码:
front -> public -> index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
<script>
function fn(params){ // 全局callback函数
console.log(params)
}
function createScriptElement(params){
let script = document.createElement('script')
script.type = 'text/javascript'
script.src = params.src
document.body.append(script)
}
window.onload = function(){
createScriptElement({src: 'http://127.0.0.1:3000/jsonp?callback=fn'})
}
</script>
</html>
front -> index.js
const express = require('express')
const app = express()
app.use(express.static('front/public')) //静态文件
app.listen(8080, (err) =>{
console.log("listen port 8080")
})
jsonp后端代码
const express = require('express')
const app = express()
app.get('/jsonp', (req, res) =>{
let callback = req.query
res.statusCode = 200
res.send(`${req.query.callback}("Hello World")`) //拼接回调函数
})
app.listen(3000, (err) =>{
console.log("listen port 3000")
})
jsonp总结
注意事项:
- script标签的src指向目标地址
- script标签设置属性 text/javascript
- 作为callback的全局函数
- 后端将传参和call拼接成函数调用的形式
代理服务器转发跨域
这里我们基于express使用http-proxy-middleware进行转发跨域(nginx等服务器也可,原理相同),原理大致是:我们的前端部署在127.0.0.1:8080下,我们请求8080下的某个资源比如/domain,然后通过服务端转发到:3000下的/domain,这样就实现了跨域(毕竟服务端不受跨域限制)
代理服务器转发前端代码
front -> public -> index.html
window.onload = function(){
let xhr = new XMLHttpRequest()
xhr.open('get', 'http://127.0.0.1:8080/domain', true)
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
console.log(xhr.response)
}
}
xhr.send()
}
这里前端部署在:8080上,想要请求:3000下的资源/domain,因此先请求:8080下的/domain,然后由服务端进行转发即可
front -> index.js
const express = require('express')
const proxy = require('http-proxy-middleware')
const app = express()
app.use(express.static('front/public'))
app.use('/', proxy.createProxyMiddleware({
target: 'http://127.0.0.1:3000', //请求转发到:3000
changeOrigin: true,
onProxyRes: function(proxyRes, req, res){
res.header('Access-Control-Allow-Origin', 'http://127.0.0.1:8080'); //改变res header,跨域要加上Access-Control-Allow-Origin
}
}))
app.listen(8080, (err) =>{
console.log("listen port 8080")
})
代理服务器后端代码
const express = require('express')
const app = express()
app.get('/domain', (req, res) =>{
res.statusCode = 200
res.send({code: 200, msg: 'success'})
})
app.listen(3000, (err) =>{
console.log("listen port 3000")
})
代理服务器转发总结
总结一下注意事项:
前端之所以不能跨域并非请求发不出去,而是浏览器拦截了服务端的相应,如果服务端在response header添加了Access-Control-Allow-Origin相关的header就可以收到跨域数据
demo只进行简单的get跨域,其他情况比如cookie还需要其他header
cors前端代码
front -> public -> index.html
<script>
window.onload = function(){
let xhr = new XMLHttpRequest()
xhr.open('get', 'http://127.0.0.1:3000/cors', true)
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
console.log(xhr.response)
}
}
xhr.send()
}
</script>
如果跨域成功在控制台可以看到返回值
front -> index.js
const express = require('express')
const app = express()
app.use(express.static('front/public'))
app.listen(8080, (err) =>{
console.log("listen port 8080")
})
cors后端代码
const express = require('express')
const app = express()
app.all('*', (req, res, next) =>{
res.header("Access-Control-Allow-Origin", "*"); //添加header允许跨域
next()
})
app.get('/cors', (req, res) =>{
res.statusCode = 200
res.send({code: 200, msg: 'success'})
})
app.listen(3000, (err) =>{
console.log("listen port 3000")
})
cors跨域总结
注意事项:
- cors跨域比较灵活,完全依赖于后端
- 如果需要cookie或者复杂请求,需要添加特定header
websocket跨域
Html5新引入的全双工协议,不受同源策略的限制,使用websocket需要在前端和后端都建立ws连接
websocket跨域前端代码
front -> public -> index.html
<script>
window.onload = function () {
var ws = new WebSocket("ws://127.0.0.1:3000");
ws.onopen = function (e) {
ws.send("Hello WebSockets!");
};
ws.onmessage = function (e) {
console.log(e.data)
};
ws.onclose = function (e) {
console.log("Connection closed.");
};
}
</script>
front -> index.js
const express = require('express')
const app = express()
app.use(express.static('front/public'))
app.listen(8080, (err) =>{
console.log("listen port 8080")
})
websocket跨域后端代码
home -> index.js
const express = require('express')
const websocket = require('ws')
const WebSocketServer = websocket.Server;
const wss = new WebSocketServer({ port: 3000 });
wss.on("connection", (ws) => {
ws.on('message', function(message){
ws.send("Hello World")
})
});