<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>

這時(shí)如果在瀏覽器中打開(kāi)這個(gè)文件,或者使用nginx作為靜態(tài)代理,只要該文件「不是」通過(guò)http://127.0.0.1:8080 這個(gè)域發(fā)起的請(qǐng)求,則由于瀏覽器的同源策略都會(huì)被攔截。

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后端解決跨域

瀏覽器在發(fā)送http請(qǐng)求的時(shí)候,會(huì)在header中記錄Origin信息

我們先來(lái)看下gin是如何獲取請(qǐng)求 header 信息的,gin 通過(guò) *gin.Context.Request.Header 來(lái)獲取到請(qǐng)求頭信息

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

想要獲取到Origin 信息,Header 本身是個(gè) map[string][]string 類型數(shù)據(jù),可能通過(guò)Get方法來(lái)獲取Origin信息

在響應(yīng)頭中將該域添加到 Access-Control-Allow-Origin中,并同時(shí)設(shè)置 Access-Control-Allow-Methods

func (ApiV1) Get(c *gin.Context)  {
origin := c.Request.Header.Get("Origin") //請(qǐng)求頭部
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")
//允許客戶端傳遞校驗(yàn)信息比如 cookie
c.Header("Access-Control-Allow-Credentials", "true")
}
c.JSON(200, gin.H{"msg": "handlers get request!"})
}

這時(shí)再訪問(wèn)剛才的html文件就可以正常的請(qǐng)求到了數(shù)據(jù)。

寫成中間件

上面只是針對(duì)某一個(gè)接口,如果想要所有的接口都可以被跨域請(qǐng)求,則可以將其寫成一個(gè)中間件,上面是將所有的域都允許跨域請(qǐng)求,其實(shí)還是有點(diǎn)問(wèn)題的,這里我將允許跨域請(qǐng)求的地址寫到一個(gè)map中,本來(lái)想寫到數(shù)組或者slice中,但是發(fā)現(xiàn)在判斷是否存在的時(shí)候,golang中并沒(méi)有in 運(yùn)算符,需要遍歷數(shù)組或者slice,復(fù)雜度為O(n) ,還是使用map吧。

另外如果請(qǐng)求方法是OPTIONS 的話,如websocket請(qǐng)求會(huì)先發(fā)一個(gè)OPTIONS請(qǐng)求,這時(shí)可以不進(jìn)行校驗(yàn),直接返回

package midwares

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

func CheckCors() gin.HandlerFunc {
//這里可以處理一些別的邏輯
return func(c *gin.Context) {
// 定義一個(gè)origin的map,只有在字典中的key才允許跨域請(qǐng)求
var allowOrigins = map[string]struct{}{
"http://127.0.0.1:8888": struct {}{},
"https://www.yangyanxing.com": struct {}{},
}
origin := c.Request.Header.Get("Origin") //請(qǐng)求頭部
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")

之后就可以完成跨域請(qǐng)求了。

文章轉(zhuǎn)自微信公眾號(hào)@序語(yǔ)程言

上一篇:

Go 語(yǔ)言 API 文檔利器:Swagger保姆級(jí)使用指南大揭秘!

下一篇:

.NET 7.0+WebAPI 后端架構(gòu)實(shí)戰(zhàn)
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊(cè)

多API并行試用

數(shù)據(jù)驅(qū)動(dòng)選型,提升決策效率

查看全部API→
??

熱門場(chǎng)景實(shí)測(cè),選對(duì)API

#AI文本生成大模型API

對(duì)比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)

#AI深度推理大模型API

對(duì)比大模型API的邏輯推理準(zhǔn)確性、分析深度、可視化建議合理性

10個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)