How to allow OPTIONS method from mobile using gorilla handler? - go

Need to accept OPTIONS method coming from mobile device,
attempted multiple ways to do so and getting strange behavior:
when trying this I get 403 from the client:
(client sends OPTIONS before POST)
import (
"net/http"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/users", UserEndpoint)
r.HandleFunc("/projects", ProjectEndpoint)
methods := handlers.AllowedMethods([]string{"OPTIONS", "DELETE", "GET", "HEAD", "POST"}
http.ListenAndServe(":8000", handlers.CORS(methods)(r))
}
if I omit the methods:
http.ListenAndServe(":8000", handlers.CORS()(r))
I get 403 not authorized
Also played around with it, removed the GET method:
methods := handlers.AllowedMethods([]string{"OPTIONS"}
http.ListenAndServe(":8000", handlers.CORS(methods)(r))
but still could
get a 200 GET when tried from rest client in browser (chromes DHC)
but if I remove the OPTIONS:
methods := handlers.AllowedMethods([]string{"DELETE", "GET", "HEAD", "POST"}
http.ListenAndServe(":8000", handlers.CORS(methods)(r))
I get 405
First example is based on gorilla handler docs
Any ideas on this issues?
Thanks

You really need to understand the request being made, but I had a similar problem and resolved it with:
handlers.CORS(
handlers.AllowedOrigins([]string{"*"}),
handlers.AllowedMethods([]string{"POST"}),
handlers.AllowedHeaders([]string{"Content-Type", "X-Requested-With"}),
)(router)
The request I needed to make (which mimics a preflight) was:
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:8080/products
It was really the AllowedHeaders func that made all the difference. As soon as I added that, the 403 error disappeared.

If you look at cors.go Options are specially handled:
corsOptionMethod string = "OPTIONS"
...
if r.Method == corsOptionMethod {
if ch.ignoreOptions {
ch.h.ServeHTTP(w, r)
return
}
if _, ok := r.Header[corsRequestMethodHeader]; !ok {
w.WriteHeader(http.StatusBadRequest)
return
}
method := r.Header.Get(corsRequestMethodHeader)
if !ch.isMatch(method, ch.allowedMethods) {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
...
So 405 is http.StatusMethodNotAllowed, so maybe it is not CORs request header?
There is also an IngoreOptions method for handling Options independely: http://www.gorillatoolkit.org/pkg/handlers#IgnoreOptions - maybe that will work in your case and you can just ignore it, or process Options on your own.

Related

Go Mux CORS error with both gorilla/handlers and rs/cors [duplicate]

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)))
}
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)))
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)
}
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:
AllowedOrigin function
How can you debug a CORS request with cURL?
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
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)
})
}
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))
}
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).
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.

How do I redirect a GET request to a POST request with some data?

