41
votes

I have fairly simple setup here as described in the code below. But I am not able to get the CORS to work. I keep getting this error:

XMLHttpRequest cannot load http://localhost:3000/signup. Response to preflight request doesn't pass access control check: No 'Access- Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8000' is therefore not allowed access. The response had HTTP status code 403.

I am sure I am missing something simple here.

Here is the code I have:

package main

import (
    "log"
    "net/http"

    "github.com/gorilla/handlers"
    "github.com/gorilla/mux"
    "myApp/src/controllers"
)

func main() {
    ac := new(controllers.AccountController)

    router := mux.NewRouter()
    router.HandleFunc("/signup", ac.SignUp).Methods("POST")
    router.HandleFunc("/signin", ac.SignIn).Methods("POST")

    log.Fatal(http.ListenAndServe(":3000", handlers.CORS()(router)))
}
7

7 Answers

62
votes

Please read the link Markus suggested, and also about what triggers CORS pre-flight requests.

Pre-flight requests: You may have a content type like JSON, or some other custom header that's triggering a pre-flight request, which your server may not be handling. Try adding this one, if you're using the ever-common AJAX in your front-end: https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Requested-With

Gorilla's handlers.CORS() will set sane defaults to get the basics of CORS working for you; however, you can (and maybe should) take control in a more functional manner.

Here's some starter code:

// Where ORIGIN_ALLOWED is like `scheme://dns[:port]`, or `*` (insecure)
headersOk := handlers.AllowedHeaders([]string{"X-Requested-With"})
originsOk := handlers.AllowedOrigins([]string{os.Getenv("ORIGIN_ALLOWED")})
methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})

// start server listen
// with error handling
log.Fatal(http.ListenAndServe(":" + os.Getenv("PORT"), handlers.CORS(originsOk, headersOk, methodsOk)(router)))
27
votes

You can get more details here: Why doesn’t Postman get a "No 'Access-Control-Allow-Origin' header is present on the requested resource" error when my JavaScript code does? about this issue.

Also try this handler: Go Cors Handler which should solve your issue. I find this much cleaner and easy to resolve the issue.

package main

import (
    "log"
    "net/http"
    "github.com/rs/cors"
    "github.com/gorilla/handlers"
    "github.com/gorilla/mux"
    "myApp/src/controllers"
)

func main() {
    ac := new(controllers.AccountController)

    router := mux.NewRouter()
    router.HandleFunc("/signup", ac.SignUp).Methods("POST")
    router.HandleFunc("/signin", ac.SignIn).Methods("POST")

    c := cors.New(cors.Options{
        AllowedOrigins: []string{"http://localhost:8000"},
        AllowCredentials: true,
    })

    handler := c.Handler(router)
    log.Fatal(http.ListenAndServe(":3000", handler)
}
20
votes

You should create a CORSOption object. For example to allow any origin, Use this code:

corsObj:=handlers.AllowedOrigins([]string{"*"})

Then you pass this object to your handle.CORS function:

log.Fatal(http.ListenAndServe(":3000", handlers.CORS(corsObj)(router)))

For testing it you can use CURL:

curl -H "Origin: http://example.com" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: X-Requested-With" \
-X OPTIONS --verbose http://127.0.0.1:3000

When it works you should see those headers:

> Accept: */*
> Origin: http://example.com
> Access-Control-Request-Method: POST
> Access-Control-Request-Headers: X-Requested-With

Final code is here: https://play.golang.org/p/AOrlJsWhvf

More info:

4
votes
package main

import (
    "log"
    "net/http"

    "github.com/gorilla/handlers"
    "github.com/gorilla/mux"
    "myApp/src/controllers"
       "github.com/rs/cors"
)

func main() {

     ac := new(controllers.AccountController)

    router := mux.NewRouter()
    router.HandleFunc("/signup", ac.SignUp).Methods("POST")
    router.HandleFunc("/signin", ac.SignIn).Methods("POST")
//cors optionsGoes Below
corsOpts := cors.New(cors.Options{
    AllowedOrigins: []string{"http://localhost:8100"}, //you service is available and allowed for this base url 
    AllowedMethods: []string{
        http.MethodGet,//http methods for your app
        http.MethodPost,
        http.MethodPut,
        http.MethodPatch,
        http.MethodDelete,
        http.MethodOptions,
        http.MethodHead,
    },

    AllowedHeaders: []string{
        "*",//or you can your header key values which you are using in your application

    },
})

    http.ListenAndServe(":3000", corsOpts.Handler(router))
}
4
votes

After declaring the mux object, add the accessControlMiddleware as a middleware to the declared object.

func main(){
  ac := new(controllers.AccountController)

    router := mux.NewRouter()
    router.Use(accessControlMiddleware)
    router.HandleFunc("/signup", ac.SignUp).Methods("POST")
    router.HandleFunc("/signin", ac.SignIn).Methods("POST")
    http.ListenAndServe(":3000", corsOpts.Handler(router))
}

// access control and  CORS middleware
func accessControlMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            w.Header().Set("Access-Control-Allow-Origin", "*")
            w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS,PUT")
            w.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type")

                if r.Method == "OPTIONS" {
                    return
                }

                next.ServeHTTP(w, r)
            })
        }
4
votes

Base on jeremiah.trein's answer.

CORS filters are set on server side. Request may work with Postman and fail with a browser because Postman doesn't send preflight request whereas a browser does.

Setting the CORS filters will allow you to configure the origins, methods and headers that the backend shall accept.

In addition, if your browser emits POST or PUT requests that contain a json payload (which is quite reasonnable), you'll need to add 'Content-Type' to the allowed headers.

Finally the handlers.CORS()(router) does not only work with the http.ListenAndServe function but also with http.Handle().

The snippet of code might as well look like:

router := mux.NewRouter()

// do all your routes declaration

headersOK := handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type"})
originsOK := handlers.AllowedOrigins([]string{"*"})
methodsOK := handlers.AllowedMethods([]string{"GET", "POST", "OPTIONS", "DELETE", "PUT"})

http.Handle("/", handlers.CombinedLoggingHandler(os.Stderr, handlers.CORS(headersOK, originsOK, methodsOK)(router)))

It is worth mentionning that i have successfuly used this snippet of code in a Google Cloud Platform Standard AppEngine (and I believe it would work in a Flex AppEngine as well).

3
votes

I realize this is an old issue but nonetheless it took me 30min to get this right.

handler = handlers.CORS(
    // handlers.AllowedMethods([]string{"GET", "POST", "PUT"}),
    handlers.AllowedHeaders([]string{"Accept", "Accept-Language", "Content-Type", "Content-Language", "Origin"}),
    // handlers.AllowedOrigins([]string{"*"}),
)(handler)

Things to note:

  • AllowedMethods does NOT need to explicitly include OPTIONS, this is part of the CORS handler
  • AllowedHeaders need to be explicitly mentioned, * is not a valid wildcard. Typical ajax libraries will send Content-Type when requesting something like application/json, so add that as well.
  • * is the default for AllowedOrigin