How to handle preflight CORS requests on a Go server
I personally find it tedious to add preflight routes for every path that will get an OPTIONS
request, so instead I simply add my handler to any OPTIONS
method that the request multiplexer (Gorilla in this case) handles as follows:
router.Methods("OPTIONS").HandlerFunc(
func(w http.ResponseWriter, r *http.Request){
myHttpLib.OptionsForBrowserPreflight(w, r)
})
Note though, that this should come before mapping other routes because if, for example, you have a path like "/foo"
and you register that first without specifying any methods for that route, then an OPTIONS request to "/foo" would run instead of your pre-flight code because its the first match.
This way you can: (1) have just one routing registration for all pre-flights, and (2) have one handler to reuse code and apply logic/rules in a single place for OPTIONS requests.
One simple way to separate out your logic and re-use the CORS handler you define would be to wrap your REST handler. For example, if you're using net/http and the Handle
method you could always do something like:
func corsHandler(h http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if (r.Method == "OPTIONS") {
//handle preflight in here
} else {
h.ServeHTTP(w,r)
}
}
}
You can wrap like this:
http.Handle("/endpoint/", corsHandler(restHandler))
Here's a snippet that worked for me:
addCorsHeader(res)
if req.Method == "OPTIONS" {
res.WriteHeader(http.StatusOK)
return
} else {
h.APIHandler.ServeHTTP(res, req)
}
func addCorsHeader(res http.ResponseWriter) {
headers := res.Header()
headers.Add("Access-Control-Allow-Origin", "*")
headers.Add("Vary", "Origin")
headers.Add("Vary", "Access-Control-Request-Method")
headers.Add("Vary", "Access-Control-Request-Headers")
headers.Add("Access-Control-Allow-Headers", "Content-Type, Origin, Accept, token")
headers.Add("Access-Control-Allow-Methods", "GET, POST,OPTIONS")
}
gorilla/handlers also has a nice CORS handler: cors.go
Example usage:
import (
"net/http"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/users", UserEndpoint)
r.HandleFunc("/projects", ProjectEndpoint)
// Apply the CORS middleware to our top-level router, with the defaults.
http.ListenAndServe(":8000", handlers.CORS()(r))
}