'Enable CORS in Golang
Hi I'm implementing rest apis and for that I want to allow cross origin requests to be served.
What I am currently doing:
Go-server code on AWS:
func (c *UserController) Login(w http.ResponseWriter, r *http.Request, ctx *rack.Context) {
w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
...
...
c.render.Json(w,rsp, http.StatusOK)
return
}
Ajax code on localhost:
<script>
$( document ).ready(function() {
console.log( "ready!" );
$.ajax({
url: 'http://ip:8080/login',
crossDomain: true, //set as a cross domain requests
withCredentials:false,
type: 'post',
success: function (data) {
alert("Data " + data);
},
});
});
I am getting the following error on browser console: XMLHttpRequest cannot load http://ip:8080/login. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8081' is therefore not allowed access. The response had HTTP status code 422.
I tried adding preflight options:
func corsRoute(app *app.App) {
allowedHeaders := "Accept, Content-Type, Content-Length, Accept-Encoding, Authorization,X-CSRF-Token"
f := func(w http.ResponseWriter, r *http.Request) {
if origin := r.Header.Get("Origin"); origin != "" {
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", allowedHeaders)
w.Header().Set("Access-Control-Expose-Headers", "Authorization")
}
return
}
app.Router.Options("/*p", f, publicRouteConstraint)
}
But it is not working.
What can be done to fix it.
Solution 1:[1]
I use gorilla/mux package to build Go RESTful API server, and client use JavaScript Request can work,
My Go Server runs at localhost:9091, and the Server code:
router := mux.NewRouter()
//api route is /people,
//Methods("GET", "OPTIONS") means it support GET, OPTIONS
router.HandleFunc("/people", GetPeopleAPI).Methods("GET", "OPTIONS")
log.Fatal(http.ListenAndServe(":9091", router))
I find giving OPTIONS here is important, otherwise error will occur:
OPTIONS http://localhost:9091/people 405 (Method Not Allowed)
Failed to load http://localhost:9091/people: 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:9092' is therefore not allowed access. The response had HTTP status code 405.
after allow OPTIONS it works great. I get the idea from This Article.
Besides, MDN CORS doc mention:
Additionally, for HTTP request methods that can cause side-effects on server's data, the specification mandates that browsers "preflight" the request, soliciting supported methods from the server with an HTTP OPTIONS request method, and then, upon "approval" from the server, sending the actual request with the actual HTTP request method.
Following is the api GetPeopleAPI method, note in the method I give comment //Allow CORS here By * or specific origin, I have another similar answer explaining the concept of CORS Here:
func GetPeopleAPI(w http.ResponseWriter, r *http.Request) {
//Allow CORS here By * or specific origin
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
// return "OKOK"
json.NewEncoder(w).Encode("OKOK")
}
In the client, I use html with javascript on localhost:9092, and javascript will send request to server from localhost:9092
function GetPeople() {
try {
var xhttp = new XMLHttpRequest();
xhttp.open("GET", "http://localhost:9091/people", false);
xhttp.setRequestHeader("Content-type", "text/html");
xhttp.send();
var response = JSON.parse(xhttp.response);
alert(xhttp.response);
} catch (error) {
alert(error.message);
}
}
and the request can successfully get response "OKOK" .
You can also check response/request header information by tools like Fiddler .
Solution 2:[2]
Thanks for the clue - it's all in the header! I use only these golang headers on the server side:
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.Header().Set("Access-Control-Allow-Origin", "*")
Now works with this JQuery:
<script
src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js">
</script>
<script>
$.ajax({
type: 'GET',
url: 'https://www.XXXXXXX.org/QueryUserID?u=juXXXXny&p=blXXXXXne',
crossDomain: true,
dataType: 'text',
success: function(responseData, textStatus, jqXHR) {
alert(responseData);
},
error: function (responseData, textStatus, errorThrown) {
alert('POST failed.');
}
});
</script>
Solution 3:[3]
You can check this out https://github.com/rs/cors
This would handle the Options Request as well
Solution 4:[4]
GO SERVER SETTING :
package main
import (
"net/http"
)
func Cors(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=ascii")
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers","Content-Type,access-control-allow-origin, access-control-allow-headers")
w.Write([]byte("Hello, World!"))
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/plm/cors",Cors)
http.ListenAndServe(":8081", mux)
}
Client JQUERY AJAX SETTING :
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js">
</script>
</head>
<body>
<br> Please confirm to proceed : <button class="myConfirmButton1">Go!!</button>
<div id="loader1" style="display:none;">loading...</div>
<div id="loader2" style="display:none;">...done</div>
<div id="myFeedback1"></div>
<script>
$(document).ready(function(){
$(".myConfirmButton1").click(function(){
$('#loader1').show();
$.ajax({
url:"http://[webserver.domain.com:8081]/plm/cors",
dataType:'html',
headers: {"Access-Control-Allow-Origin": "*", "Access-Control-Allow-Headers": "access-control-allow-origin, access-control-allow-headers"},
type:'get',
contentType: 'application/x-www-form-urlencoded',
success: function( data, textStatus, jQxhr ){
$('#loader1').hide();
$('#loader2').show();
$('#myFeedback1').html( data );
},
error: function( jqXhr, textStatus, errorThrown ){
$('#loader1').hide();
$('#myFeedback1').html( errorThrown );
alert("error" + errorThrown);
}
});
});
});
</script>
</body>
Client TEST REQUEST with curl and obtained response :
curl -iXGET http://[webserver.domain.com:8081]/plm/cors
HTTP/1.1 200 OK
Access-Control-Allow-Headers: Content-Type,access-control-allow-origin, access-control-allow-headers
Access-Control-Allow-Origin: *
Content-Type: text/html; charset=ascii
Date: Wed, 17 Jan 2018 13:28:28 GMT
Content-Length: 13
Hello, World!
Solution 5:[5]
For allowing CORS your server should to catch all Preflight request that's browser sends before real query with OPTIONS method to the same path.
First way is managing this manually by something like this:
func setupCORS(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) {
setupCORS(&w, req)
if (*req).Method == "OPTIONS" {
return
}
// process the request...
}
The second way is use ready to third party pkg like 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.AllowAll() setup the middleware with default options being
// all origins accepted with simple methods (GET, POST). See
// documentation below for more options.
handler := cors.AllowAll().Handler(mux)
http.ListenAndServe(":8080", handler)
}
Solution 6:[6]
Adding to all the great answers: instead of setting the headers in every handler you probably want to use the appHandler pattern:
type Handler func(http.ResponseWriter, *http.Request) *Error
func (fn Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
if e := fn(w, r); e != nil { // e is *appError, not os.Error.
http.Error(w, e.Message, e.Code)
}
}
func Login(w http.ResponseWriter, r *http.Request) *Error {
...
return nil
}
r.Handle("/login", Handler(Login))
Solution 7:[7]
Ok this problem gave me some issues but found a fix u have to use
github.com/gorilla/handlers
along with the gollila/mux lib
so here is a snippet
r := mux.NewRouter()
header := handlers.AllowedHeaders([]string{"X-Requested-With", "Content-Type", "Authorization"})
methods := handlers.AllowedMethods([]string{"GET", "POST", "PUT", "HEAD", "OPTIONS"})
origins := handlers.AllowedOrigins([]string{"*"})
api := r.PathPrefix("/api").Subrouter()
api.Handle("/route", function).Methods("GET", "OPTIONS")
r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
_, _ = fmt.Fprint(w, "hello")
})
err := http.ListenAndServe(":9000", handlers.CORS(header, methods, origins)(r))
if err != nil {
fmt.Println(err)
}
this should fix ur problem
Solution 8:[8]
First : cors
svc.Handle("/", restAPI.Serve(nil))After, I fix: Handle -> HandleFunc
svc.HandleFunc("/", func(rw http.ResponseWriter, req *http.Request) { setupHeader(rw, req) if req.Method == "OPTIONS" { rw.WriteHeader(http.StatusOK) return } restAPI.Serve(nil).ServeHTTP(rw, req) return }) func setupHeader(rw http.ResponseWriter, req *http.Request) { rw.Header().Set("Content-Type", "application/json") rw.Header().Set("Access-Control-Allow-Origin", "*") rw.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") rw.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") }
Solution 9:[9]
router := mux.NewRouter()
api := router.PathPrefix("/api/v1").Subrouter()
api.HandleFunc("/getLastDateOfAMonth", lastday.Handler).Methods(http.MethodPost)
c := cors.New(cors.Options{
AllowedOrigins: []string{"http://localhost:3000"},
AllowCredentials: true,
AllowedMethods: []string{"GET", "DELETE", "POST", "PUT"},
})
handler := c.Handler(router)
log.Fatal(http.ListenAndServe(":3001", handler))
Please check this -> https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request#:~:text=A%20CORS%20preflight%20request%20is,Headers%20%2C%20and%20the%20Origin%20header.
We all face CORS issue -> Fix -> The Backend server should Accept CORS. Add cors to your backend application. So that it understand CORS on Preflight request from the browser.
Solution 10:[10]
i have been facing the same issue lately. and i found out that only setting the Access-Control-Allow-Origin didnt fixed my issue. the problem i had was the fact that browser first sends a OPTIONS request
so i had to check for OPTIONS requests and send ok response to fix this issue. and doing this actually fixed most of my cros related problems.
sample code of how i did is listed below
This is my server side middleware whcih is responsible for handling all the CROS related things
func CORSMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*") // change this later
w.Header().Set("Access-Control-Allow-Credentials", "true")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, DELETE, OPTIONS")
if r.Method == "OPTIONS" {
w.WriteHeader(204)
return
}
next.ServeHTTP(w, r)
})
}
for my fronted i usually use react framework. and below is a example code of me creating a post. and how i would usually use this in fronted.
( also for my forms im using formik here )
<Formik
initialValues={{ title: "", slug: "" }}
validate={(values) => {
const errors = {};
if (!values.title) {
errors.title = "Required";
} else if (!values.slug) {
errors.slug = "Required";
}
return errors;
}}
onSubmit={(resp, { setSubmitting }) => {
setTimeout(() => {
var encodedPost = btoa(value);
resp.post = encodedPost;
console.log(JSON.stringify(resp, null, 2));
const cookies = new Cookies();
let authtoken = cookies.get("auth");
/* sending the request */
var myHeaders = new Headers();
myHeaders.append("Authorization", `Bearer ${authtoken}`);
myHeaders.append("Content-Type", "application/json");
var requestOptions = {
method: "POST",
headers: myHeaders,
body: JSON.stringify(resp),
};
fetch(
"http://localhost:4000/api/v1/post/create",
requestOptions
)
.then((response) => response.json())
.then((result) => {
console.log(result);
alert(JSON.stringify(result, null, 2));
})
.catch((error) => {
alert(JSON.stringify(error, null, 2));
});
setSubmitting(false);
}, 400);
}}
>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
