目前的项目中大量运用前后端分离的结构,前端调用接口时,因为浏览器安全策略的限制,很容易产生跨域问题

跨域

出于浏览器的同源策略限制,浏览器会拒绝跨域请求

同源策略:请求时应该具有相同的协议、域名、端口,只要有一个不同就属于跨域

主机(https://cheriko.fun)是否跨域原因
http://cheriko.fun协议不同
https://cheriko.fun:11451端口不同
https://baidu.com域名不同
https://cheriko.fun/about/cheriko\

跨域方式

  1. 前后端协商JSONP
  2. 前端解决,使用代理dev
  3. 后端解决,设置请求头
  4. 运维端解决,nginx代理

JSONP

HTML script标签的src属性,不受跨域访问限制,所以它可以发送跨域请求,但只能发送GET请求,而且不安全

使用此种方式,后端将会返回一个函数,这个函数必须提前在前端定义好,后端会把值注入到函数的参数里

前端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script>
const jsonp = (name) => {
// 创建script标签
let script = document.createElement('script')
// 给script标签的src属性赋值,给后端传函数名
script.src = 'http://localhost:3000/api/jsonp?callback=' + name
// 添加到body(发起请求)
document.body.append(script)
return new Promise((resolve) => {
// 接收值
window[name] = (data) => {
resolve(data)
}
})

}

// 返回的函数的函数名,不能有重复,否则会惨遭覆盖
jsonp(`callback ${new Date().getTime()}`).then(res=> {
console.log(res)
})
</script>

后端:

1
2
3
4
5
6
7
8
9
10
11
12
13
import express from 'express'

const app = express();

app,get('/api/jsonp',(req, res)=> {
const { callback } = req.query
// 后端接收到前端的函数名,把函数(注入了值)返回给前端
res.send(`${callback}('hello jsonp')`)
})

app.listen(3000, ()=> {
console.log('server is running')
})

前端代理

纯前端代理解决跨域,需要用到构建工具(例如Webpack、Rollup、Vite等),只对开发环境有效

前端发送请求:

1
2
3
fetch('/api/json').then(res=>res.json()).then(res=>{
console.log(res)
})

Vite配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { defineConfig } from 'vite'

export default defineConfig({
server: {
// 配置代理
proxy: {
// 定义路径,拦截到/api就会将请求转发给服务端
'/api': {
// 转发地址
target: 'http://localhost:3000',
// 跨域
changeOrigin: true,
// 路径重写,是否需要看实际情况
rewrite: (path)=> path.replace(/^\api/, '')
}
}
}
})

后端:

1
2
3
4
5
6
7
8
9
10
11
import express from 'express'

const app = express()

app.get('/json',(req, res)=> {
res.json({name:'yajue',age:24})
})

app.listen(3000, ()=> {
console.log('server is running')
})

后端设置请求头

后端只需要开启允许跨域即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import express from 'express'

const app = express();

app,get('/json',(req, res)=> {
// 允许前端的任何请求,但是不安全,因为任何人都可以调用
// res.setHeader('Access-Control-Allow-Origin', '*')
// 一般会指定一个IP(域名也可以),只有它才能访问
res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500')
res.json({name:'yajue',age:24})
})

app.listen(3000, ()=> {
console.log('server is running')
})

Nginx反向代理

适合已上线的项目

前端发送请求:

1
2
3
fetch('/api/json').then(res=>res.json()).then(res=>{
console.log(res)
})

Nginx配置文件(在server块内):

1
2
3
location /api/ {
proxy_pass http://localhost:3000/;
}

启动Nginx服务后,前端截取到/api/就会转发到http://localhost:3000/