所有文章 > API产品 > 使用gin搭建api后台系统之跨域问题

使用gin搭建api后台系统之跨域问题

在前后端分离的开发模式下,跨域问题一直是个老生常谈的问题,一般的解决方案分为前端与后端,前端主要利用jsonp来解决,但是后端解决会更更加方便,本文记录一下使用gin框架时在后端解决跨域问题。

准备前端文件

准备一下用于前端发送http请求的文件,该文件使用jquery来发送ajax,当然也可以使用vue+axios

我这里为了演示,简单的使用python 启了个http服务

python -m SimpleHTTPServer 8888

<html>
<head>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
</head>
<body>
<script>
$(document).ready(function(){
const url = "http://127.0.0.1:8080/api/v1/test";
$.ajax({
url: url,
type: "GET",
success:function(result){
console.log(result)
},
error:function(error){
console.log(error)
}
})
})
</script>
</body>
</html>

这时如果在浏览器中打开这个文件,或者使用nginx作为静态代理,只要该文件「不是」通过http://127.0.0.1:8080 这个域发起的请求,则由于浏览器的同源策略都会被拦截。

Access to XMLHttpRequest at 'http://127.0.0.1:8080/api/v1/test' from origin 'http://127.0.0.1:8888' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

gin后端解决跨域

浏览器在发送http请求的时候,会在header中记录Origin信息

我们先来看下gin是如何获取请求 header 信息的,gin 通过 *gin.Context.Request.Header 来获取到请求头信息

func (ApiV1) Get(c *gin.Context)  {
fmt.Println(c.Request.Header)
c.JSON(200, gin.H{"msg": "handlers get request!"})
}

想要获取到Origin 信息,Header 本身是个 map[string][]string 类型数据,可能通过Get方法来获取Origin信息

在响应头中将该域添加到 Access-Control-Allow-Origin中,并同时设置 Access-Control-Allow-Methods

func (ApiV1) Get(c *gin.Context)  {
origin := c.Request.Header.Get("Origin") //请求头部
if origin!=""{
// 将该域添加到allow-origin中
c.Header("Access-Control-Allow-Origin", 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")
//允许客户端传递校验信息比如 cookie
c.Header("Access-Control-Allow-Credentials", "true")
}
c.JSON(200, gin.H{"msg": "handlers get request!"})
}

这时再访问刚才的html文件就可以正常的请求到了数据。

写成中间件

上面只是针对某一个接口,如果想要所有的接口都可以被跨域请求,则可以将其写成一个中间件,上面是将所有的域都允许跨域请求,其实还是有点问题的,这里我将允许跨域请求的地址写到一个map中,本来想写到数组或者slice中,但是发现在判断是否存在的时候,golang中并没有in 运算符,需要遍历数组或者slice,复杂度为O(n) ,还是使用map吧。

另外如果请求方法是OPTIONS 的话,如websocket请求会先发一个OPTIONS请求,这时可以不进行校验,直接返回

package midwares

import (
"github.com/gin-gonic/gin"
)

func CheckCors() gin.HandlerFunc {
//这里可以处理一些别的逻辑
return func(c *gin.Context) {
// 定义一个origin的map,只有在字典中的key才允许跨域请求
var allowOrigins = map[string]struct{}{
"http://127.0.0.1:8888": struct {}{},
"https://www.yangyanxing.com": struct {}{},
}
origin := c.Request.Header.Get("Origin") //请求头部
method := c.Request.Method
if method == "OPTIONS"{
c.AbortWithStatus(http.StatusNoContent)
}
if origin!=""{
if _, ok:=allowOrigins[origin];ok{
c.Header("Access-Control-Allow-Origin", 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")
}
}
c.Next()
}
}

在main.go中加入该中间件

func main() {
r := gin.Default()
r.Use(midwares.CheckCors())
....
r.Run(":8080")

之后就可以完成跨域请求了。

文章转自微信公众号@序语程言

#你可能也喜欢这些API文章!
搜索、试用、集成国内外API!
幂简集成API平台已有 4645种API!
API大全
同话题下的热门内容
na
API 市场在 5 个领域中的作用
na
人工智能API产品的定价模式