同源策略:

  • 协议相同(protocol)
  • 主机相同(host)
  • 端口相同(port)

如果不满足同源策略的网络请求就形成了跨域。

一.解决方案之 jsonp:

前端代码

1
2
3
4
5
6
7
8
9
10
11
12
showJsonp = function (obj) {
console.log(obj)
}
// 回调函数拼在请求参数中
const url = 'http://localhost:3030/getTodoJsonp?callback=showJsonp'
// 利用script标签的src属性发起请求,解决跨域的问题,但仅限于get请求
const scriptEle = document.createElement('script')
scriptEle.setAttribute('src', url)
document.body.appendChild(scriptEle)
scriptEle.onload = function () {
document.body.removeChild(scriptEle)
}

服务端代码(Node Express框架)

1
2
3
4
5
6
// 处理jsonp请求,解决跨域问题
app.get('/getTodoJsonp', function (req, res, next) {
let callback = req.query.callback
let content = callback + "({'message':'测试数据'})"
res.send(content)
})

页面打印:{message: "测试数据"}

二.解决方案之CORS:

CORS (Cross-Origin Resource Sharing,跨域资源共享)是一个系统,它由一系列传输的HTTP头组成,这些HTTP头决定浏览器是否阻止前端 JavaScript 代码获取跨域请求的响应

CORS 实现起来非常方便,只需要增加一些 HTTP 头,让服务器能声明允许的访问来源

只要后端实现了 CORS,就实现了跨域

后端代码栗子(Node Express框架)

1
2
3
4
5
6
7
8
9
// 设置允许跨域请求
app.all('*', function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept')
res.header('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS')
res.header('X-Powered-By', '3.2.1')
res.header('Content-Type', 'application/json;charset=utf-8')
next()
})

*Access-Control-Allow-Origin 设置为其实意义不大,可以说是形同虚设,实际应用中,上线前我们会将Access-Control-Allow-Origin 值设为我们目标host**

三.解决方案之Websocket:

Websocket 是 HTML5 的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。什么是全双工通信 ?简单来说,就是在建立连接之后,server 与 client 都能主动向对方发送或接收数据。我们这里以第三方库 ws 为例:

1
2
3
4
5
6
7
8
9
10
11
12
const WebSocket = require('ws');

const ws = new WebSocket('ws://www.host.com/path');

ws.on('open', function open() {
ws.send('something');
});

ws.on('message', function incoming(data) {
console.log(data);
});
... ...

需要注意的是,Websocket 属于长连接,在一个页面建立多个 Websocket 连接可能会导致性能问题。

四. 解决方案之Proxy:

代理(Proxy)也称网络代理,是一种特殊的网络服务,允许一个(一般为客户端)通过这个服务与另一个网络终端(一般为服务器)进行非直接的连接。一些网关、路由器等网络设备具备网络代理功能。一般认为代理服务有利于保障网络终端的隐私或安全,防止攻击

  1. vue项目开发过程可以配置vue.config.js文件来设置代理允许跨域。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// vue.config.js文件
module.exports = {
devServer: {
host: '127.0.0.1',
port: 8080,
open: true,// vue项目启动时自动打开浏览器
proxy: {
'/api': { // '/api'是代理标识,用于告诉node,url前面是/api的就是使用代理的
target: "http://localhost:3030", //目标地址,一般是指后台服务器地址
changeOrigin: true, //是否跨域
pathRewrite: { // pathRewrite 的作用是把实际Request Url中的'/api'用""代替
'^/api': ""
}
}
}
}
}

// axios发送请求中配置请求的根路径
axios.defaults.baseURL = '/api'
  1. 服务端实现代理请求转发

以express框架为例

1
2
3
4
5
6
7
8
9
var express = require('express');
const proxy = require('http-proxy-middleware')
const app = express()
app.use(express.static(__dirname + '/'))
app.use('/api', proxy({
target: 'http://localhost:4000',
changeOrigin: false
}));
module.exports = app
  1. 通过配置nginx实现代理(一般用于生产环境中)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
server {
listen 8088;
server_name 127.0.0.1; # 主机名
gzip on; # 开启gzip压缩功能
gzip_min_length 1024; #设置最小压缩大小,单位字节
gzip_types text/plain application/x-javascript text/css application/xml; #用来指定压缩的类型,“text/html”类型总是会被压缩。
location / {
root /home/exam/dist;
index index.html index.htm;
# 添加请求头
add_header 'Access-Control-Allow-Origin' *;
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
}

location /api {
include uwsgi_params;
# 代理请求路径
proxy_pass http://127.0.0.1:3033/api;
}

}