跨域常见解决方式


首先概括一下跨域,就是请求时要保证 同协议,同域名,同端口,三者有一个不同即为跨域,跨域导致的常见问题就是ajax请求无法发送,下面总结一下解决跨域的常见方法

demo使用技术:
前端:原生js
后端:express

项目结构
structure

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总结

summary1

注意事项:

  1. script标签的src指向目标地址
  2. script标签设置属性 text/javascript
  3. 作为callback的全局函数
  4. 后端将传参和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")
})

代理服务器转发总结

summary2

总结一下注意事项:

  1. 前端所部署的domain1设置转发
  2. 前端请求自己的域domain1,而不是domain2
  3. 由前端所在的domain1进行配置,不需要domain2参与

    cors跨域

前端之所以不能跨域并非请求发不出去,而是浏览器拦截了服务端的相应,如果服务端在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跨域总结

summary3

注意事项:

  1. cors跨域比较灵活,完全依赖于后端
  2. 如果需要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")
    })
});

websocket跨域总结

summary4


Author: Maple
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source Maple !
  TOC