Making golang Gorilla CORS handler work
Asked Answered
S

8

51

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)))
}
Sigvard answered 6/12, 2016 at 0:40 Comment(2)
Jup: gorillatoolkit.org/pkg/handlers#AllowedOriginsHormonal
Crossposted at groups.google.com/forum/#!topic/gorilla-web/T3ICTbJaly4Triplenerved
R
67

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)))
Rochellerochemont answered 6/12, 2016 at 3:34 Comment(1)
Just some bookkeeping: Safari likes to send along the Content-Type request header when performing a "non-standard" operation (e.g. PUT), so you'll want to make your CORS headers allow Content-Type as well. headersOk := handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type"}) related reference in Gorilla CORS packageRochellerochemont
B
39

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)
}
Bonni answered 6/7, 2017 at 21:57 Comment(4)
Thanks, using github.com/rs/cors was easiest and worksHultgren
This is much leaner and really awesome...Thanks a lot dude! @from_TanzaniaGiveaway
it was so simple. thx manInsecure
For some reason, the equivalent code with github.com/gorilla/handlers didn't work for me but this did the trick! Thanks!Ibidem
L
21

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:

Lallation answered 6/12, 2016 at 3:38 Comment(0)
C
7

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)
            })
        }
Cantankerous answered 24/12, 2019 at 16:23 Comment(1)
My issue was with not handling the actual request for "OPTIONS", so sticking a return response in the middleware like you have done makes sense.Gerardgerardo
A
7

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
Aftergrowth answered 31/12, 2019 at 10:33 Comment(1)
More details about gorilla/handlers's lack of support for the wildcard in the Access-Control-Allow-Headers header in #70028794Replay
A
4
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))
}
Argueta answered 16/10, 2018 at 19:26 Comment(1)
gorilla/handlers's AllowedHeaders currently interprets "*" as a literal value as opposed to a wildcard. See #70028794Replay
L
4

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

Lingual answered 2/3, 2020 at 21:53 Comment(0)
L
0

The aformentioned package github.com/rs/cors provides a constructor

AllowAll() *Cors

that

...create a new Cors handler with permissive configuration allowing all origins with all standard methods with any header and credentials.

Laborious answered 22/12, 2021 at 9:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.