When a user hits a certain url with a GET request I'd like to redirect them to a POST request at another location.
package main
import (
"bytes"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
)
func old(w http.ResponseWriter, r *http.Request) {
newURL := "/new"
var bdy = []byte(`title=Buy cheese and bread for breakfast.`)
r.Method = "POST"
r.URL, _ = url.Parse(newURL)
r.RequestURI = newURL
r.Body = ioutil.NopCloser(bytes.NewReader(bdy))
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
http.Redirect(w, r, newURL, 302)
}
func new(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
fmt.Printf("Method:%v\n", r.Method)
fmt.Printf("Title:%v\n", r.Form.Get("title"))
}
func main() {
http.HandleFunc("/", old)
http.HandleFunc("/new", new)
port := 8000
fmt.Printf("listening on %v\n", port)
if err := http.ListenAndServe(fmt.Sprintf(":%v", port), nil); err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
When I hit "/" I end up getting redirected to "/new" but with a GET request and no form data:
Method:GET
Title:
If I curl "/new" directly I get :
curl -XPOST localhost:8000/new -d "title=Buy cheese and bread for breakfast."
Method:POST
Title:Buy cheese and bread for breakfast.
A HTTP redirect (i.e. reply with status code 301, 302, 307,308 and Location header) can only redirect the existing request to another location and not change the payload of the request. It can add some cookies in the response header though.
In order to automatically change a GET request into a POST request with a specific payload you might try to send the client a HTML page with a <form method=POST... and the payload with hidden input fields, i.e. <input name=... value=... type=hidden> and then add some JavaScript to the page which automatically submits the form. But this kind of hack will only work in browsers and only if JavaScript is enabled and will not work with all kind of payloads either.
To keep compatibility with a broader range of clients it is probably better to design it differently, i.e. keep the GET request in the redirect but give the necessary payload as a parameter to the new target, i.e. http://new.target/foo?payload=..... But the details depend on what the target of the request can deal with.
Unfortunately I don't believe a redirect can change the verb (e.g., GET, POST) or add data to the request. It can only change the URL.
See Redirect () for more information.
I've never heard about changing verb from GET to POST. I guess it's impossible because POST supposes body of body (however may be empty) and GET doesn't. So in general case browser would not be able to take the body from nothing.
Otherwise is possible: you may send 302 redirect after post to make browser perform get. Also verb can be kept with 307 reply code.
Try to rethink browser-server interaction. May be you can redirect POST to another location to solve a task?

Go http response few headers

I'm want send to user alert if he type wrong password and return it to page were he type password. I'm making it like this
func sendJSONHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
http.ServeFile(w, r, "template/api/api.html")
} else if r.Method == "POST" {
r.ParseForm()
if r.Form["password"][0] == "apiPassword" {
j := struct {
Proxies []string
}{Proxies: code.UP.Proxy}
w.Header().Set("Access-Control-Allow-Origin", corsAddrSite)
json.NewEncoder(w).Encode(j)
} else {
// here is a problem
fmt.Fprintln(w, "<script>alert('Wrong Password')</script>")
http.ServeFile(w, r, "template/api/api.html")
}
}
}
But i'v get http: multiple response.WriteHeader calls error.
How to do it right?
You cannot write to the http.ResponseWriter more than once depending on the HTTP spec.
from the go docs https://golang.org/pkg/net/http/#ResponseWriter
To solve your issue, you could have the script tags inside the template file, or make a new template. You could also tailor the response by adding the alert script before you send it. Maybe with template files.
However a proper solution to this problem might be to have more logic in the actual html served, the front end should display a response based on the status code or response body.

Making golang Gorilla CORS handler work

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)))
}
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)))
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)
}
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:
AllowedOrigin function
How can you debug a CORS request with cURL?
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
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)
})
}
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))
}
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).
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.

Access Control Allow Origin header not present with fetch api call

So I'm trying to use isomorphic-fetch https://www.npmjs.com/package/isomorphic-fetch
I have a server written in go that is giving back JSON data. This is how I'm making the call -
export function fetchDistricts(geoState) {
return function (dispatch) {
dispatch(requestDistricts(geoState));
return fetch(`http://localhost:8100/districts/`)
.then(response => {console.log(response);})
.then(json => {
console.log("json");
});
}
I get this error in the chrome console
Fetch API cannot load http://localhost:8100/districts/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8200' is therefore not allowed access.
This is weird, because in my handler I am doing this
func getDistricts(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/jsonp;charset=UTF-8")
w.WriteHeader(http.StatusOK)
w.Header().Set("Access-Control-Allow-Origin", "*")
rows, err := db.Query("SELECT * from districts")
//other code here
Further, this was working
var activitiesDfD = $.ajax({
url: "http://localhost:8100/district/1/activities",
type: "GET",
dataType: "json"
});
$.when(activitiesDfD).then(function(data, textStatus, jqXhr) {
Why would this fail when using the fetch API and how do I get around this?
Edit-
I've now tried this
func getDistricts(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/jsonp;charset=UTF-8")
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get(`origin`))
w.WriteHeader(http.StatusOK)
Incorporating the two suggestions below - but the error is the same.
Almost all web browsers reject the origin "*". Therefore sending "*" as the Access-Control-Allow-Origin header results in a same-origin-policy violation.
Fortunately there is a work-around. If you look at the gin-cors code that handles this, what it does instead is to re-send the "origin" header sent by the browser. So to make * work, you'd have to do this:
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get(`origin`))
I ended up using this middleware https://github.com/rs/cors, and that got everything working correctly.

Resources