Go gin框架CORS

58

我正在使用Go gin框架 gin

func CORSMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Writer.Header().Set("Content-Type", "application/json")
        c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
        c.Writer.Header().Set("Access-Control-Max-Age", "86400")
        c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
        c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, X-Max")
        c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")

        if c.Request.Method == "OPTIONS" {
            c.AbortWithStatus(200)
        } else {
            c.Next()
        }
    }
}

我收到状态码:200 OK,但OPTIONS请求后没有任何变化。看起来我漏了些什么,但是我不知道哪里错了。

有人可以帮助我吗?

11个回答

99

顺便说一句,这是符合我需求的CORS中间件。

func CORSMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
        c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
        c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
        c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")

        if c.Request.Method == "OPTIONS" {
            c.AbortWithStatus(204)
            return
        }

        c.Next()
    }
}

为什么是204?为什么不是200? - qwertmax
10
HTTP NoContent 表示服务器已成功处理请求,但不需要返回任何实体内容。这个状态码通常用于删除操作或更新操作,表示操作已成功执行但是没有需要返回的响应数据。 - Billcountry
1
将近6年了,仍然非常好。很快就解决了我的问题。但我想知道为什么这不是github官方文档的一部分... - user8237421
感谢@Jack。只能将其添加到应用程序的根目录。对于我根据路由URL添加了一些条件的应用程序组路由不起作用。 - Gireesh
4
c.Writer.Header().Set("Access-Control-Allow-Origin", "*") 可能导致浏览器出现跨域问题,如果出现这种情况,需要指定特定的来源而不是使用 * - Eric
并йқһжүҖжңүOPTIONSиҜ·жұӮйғҪжҳҜCORSйў„жЈҖиҜ·жұӮгҖӮдҪҶжҳҜпјҢжӯЈеҰӮ@EricжҢҮеҮәзҡ„йӮЈж ·пјҢжӮЁеҝҪз•ҘдәҶйҖҡй…Қз¬ҰејӮеёёгҖӮ - jub0bs

28

此外,官方的gin中间件可以处理CORS请求,github.com/gin-contrib/cors

您可以使用 $ go get github.com/gin-contrib/cors 进行安装,然后像这样在应用程序中添加该中间件: package main

import (
    "time"

    "github.com/gin-contrib/cors"
    "github.com/gin-gonic/gin"
)

func main() {
    router := gin.Default()
    // CORS for https://foo.com and https://github.com origins, allowing:
    // - PUT and PATCH methods
    // - Origin header
    // - Credentials share
    // - Preflight requests cached for 12 hours
    router.Use(cors.New(cors.Config{
        AllowOrigins:     []string{"https://foo.com"},
        AllowMethods:     []string{"PUT", "PATCH"},
        AllowHeaders:     []string{"Origin"},
        ExposeHeaders:    []string{"Content-Length"},
        AllowCredentials: true,
        AllowOriginFunc: func(origin string) bool {
            return origin == "https://github.com"
        },
        MaxAge: 12 * time.Hour,
    }))
    router.Run()
}

4
如果您使用 AllowOriginFunc,则您在 AllowOrigins 中定义的来源将被忽略。 AllowOriginFunc 是一种自定义函数,用于验证来源。它以来源作为参数,并返回 true 表示允许或 false 表示不允许。如果设置了此选项,则忽略 AllowOrigins 的内容。 - drocha87
此外,应避免使用 AllowOriginFunc,因为它可能导致 Web 缓存污染。请参阅 https://github.com/go-chi/cors/issues/23 - jub0bs

15

我花了大约一小时的时间才理解为什么来自互联网的某些示例能够正常工作,而另一些则不能。所以我找到了区别——代码行顺序很重要,首先你应该使用config,然后再声明你的端点,而不是相反的顺序。

正常工作:

router := gin.Default()
router.Use(cors.Default())
router.GET("/ping", pong)
router.Run(":8082")

不能正常工作:

router := gin.Default()
router.GET("/ping", pong)
router.Use(cors.Default())
router.Run(":8082")

13
func CORSMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {

        c.Header("Access-Control-Allow-Origin", "*")
        c.Header("Access-Control-Allow-Credentials", "true")
        c.Header("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
        c.Header("Access-Control-Allow-Methods", "POST,HEAD,PATCH, OPTIONS, GET, PUT")

        if c.Request.Method == "OPTIONS" {
            c.AbortWithStatus(204)
            return
        }

        c.Next()
    }
}

然后使用它

router = gin.New()  
router.Use(CORSMiddleware())

4

有一个名为https://github.com/rs/cors的包,可以正确地处理CORS请求。它提供了受欢迎的路由器包括gin的示例。

package main

