Gin: Issue with custom CORS header middleware

Created on 3 Jul 2014  路  29Comments  路  Source: gin-gonic/gin

I have created a the following middleware function but It doesn't seem to be working properly. When I do an options request I get a 404 and I get http: multiple response.WriteHeader calls in my console.

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-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
        if c.Req.Method == "OPTIONS" {
            fmt.Println("options")
            c.Abort(200)
            return
        }
        // c.Next()
    }
}

I have mounted it as the before anything else with the following.

r.Use(CORSMiddleware())
bug

Most helpful comment

I'm using smth like this right now.

func CORSMiddleware() gin.HandlerFunc {
     return func(c *gin.Context) {
         c.Writer.Header().Set("Access-Control-Allow-Origin", "http://domain.com")
         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", "Origin, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
         c.Writer.Header().Set("Access-Control-Expose-Headers", "Content-Length")
         c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")

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


r.Use(CORSMiddleware())

All 29 comments

What gin/go versions are you on? Can you please post a complete, runnable example? This[0] works fine for me.

[0] https://gist.github.com/alexandernyquist/94580423363d3ec95fa8

@alexandernyquist I am using the newest version of gin and I am on go1.3 darwin/amd64. (I just did go get -u . before sending this)

Also I have given you and @manucorporat read permissions to the repo. I think there may be too hard to get a runnable example without the rest of the app.

I think the only dependency the app has is Rethinkdb.

Routes: https://github.com/Lanciv/GoGradeAPI/blob/master/handlers/routes.go
Main: https://github.com/Lanciv/GoGradeAPI/blob/master/main.go

Thanks for the access, however I only have access to a Windows machine at the moment.

I'll see if I can get a VirtualBox running this weekend.

"http: multiple response.WriteHeader calls" will be fixed soon with this: https://github.com/gin-gonic/gin/pull/16
Anyway I think that message is just a warning, it guess it should work.

It's incredible that the standard library do not provide any way to know the current status code.

@alexandernyquist I'll get a VM up for you real quick.

@manucorporat Well I get a 404 and the headers aren't set at all :/

Can you guys do a quick review of this: https://github.com/gin-gonic/gin/issues/24 ? once this is merged, your bug can be fixed easily.

@manucorporat I switched to that commit but I am getting the same error.

Of course! that commit do not fix it, but I need to merge that first :)

@manucorporat Do you know when you will have it done?

@alexandernyquist Would you still like me to get you a vm to test with?

@alexandernyquist I just tested your gist and it is returning a 404 to OPTIONS.

it will be fixed today!

@MattAitchison Have you added route for OPTIONS? Unfortunately, there is no OPTIONS-method in the current master branch (PR coming).

Patching gin.go with a OPTIONS-method:

func (group *RouterGroup) OPTIONS(path string, handlers ...HandlerFunc) {
    group.Handle("OPTIONS", path, handlers)
}

And registering the route:

r.OPTIONS("", func (g *gin.Context) {
    g.JSON(200, gin.H{"foo":"bar"})
})

Will work.

@alexandernyquist Well I would like to handle OPTIONS inside my middleware. I don't believe I should have to mount the middleware and add a route for OPTIONS. I have added the following route which has fixed the problem for now. I hope it's only temporary.

r.Handle("OPTIONS", "/*cors", []gin.HandlerFunc{CORSMiddleware()})

It looks like the 404 handler sends a 404 before my OPTIONS middleware can write.

Ah, I was a bit too quick there. I'll check it out when I get home.

Just an update, this is already fixed in my local repo

@manucorporat that works like a charm. Nice!

Works now! Thanks!

has someone successful experience with cors?

@qwertmax - Super basic approach but works very well.

    r.Use(func(c *gin.Context) {
               // Run this on all requests   
               // Should be moved to a proper middleware 
        c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
        c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type,Token")
        c.Next()
    })

    r.OPTIONS("/*cors", func(c *gin.Context) {
              // Empty 200 response
    })

I'm using smth like this right now.

func CORSMiddleware() gin.HandlerFunc {
     return func(c *gin.Context) {
         c.Writer.Header().Set("Access-Control-Allow-Origin", "http://domain.com")
         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", "Origin, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
         c.Writer.Header().Set("Access-Control-Expose-Headers", "Content-Length")
         c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")

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


r.Use(CORSMiddleware())

Hello I am getting multiple headers on 2 nd request.
screen shot 2018-08-17 at 4 43 18 pm

@shivkumarsingh7 can you describe a little bit more your problem?

I am using middleware with below code

````
func CORSMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "http://domain.com")
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", "Origin, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
c.Writer.Header().Set("Access-Control-Expose-Headers", "Content-Length")
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")

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

}

r.Use(CORSMiddleware())
````

It's working properly with package "github.com/gin-gonic/gin" but When I am changing package to "gopkg.in/gin-gonic/gin.v1" I am getting multiple headers on 2nd request.

@shivkumarsingh7 may be because you receive 2 responses (OPTIONS and than GET/POST/whatever) ?

@qwertmax apart from the option and one get, post, put or delete I am getting header twice in one response only

Deal with http OPTIONS method currently:

engine := gin.Default() // return *gin.Engine
engine.Use(CorsMW)        // incomplete
group := engine.Group("/prefix") // return *gin.RouterGroup
group.GET("/path", handlerFunc)

404 Happend:

engine := gin.Default()
engine := engine.Group("/prefix")
group.Use(CorsMW)
group.GET("/path", handlerFunc)
  • Request will be processed by Engine's Middleware before matches the route config.
  • Request will be processed by RouterGroup's Middleware after matches the route config.

So we use CorsMiddleware in *gin.Engine will process all CORS correctly.

Was this page helpful?
1 / 5 - 1 ratings

Related issues

mdsantosdev picture mdsantosdev  路  30Comments

valyala picture valyala  路  31Comments

nithinmohan picture nithinmohan  路  24Comments

ndbroadbent picture ndbroadbent  路  67Comments

ycdg picture ycdg  路  18Comments