Go gin framework CORS - go

I'm using Go gin framework 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()
}
}
}
I've got Status Code:200 OK, but nothing happens after OPTIONS request.
It looks like I miss something, but I can't understand where am I wrong.
Can anybody help me?

FWIW, this is my CORS Middleware that works for my needs.
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()
}
}

There is also official gin's middleware for handling CORS requests github.com/gin-contrib/cors.
You could install it using $ go get github.com/gin-contrib/cors and then add this middleware in your application like this:
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()
}

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()
}
}
then use it
router = gin.New()
router.Use(CORSMiddleware())

I spent like an hour to get why some example from the internet works, and some doesn't. So I got the difference - line order is important, fristly you should use config and then declare your endpoints, but not the opposite way.
Works:
router := gin.Default()
router.Use(cors.Default())
router.GET("/ping", pong)
router.Run(":8082")
Doesn't work:
router := gin.Default()
router.GET("/ping", pong)
router.Use(cors.Default())
router.Run(":8082")

There is package https://github.com/rs/cors, that handles CORS requests in the right way. It has the examples for the popular routers including gin. That it:
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")
}
In common case, you just add the default handling with router.Use(cors.Default()) to your middlewares in gin. It is enough.

We created a minimal middleware.
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)
}
}
and register it with gin middleware :
app := gin.New()
app.Use(middleware.CreateOptionsMiddleware().Response).
Use(next-middleware)......

This worked for me - NOTE: the setting of header directly.
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()
}
}

With 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,
}))
In my case, I had a JWT token middleware before this one that blocked all OPTION pre-flight requests. Putting the CORS middleware first solved the problem.

My frontend was adding a trailing slash to the URL's because of which I was getting CORS, do check for trailing slash.

Related

CORSMiddleware not adding Access-Control headers

I have added this code in my service but the server is not sending Access-Control headers, am I doing something wrong, I've attached a screenshot of all the response headers I'm getting.
func CORSMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Content-Type", "application/json")
c.Writer.Header().Set("Access-Control-Allow-Methods", "PATCH, PUT, POST, GET, DELETE, OPTIONS")
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Access-Control-Allow-Headers, X-Custom-Header, Content-Type, Content-Template, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
log.Infof(c, "All headers %v", c.Writer.Header())
log.Infof(c, "All headers %v", c.Request.Header)
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
return
}
c.Next()
}
}
router := gin.Default()
router.Use(gin.Recovery())
router.Use(middlewares.CORSMiddleware())
router.Use(middlewares.AuthMiddleware())
How can I solve this issue, one more thing, the response status code is 301 and the method type is OPTIONS, I don't know why this is happening.

Is there a way to make CORS work for a single route in Gin Go

I am trying to make a single gin server endpoint be accessible by a certain origin. I have tried some packages such as https://github.com/gin-contrib/cors but from what I understand it sets CORS to your whole server.
For example I have multiple routes but I only want "/scrape" to be allowed to be accessed by "google.com"
/data "all origins"
/ping "all origins"
/scrape "google.com"
Of course you can. It(https://github.com/gin-contrib/cors) just a middleware.
package main
import (
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
// CORS for example.com and example.net origins
router.Use(cors.New(cors.Config{
AllowOrigins: []string{"example.com"},
AllowOriginFunc: func(origin string) bool {
return origin == "example.net"
}})).GET("/scrape", func(c *gin.Context) {
// serve something
})
allOrigins := router.Group("/")
allOrigins.Use(cors.Default())
allOrigins.GET("/data", func(c *gin.Context) {
// serve something
})
allOrigins.GET("/ping", func(c *gin.Context) {
// serve something
})
router.Run()
}
See more middleware example: https://github.com/gin-gonic/gin#using-middleware

Go CORS issue no response

I have a React app using fetch calling to a go mux api.
I am well aware of the question here: Making golang Gorilla CORS handler work
but this does not work for me. I have tried everything in that post and still no success. Looks like go is not even running any middleware or route handler function for me.
Here is the first way I tried fixing it. This uses gorilla/handlers
package main
import (
"fmt"
"net/http"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
)
func commonMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println("MIDDLEWARE CALLED")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
next.ServeHTTP(w, r)
})
}
func ApiHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("ROUTE CALLED")
fmt.Fprintf(w, `{"works:"true}`)
}
func main() {
var router *mux.Router = mux.NewRouter()
router.Use(commonMiddleware)
router.HandleFunc("/api", ApiHandler).Methods("POST")
headersOk := handlers.AllowedHeaders([]string{"Access-Control-Allow-Origin", "Accept", "Accept-Language", "Content-Type", "Content-Language", "Origin"})
originsOk := handlers.AllowedOrigins([]string{"http://localhost:*", "*"})
methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})
http.ListenAndServe(":8000", handlers.CORS(headersOk, originsOk, methodsOk)(router))
}
This uses rs/cors
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
"github.com/rs/cors"
)
func commonMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Println("MIDDLEWARE CALLED")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
next.ServeHTTP(w, r)
})
}
func ApiHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println("ROUTE CALLED")
fmt.Fprintf(w, `{"works:"true}`)
}
func main() {
var router *mux.Router = mux.NewRouter()
router.Use(commonMiddleware)
router.HandleFunc("/api", ApiHandler).Methods("POST")
c := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowCredentials: true,
})
handler := c.Handler(router)
http.ListenAndServe(":8000", handler)
}
However, in both cases CORS errors still appear in the browser. I am running the react server on port 5000 and the go server is on port 8000.
fetch("http://localhost:8000/api", {
method: 'POST',
headers: {
// "Access-Control-Allow-Origin": "*",
'Content-Type': 'application/json'
},
body: JSON.stringify({
example: 1
})
})
Error in Chrome:
Access to fetch at 'http://localhost:8000/validate/' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Neither solution works. In fact "MIDDLEWARE CALLED" and "ROUTE CALLED" in go never print out. The api works just fine in Postman so I know the router works fine and the issue really is CORS. So it appears that the route never gets called.
This is stunning, could it have something to do with preflight. How do I disable all cors issues?
If you want to allow all origin, the other way you can do it is
c := cors.New(cors.Options{
AllowOriginFunc: func(r string) bool {
return true
}
})
I had a similar problem. The issue was in my antivirus software. Disabiling it fixed the issue.

