首先要强调的是,跨域是浏览器引发的问题,其他请求方式如curl、apifox、postman都不会引发跨域问题。记住,跨域是浏览器引发的问题
要理解跨域,得先知道什么是不跨域,这里引发出一个概念,同源
同源:指协议、域名、端口号都相同。记住,是三个都相同,没得商量
一个请求是一个源请求另一个源,当请求源和被请求的源不同时,就是跨域
例如:做了一个前后端分离的项目,前端的端口和后端的端口不同。使用浏览器访问前端页面后,前端需要向后端发起请求。这里因为它们的端口不同,所以这就是一个跨域请求。当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不一同,即跨域。
跨域问题
经过前文的梳理,我们了解了什么是跨域请求,那跨域请求有什么问题吗?有的兄弟,有的。因为浏览器的同源策略,跨域请求得到数据是不给前端用的。注意我这里的措辞,请求得到数据,是的,请求已经发生了,浏览器允许请求发出,是发出请求后,得到的数据检查后不符合不给前端使用(这里有简单请求和复杂请求,就不细分了,有兴趣可以了解一下)
解决跨域问题
解决跨域有两种方法,一种是代理,另一种是CORS。其他方法还有,但是使用少,略过
CORS解决跨域
这种方法是直接的解决跨域问题,这种方法是在后端使用的。浏览器得到请求返回的数据需要检查,检查通过后才给前端使用,那么后端让返回的数据通过检查即可
检查的地方如下(具体不止这些,不要被误导),在响应的响应头上加上对应的响应头即可
Access-Control-Allow-Origin: * // 表明接受什么域名
Access-Control-Allow-Credentials: true // 是否允许发送cookie
Access-Control-Expose-Headers: XXX // 是否暴露其他headers值给前端
例如gin中去解决跨域,加上需要的响应头即可
//允许跨域的中间件
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method
if c.Request.Header.Get("origin") != "" {
c.Header("Access-Control-Allow-Origin", "*") // 可将将 * 替换为指定的域名
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
c.Header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization")
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type")
c.Header("Access-Control-Allow-Credentials", "true")
}
if method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
}
c.Next()
}
}
func Index(c *gin.Context) {
c.JSON(200, gin.H{
"code": 0,
"msg": "成功",
"data": gin.H{},
})
return
}
func main() {
r := gin.Default()
r.GET("/api/no_cors", Index)
r.POST("/api/no_cors", Index)
r.GET("/api/cors", Cors(), Index)
r.POST("/api/cors", Cors(), Index)
r.Run(":8080")
}
可以看到,这种方法就是直接的让浏览器检查,检查通过即可
代理
与上一个的解决思路不同,代理解决跨域问题的核心思路是让请求在浏览器看来是同源的。具体来说,通过在前端和后端之间设置一个代理服务器,隐藏后端真实的源,让浏览器认为请求是同源的,从而绕过同源策略的限制。

这种方案是目前最主流的跨域解决方案,它分为两类,一个是开发环境,一个是生产环境
开发环境解决跨域
以vue3为例,vite提供了代理功能
import {fileURLToPath, URL} from 'node:url'
import {defineConfig, loadEnv} from 'vite'
import vue from '@vitejs/plugin-vue'
import type {ImportMetaEnv} from "./env";
// https://vitejs.dev/config/
export default defineConfig(({mode}) => {
let env: Record<keyof ImportMetaEnv, string> = loadEnv(mode, process.cwd())
const serverUrl = env.VITE_SERVER_URL
const wsUrl = serverUrl.replace("http", "ws")
return {
plugins: [
vue(),
],
envDir: "./",
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server: {
host: "0.0.0.0",
port: 80,
proxy: {
"/api": {
target: serverUrl,
changeOrigin: true,
}
}
}
}
})
生产环境解决跨域
使用nginx的反向代理
server {
listen 80;
server_name blog.fengfengzhidao.com;
location / {
try_files $uri $uri/ /index.html;
root /opt/gvb/web/dist;
index index.html index.htm;
}
location /api/ {
# rewrite ^/(api/.*) /$1 break;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
#反向代理的源
proxy_pass http://127.0.0.1:8082/api/;
}
location /uploads/ {
alias /opt/gvb/server/uploads/;
}
access_log /opt/gvb/access.log;
error_log /opt/gvb/error.log;
}
文章是借鉴一篇文章彻底搞懂跨域问题再加上本人理解完成的,有兴趣可以看一下

1711

被折叠的 条评论
为什么被折叠?