import (
    "net/http"

    "github.com/gin-gonic/gin"
    cors "github.com/rs/cors/wrapper/gin"
)

func main() {
    router := gin.Default()

    router.Use(cors.Default())
    router.GET("/", func(context *gin.Context) {
        context.JSON(http.StatusOK, gin.H{"hello": "world"})
    })

    router.Run(":8080")
}

通常情况下,您只需在 gin 的中间件中添加默认处理程序 router.Use(cors.Default()) 即可。这已经足够了。


3
我们创建了一个简洁的中间件。
import (
    "github.com/gin-gonic/gin"
    "net/http"
)

type optionsMiddleware struct {

}

func CreateOptionsMiddleware() *optionsMiddleware{
    return &optionsMiddleware{}
}

func (middleware *optionsMiddleware)Response(context *gin.Context){
    if context.Request.Method == "OPTIONS" {
        context.AbortWithStatus(http.StatusNoContent)
    }
}

并使用gin中间件进行注册:

app  := gin.New()
app.Use(middleware.CreateOptionsMiddleware().Response).
    Use(next-middleware)......

小心。并不是所有的 OPTIONS 请求都是预检请求。 - jub0bs

3

看起来我错过了什么,但我不知道哪里出错了。

CORS协议比表面上看起来更加复杂,通过“手动”设置CORS响应头来实现它容易出错。在这种特定情况下,

这些困难足以使你相信,一般来说,你最好依赖于一个好的CORS中间件库,它可以将所有这些复杂性抽象化。

Gin确实有一个官方的CORS中间件库:gin-contrib/cors,但它远非理想;我在最近的一篇博客文章中详细讨论了这个话题。如果gin-contrib/cors让您不满意,也许您会喜欢我的CORS中间件库:jub0bs/fcors。它旨在不仅更易于使用,而且更难被误用
它主要与http.Handler兼容,但可以通过http.Handler-to-gin.HandleFunc适配器与Gin一起使用,例如gwatts/gin-adapter提供的适配器:
package main

import (
    "log"
    "net/http"

    "github.com/gin-gonic/gin"
    adapter "github.com/gwatts/gin-adapter"
    "github.com/jub0bs/fcors"
)

func main() {
    // create an engine
    engine := gin.Default()

    // configure the CORS middleware
    cors, err := fcors.AllowAccess(
        fcors.FromAnyOrigin(),
        fcors.WithMethods(
            http.MethodGet,
            http.MethodPost,
            http.MethodPut,
            http.MethodDelete,
            "UPDATE",
        ),
        fcors.WithRequestHeaders(
            "Authorization",
            "Content-Type",
            "X-CSRF-Token",
            "X-Max",
        ),
        fcors.MaxAgeInSeconds(86400),
    )
    if err != nil {
        log.Fatal(err)
    }

    // apply the CORS middleware to the engine
    engine.Use(adapter.Wrap(cors))

    // register the hello handler for the /hello route
    engine.GET("/hello", hello)

    // start the server on port 8080
    if err := engine.Run(":8080"); err != nil {
        log.Fatal(err)
    }
}

func hello(ctx *gin.Context) {
    ctx.JSON(http.StatusOK, gin.H{"hello": "world"})
}

2
使用 gin-contrib/cors
import "github.com/gin-contrib/cors"

[...]

router.Use(cors.New(cors.Config{
    AllowOrigins:     []string{"http://localhost:4040"},
    AllowMethods:     []string{"GET"},
    AllowHeaders:     []string{"Content-Type", "Content-Length", "Accept-Encoding", "Authorization", "Cache-Control"},
    ExposeHeaders:    []string{"Content-Length"},
    AllowCredentials: true,
    MaxAge:           12 * time.Hour,
}))

在我的情况下,我之前有一个JWT令牌中间件,它阻止了所有的OPTION预检请求。将CORS中间件放在第一位解决了这个问题。

2
这对我有用 - 注意:直接设置标题。
func CORSMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {

        c.Header("Access-Control-Allow-Origin", "*")
        c.Header("Access-Control-Allow-Headers", "*")
        /*
            c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
            c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
            c.Writer.Header().Set("Access-Control-Allow-Headers", "access-control-allow-origin, access-control-allow-headers")
            c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH")
        */

        if c.Request.Method == "OPTIONS" {
            c.AbortWithStatus(204)
            return
        }

        c.Next()
    }
}

这对我也起作用,cors.Default() 没有帮助我,谢谢。 - PercyPham
小心。不是所有的 OPTIONS 请求都是预检请求。 - jub0bs

0

我的前端因为添加了尾随斜杠到URL中,导致我遇到了CORS问题,请检查是否存在尾随斜杠。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接