Why is my Google Cloud Run server returning a CORS error?

I made a backend in go and deployed it using Google Cloud Run. Now I am trying to ping it from my website hosted locally, but then I get a CORS error like
type: "cors"
url: "https://abc.a.run.app/do-a"
redirected: false
status: 500
ok: false
statusText: ""
headers: Headers {}
body: (...)
bodyUsed: false
These are the headers I set in my http handler function in go.
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "http://localhost:3000")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
My handler function is routed like
func main() {
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
http.HandleFunc("/do-a", endpoints.DoA)
err := http.ListenAndServe(":"+port, nil)
handle(err)
}
Please check this example from the official documentation:
// Package http provides a set of HTTP Cloud Functions samples.
package http
import (
"fmt"
"net/http"
)
// CORSEnabledFunctionAuth is an example of setting CORS headers with
// authentication enabled.
// For more information about CORS and CORS preflight requests, see
// https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request.
func CORSEnabledFunctionAuth(w http.ResponseWriter, r *http.Request) {
// Set CORS headers for the preflight request
if r.Method == http.MethodOptions {
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Access-Control-Allow-Headers", "Authorization")
w.Header().Set("Access-Control-Allow-Methods", "POST")
w.Header().Set("Access-Control-Allow-Origin", "https://example.com")
w.Header().Set("Access-Control-Max-Age", "3600")
w.WriteHeader(http.StatusNoContent)
return
}
// Set CORS headers for the main request.
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Access-Control-Allow-Origin", "https://example.com")
fmt.Fprint(w, "Hello World!")
}
From the code you posted I can not tell if you check for the preflight request and set the Access-Control-Allow-Methods header.

How to solve "No 'Access-Control-Allow-Origin' header is present on the requested resource"

I'm implementing REST API's in Go and for that I want to allow cross origin requests to be served.
What I am currently doing:
Go server code:
//handleCrossO ... This function will handle CROS
func handleCrossO(w *http.ResponseWriter) {
(*w).Header().Set("Content-Type", "application/json")
(*w).Header().Set("Access-Control-Allow-Origin", "*")
(*w).Header().Set("Access-Control-Allow-Methods", "POST, GET,
OPTIONS, PUT, DELETE")
(*w).Header().Set("Access-Control-Allow-Headers", "Accept,
Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token,
Authorization, Auth")
}
//Response ... This function will create response
func Response(w http.ResponseWriter, message string, statusCode int)
{
handleCrossO(&w)
w.WriteHeader(statusCode)
w.Write([]byte("{\"message\":\"" + message + "\"}"))
}
I am getting the following error on browser console:
Access to XMLHttpRequest at 'http://ip:8080/config' from origin 'http://ip:4200' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I have also tried the following code to check OPTIONS method:
// CheckAuthorization function check if the User is autrhorized to make calls or not
// if ssid is mising then give unauthorized error otherwise call next
func CheckAuthorization(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method == "OPTIONS" {
//handle preflight in here
response.Response(w, "success", 200)
}else {
store := session.SessionStore()
session, _ := store.Get(r, utils.SessionName)
ssid := r.Header.Get("Auth")
if _, ok := session.Values[ssid]; ok {
next.ServeHTTP(w, r)
} else {
var getTokenRes = GetTokenRes{}
sendResponse(w, getTokenRes, 1, "Invalid
SSID", 400)
}
}
}
}
But it is not working.
I have also tried allow OPTIONS method:
router.HandleFunc("/network", authmiddleware.CheckAuthorization(createConfiguration)).Methods("POST", "OPTIONS")
Preflight request should return success and headers. Try to use like following
func setupResponse(w *http.ResponseWriter, req *http.Request) {
(*w).Header().Set("Access-Control-Allow-Origin", "*")
(*w).Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
(*w).Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
}
func indexHandler(w http.ResponseWriter, req *http.Request) {
setupResponse(&w, req)
if (*req).Method == "OPTIONS" {
return
}
// process the request...
}
Also you can use https://github.com/rs/cors
package main
import (
"net/http"
"github.com/rs/cors"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte("{\"hello\": \"world\"}"))
})
// cors.Default() setup the middleware with default options being
// all origins accepted with simple methods (GET, POST). See
// documentation below for more options.
handler := cors.Default().Handler(mux)
http.ListenAndServe(":8080", handler)
}

Resources