一、跨域
跨域: 跨域(Cross-Origin)是指在网络中,一个源(Origin)的网页或者网站尝试去访问另一个不同源的资源时发生的情况。一个“源”是由协议(例如HTTP或HTTPS)、域名和端口号组成的组合。如果任意一个组件不同,就被认为是不同的源。
注意: 协议、域名、端口号中的任意一个不同,就会受到同源策略的限制。
比如: 端口不同导致跨域报错:
二、四种解决方法
方法1: jsonp(前后端协商)
- 原理: script标签的src不受到同源策略的限制。但是只能使用get请求。
- 后端返回一段js代码,代码的内容是一个前端定义好的函数的调用,把需要返回的内容传递进去。(很有意思,后端调用前端定义好的函数,前端定义好的函数必须在全局作用域)
- 缺点:只能发送get 请求,不安全,不好维护.
- 优点,兼容性比较好。
1.1 前端
const jsonp = (callback) => {
let script = document.createElement("script")
script.src = `http://localhost:3001/api/jsonp?callback=${callback}`
document.body.appendChild(script)
//请求完成 移除script
script.onload = () => {
document.body.removeChild(script)
script = null
}
return new Promise((resolve) => {
// 前端在全局作用域定义一个函数,等待后端返回以后调用该函数,函数的名字需要和传给后端的函数同名
window[callback] = (data) => {
resolve(data)
}
})
}
// 测试,生成的代码需要唯一
jsonp(`callback${new Date().getTime()}`).then((res) => console.log(res))
1.2 后端
import express from './express'
const arr = express()
app.get('/api/jsonp',(req,res)=>{
const {callback} = req.query;
// 返回一段js代码,代码的内容是一个函数的调用,函数的名字是前端的get请求的参数。
res.send(`${callback}("hello jsonp")`)
})
app.listen(3001,()=>{
console.log(`server is running at 3001`);
})
方法2: 打包工具自带的服务器代理请求,解决跨域(推荐)
- 原理: 跨域是浏览器特有的,而服务器和服务器之间不存在跨域。前端的打包工具会自带一个服务器,我们只需要在配置文件中配置反向代理的的规则,然后把请求请求到打包工具的服务器,它就会自动把该请求转发到指定的服务器,当请求完成的时候,它又会把请求的结果返回给浏览器。
以vite为例:
在项目中初始化vite
,新建vite.config.ts
$ pnpm i vite
vite.config.ts
// vite.config.ts
import { defineConfig } from "vite"
export default defineConfig({
server: {
proxy: {
"/api": {
target: `http://localhost:3000`,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
},
})
解释:
target
: 所有以/api
开头的地址,都会被代理到target
对应的http://localhost:3000
这个地址。changeOrigin:true
: 允许跨域。rewrite
: ,接受一个函数,函数的参数就是请求时候的真实的path
,返回值是真实发到对应的目标服务器的path.
以上面的vite 配置为例,举个例子:
当我们发起请求:http://localhost:5173/api/sayHello
(5173 vite默认的端口)的时候,就会被代理到http://localhost:3000/sayHello
.
方法3:后端解决(响应头加上指定的字段)
原理: 只要后端在响应头中加上对应的字段,浏览器就不会再报错。一般通过拦截器统一处理。
以express 为例:
app.get("/api/sayHello", (req, res) => {
// 允许有所的地址跨域
res.setHeader("Access-Control-Allow-Origin", "*")
// 允许所有的请求方法
res.setHeader("Access-Control-Allow-Methods", "*")
// 允许携带cookie
res.setHeader("Access-Control-Allow-Credentials", "*")
// 允许所有的请求头
res.setHeader("Access-Control-Allow-Headers", "*")
res.send({ name: "hello word" })
})
app.listen(3000, () => {
console.log("service running at 3000 ...")
})
如果只想对某一个请求允许跨域,springboot 还可以通过注解实现,前端的装饰器,有时间我再试着写一下前端的装饰器,哈哈哈~~~
方法4: nginx 服务器代理解决跨域。
原理:服务器和服务器之间的通信不存在跨域,因此我们可以开一台中间服务器(nginx
),后端无需改变。前端把请求发给nginx
, nginx 服务器把请求毫无变化地转发给后端的服务器,后端的服务器响应给 nginx
服务器,nginx
服务器加上响应头以后,再返回给前端。
这样,前端收到的响应就是具有跨域请求响应头的响应了。
直接再官网下载nginx服务器,解压到本地,找到对应的配置文件配置一下,然后启动nginx服务器就行了。
这里使用微软appStore 中安装的ubuntu 为例子实现。本以为安装很简单,但是安装的时候有一堆问题,几次差点放弃,还好坚持下来了,历尽千辛,记录在这里了# ubuntu 安装遇到的问题(win11)
linux 不是本文的重点,就列举几个常用的linux 命令吧。
$ apt-get install nginx # 安装nginx ,注意换源
$ cat /etc/resolv.conf # 查看IP地址
$ nginx #启动nginx服务 80 默认端口
$ curl http://172.17.16.1:3000/api/sayHello # 发起请求
注意: nginx 默认的端口是80 ,如果windows本地有IIS 服务,或者其它服占用了端口,需要自己更改一下端口。
- 安装完成以后,打开ubuntu 安装教程
依次执行 以下命令,更改nginx 的配置文件。
$ cd /etc/nginx/sites-available #n ginx配置文件的位置
$ ls # 查看当前文件下的所有文件
$ vim default # vim 编辑器没有的话自己安装一下
i
进入编辑模式(左下角的INSERT 代表编辑模式)
找到对应的配置位置,配置代理(如果代理不生效,可能是80端口被占用了,更改一下)server 中加上配置
server {
listen 8866 default_server; # 因为我的 80 端口被其它服务占用了,因此改一下
listen [::]:8866 default_server;
# include snippets/snakeoil.conf;
root /var/www/html;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}
#允许跨域请求的域,* 代表所有
add_header 'Access-Control-Allow-Origin' *;
#允许带上cookie请求
add_header 'Access-Control-Allow-Credentials' 'true';
#允许请求的方法,比如 GET/POST/PUT/DELETE
add_header 'Access-Control-Allow-Methods' *;
#允许请求的header
add_header 'Access-Control-Allow-Headers' *;
location /api {
proxy_pass http://172.17.16.1:3000; # 吧所有的/api 开头的path 代理到这个位置,和vite 配置类似
}
}
请求的时候,直接请求到代理服务器,代理服务器会自行转发。