Adding CORS header into gqlgen default example? - go

I'm trying to setup up a basic stack using React, Apollo served by graphQL served by Go through gqlgen.
I'm following the getting started guide for gqlgen, which works fine.
I'm also following the getting started guide for Apollo, which works fine.
My trouble lies in the chrome not allowing me to ping the backend when I try to stitch the two together. I get a CORS preflight header error which is understandable, considering React is on localhost:3000 and the Go server is on localhost:8080
gqlgen provided a helpful example on how to get CORS running on the Go server here using chi and go-cors. However the example provided is based of the starwars example of gqlgen rather then the getting-started example.
I modified the CORS example to the following:
package main
import (
"net/http"
"github.com/99designs/gqlgen/graphql/handler"
"github.com/99designs/gqlgen/graphql/handler/transport"
"github.com/99designs/gqlgen/graphql/playground"
"github.com/FlockSupport/graphql/graph"
"github.com/FlockSupport/graphql/graph/generated"
"github.com/go-chi/chi"
"github.com/gorilla/websocket"
"github.com/rs/cors"
)
func main() {
router := chi.NewRouter()
// Add CORS middleware around every request
// See https://github.com/rs/cors for full option listing
router.Use(cors.New(cors.Options{
AllowedOrigins: []string{"http://localhost:8080"},
AllowCredentials: true,
Debug: true,
}).Handler)
// srv := handler.New(starwars.NewExecutableSchema(starwars.NewResolver())) // MODIFIED THIS.
srv := handler.New(generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{}}))
srv.AddTransport(&transport.Websocket{
Upgrader: websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
// Check against your desired domains here
return r.Host == "example.org"
},
ReadBufferSize: 1024,
WriteBufferSize: 1024,
},
})
router.Handle("/", playground.Handler("GraphQL Playground", "/query"))
// router.Handle("/", handler.Playground("Starwars", "/query")) // MODIFIED THIS.
router.Handle("/query", srv)
err := http.ListenAndServe(":8080", router)
if err != nil {
panic(err)
}
}
The server runs, but testing queries out through the playground (localhost:8080 so CORS shouldn't matter here) gives the following error:
{
"error": {
"errors": [
{
"message": "transport not supported"
}
],
"data": null
}
}
This also doesn't work even if I setup the Chi router only:
func main() {
router := chi.NewRouter()
srv := handler.New(generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{}}))
router.Handle("/", playground.Handler("GraphQL Playground", "/query"))
router.Handle("/query", srv)
err := http.ListenAndServe(":8080", router)
if err != nil {
panic(err)
}
}
I get a Server Cannot Be Reached when I access localhost:8080 playground.
How can I enable CORS properly on the gqlgen Go server so React can ping it?

I had the same issue, and solved it by using the NewDefaultServer method instead of New.
srv := handler.NewDefaultServer(.....)

You need to provide CORS checks as middleware.
For instance you could use "github.com/rs/cors". It could looks like following code
mux.Handle("/playground", handler.Playground("API", "/gql"))
mux.Handle("/gql",
handler.GraphQL(
gen.NewExecutableSchema(gen.Config{Resolvers: resolver.New(dep)}),
handler.ErrorPresenter(errorCustomize)))
h := cors.AllowAll().Handler(util.JwtMiddleware(mux))
srv := &http.Server{
Addr: ":8087",
Handler: h,
}

I had the same problem but actually it was because, when I did the request to the server I didn't add the header Content-Type: application/json. curiously I had to include the Content-Length header when using postman.
so, when I tried:
curl --request POST \
--url http://localhost:8000/ \
--data '{"query":"query {\n televisions {\n id\n }\n}"}'
This was the response:
{"errors":[{"message":"transport not supported"}],"data":null}
But when I added the header:
curl --request POST \
--url http://localhost:8000/ \
--header 'content-type: application/json' \
--data '{"query":"query {\n televisions {\n id\n }\n}"}'
The server response normally:
{"data":{"televisions":[]}}

Related

Dynamically instantiating HandleFunc

I've been trying to wrap my head around implementing HandleFunc() in a dynamic way.
This yaml file I want to turn into a local http web server in Go.
# Example .mock.yaml config
Endpoints:
- Resource: /city/1
Method: GET
Response: '{ Id": 1, "Name": "Albuquerque", "Population": 559.374, "State": "New Mexico" }'
StatusCode: 200
- Resource: /city
Method: POST
Response: '{ "Name": "Albuquerque", "Population": 559.374, "State": "New Mexico" }'
statusCode: 200
- Resource: /city/1
Method: PUT
Response: '{ "Population": 601.255 }'
StatusCode: 204
- Resource: /city/1
Method: DELETE
StatusCode: 204
Now I managed to implement something as following:
package utils
import (
"io"
"net/http"
"github.com/gorilla/mux"
"github.com/bschaatsbergen/mock/model"
)
func StartServer(conf model.Config, port string) {
r := mux.NewRouter()
for _, endpoint := range conf.Endpoints {
r.HandleFunc(endpoint.Resource, func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(endpoint.StatusCOde)
io.WriteString(w, endpoint.Response)
}).Methods(endpoint.Method)
}
address := ":" + port
http.ListenAndServe(address, r)
}
^ https://play.golang.org/p/YoTTUKnQL_5
But this doesn't cut it as it overwrites an earlier created route ('/city/1' GET, DELETE and POST are conflicting).
If anyone could give me a hand on how to dynamically translate the yaml config into a local web server, it would be appreciated!
I quote from #mkopriva, thanks!
I'm fairly certain your problem is the same as the one here:
stackoverflow.com/questions/69595865/…. i.e. All of your anon handler
closures capture one and the same variable, the iteration variable,
which, at the end of the loop, will hold the last element of the
Endpoints slice. Correct? Can the question be closed as duplicate?
Working piece of code:
func StartServer(conf model.Config, port string) {
r := mux.NewRouter()
for _, endpoint := range conf.Endpoints {
route := endpoint
r.HandleFunc(route.Resource, func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(route.Statuscode)
io.WriteString(w, route.Response)
}).Methods(route.Method)
}
address := ":" + port
log.Fatal(http.ListenAndServe(address, r))
}

how to enable gqlgen server for CORS issue?

GOAL:
Svelte app running on port 5000 wants to query data from gqlgen GraphQL server running on port 8080, both on localhost. I tried to query a public graphql API such as https://api.react-finland.fi/graphql just to test if my Svelte app (port:5000) is working well and it is. So I think the problem lies with my Go graphql server (port:8080).
SYSTEM
go version
go version go1.15 linux/amd64
go 1.15
require (
github.com/99designs/gqlgen v0.12.1
github.com/go-chi/chi v4.1.2+incompatible
github.com/gorilla/websocket v1.4.2
github.com/rs/cors v1.7.0
github.com/vektah/gqlparser/v2 v2.0.1
)
HAVE TRIED
According to the official site, I have tried their approach.
And here is my code:
func main() {
port := os.Getenv("PORT")
if port == "" {
port = defaultPort
}
router := chi.NewRouter()
// Add CORS middleware around every request
// See https://github.com/rs/cors for full option listing
router.Use(cors.New(cors.Options{
AllowedOrigins: []string{"http://localhost:5000", "http://localhost:8080"},
AllowOriginFunc: func(origin string) bool { return true },
AllowedMethods: []string{},
AllowedHeaders: []string{},
AllowCredentials: true,
Debug: true,
}).Handler)
srv := handler.NewDefaultServer(generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{}}))
srv.AddTransport(&transport.Websocket{
Upgrader: websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
// Check against your desired domains here
return r.Host == "localhost:8080"
},
ReadBufferSize: 1024,
WriteBufferSize: 1024,
},
})
http.Handle("/", playground.Handler("GraphQL playground", "/query"))
http.Handle("/query", srv)
log.Printf("connect to http://localhost:%s/ for GraphQL playground", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
err := http.ListenAndServe(":8080", router)
if err != nil {
panic(err)
}
}
RESULT
I got those errors:
Access to fetch at 'http://localhost:8080/' from origin 'http://localhost:5000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
HOW TO SOLVE?
I've read quite some documentation and googled around... but couldn't figure out exactly how to and I don't know how to debug to find solution. I just learnt GO for two days so far. Can someone help? Thanks!
Hi now someone helped me find out the major problems I have:
1.Yes, we can use just one in the line where I wrote :
AllowedOrigins: []string{"http://localhost:5000", "http://localhost:8080"},
AllowOriginFunc: func(origin string) bool { return true },
In fact the second one will overwrite the first one, so can choose just one.
2.In this part
log.Fatal(http.ListenAndServe(":"+port, nil))
err := http.ListenAndServe(":8080", router)
if err != nil {
panic(err)
}
I have written the http:ListenAndServe twice, so it didn't get to the second one. I deleted the log.Fatal(http.ListenAndServe(":"+port, nil))
3.Since we passed the middleware to router for http request, we need to use that instead of http.handle. So those two lines were wrong:
http.Handle("/", playground.Handler("GraphQL playground", "/query"))
http.Handle("/query", srv)
The right way to do it should be :
router.Handle("/", playground.Handler("GraphQL playground", "/query"))
router.Handle("/query", srv)
In fact that was shown in the official approach... but somehow after I tried several different solutions I got lost in the rabbit holes and didn't see those obvious errors! >.<
After the above changes now it works finally! Thanks for your help!

How to get header data of postman using gin package in golang

I want to get the header data using gin package(golang) in the postman but I don't get any idea how to do it. I search it for google but not getting any answer. Can anyone help me to get the data from the postman header the data I want to get is shown in image.
Image:-
You can get the token header with c.Request.Header["Token"].
Here is a sample code.
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/test", func(c *gin.Context) {
c.JSON(200, gin.H{
"token_data": c.Request.Header["Token"],
})
})
r.Run() // listen and serve on 0.0.0.0:8080
}
Here is an example screenshot of postman.
I use this code and work well
func getProduct(c *gin.Context) {
token := strings.Split(c.Request.Header["Authorization"][0], " ")[1]
c.JSON(200, gin.H{"result": "get product", "token": token})
}
Here is the test data
GET http://localhost:8081/api/v2/product HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWF0IjoxNTg4OTI4NzY0LCJleHAiOjE1ODg5MzM3NjR9.GrPK-7uEsfpdAYamoqaDFclYwTZ3LOlspoEXUORfSuY
Instead of accessing request object directly, gin provides a getter (easier to use and makes code cleaner). Based on #Shiva accepted answer:
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/test", func(c *gin.Context) {
c.JSON(200, gin.H{
// or c.GetHeader("Authorization")
"token_data": c.GetHeader("Token"),
})
})
r.Run() // listen and serve on 0.0.0.0:8080
}

Go & Socket.io HTTP + WSS on one port with CORS?

Brand new to Go.. Still obviously learning the syntax and the basics.. But I do have a specific goal in mind..
I'm trying to just get a simple server up on :8080 that can respond to both HTTP and socket.io (via /socket.io/ url), specificaly with CORS.
My code:
package main
import (
"log"
"net/http"
"github.com/rs/cors"
"github.com/googollee/go-socket.io"
)
func SayHelloWorld(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, World!"))
}
func main() {
c := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
AllowCredentials: true,
})
server, err := socketio.NewServer(nil)
if err != nil {
log.Fatal(err)
}
server.On("connection", func(so socketio.Socket) {
log.Println("on connection")
so.Join("chat")
so.On("chat message", func(msg string) {
log.Println("emit:", so.Emit("chat message", msg))
so.BroadcastTo("chat", "chat message", msg)
})
so.On("disconnection", func() {
log.Println("on disconnect")
})
})
server.On("error", func(so socketio.Socket, err error) {
log.Println("error:", err)
})
http.Handle("/socket.io/", c.Handler(server))
http.HandleFunc("/", SayHelloWorld)
log.Println("Serving at localhost:8080...")
log.Fatal(http.ListenAndServe(":8080", nil))
}
On the client side I'm still seeing:
WebSocket connection to 'wss://api.domain.com/socket.io/?EIO=3&transport=websocket&sid=xNWd9aZvwDnZOrXkOBaC' failed: WebSocket is closed before the connection is established.
(index):1 XMLHttpRequest cannot load https://api.domain.com/socket.io/?EIO=3&transport=polling&t=1420662449235-3932&sid=xNWd9aZvwDnZOrXkOBaC. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://fiddle.jshell.net' is therefore not allowed access.
EDIT #1:
So I've been banging my head away trying to understand why I can't connect.. Came across an even more confusing piece of the puzzle?
https://gist.github.com/acoyfellow/167b055da85248c94fc4
The above gist is the code of my golang server + the browser code used to connect.. This code will send 30 HTTP GET requests per second to the backend, without connecting, upgrading, or giving any errors (client or server side).. it essentially DDOS's my own backend?
Someone, please someone tell me I'm doing something stupid.. This is quite the pickle :P
EDIT #2:
I can stop the "DDOS" by simply adjusting the trailing / on the URL of the socket.io endpoint in Go.. So: mux.Handle("/socket.io", server) to mux.Handle("/socket.io/", server) will now produce error messages and connection attempts with error responses of:
WebSocket connection to 'wss://api.domain.com/socket.io/?EIO=3&transport=websocket&sid=0TzmTM_QtF1TaS4exiwF' failed: Error during WebSocket handshake: Unexpected response code: 400 socket.io-1.2.1.js:2
GET https://api.domain.com/socket.io/?EIO=3&transport=polling&t=1420743204485-62&sid=0TzmTM_QtF1TaS4exiwF 400 (Bad Request)
So I gave up using googoolee's Socket.io implementation and went with gorilla's.
I checked out their examples: https://github.com/gorilla/websocket/tree/master/examples/chat
Checked out their docs: http://www.gorillatoolkit.org/pkg/websocket
-- Under Origin Considerations I found:
An application can allow connections from any origin by specifying a function that always returns true:
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool { return true },
}
I added this CheckOrigin function to the conn.go file in their example, and was able to get a CORS socket server talking to a browser.
As a first adventure into Golang, this was frustrating and fun.. +1 to anyone else learning
Don't you mean http + ws or https + wss. If you remove a s from wss, you should be able to connect.
If you want tls for web socket (wss), then you need to http.ListenAndServeTLS.
It appears that CORS does not apply to WebSockets. Per this related question "With WebSocket, there is an "origin" header, which browser MUST fill with the origin of the HTML containing the JS that opens the WS connection."
As stated here:
Cross origin websockets with Golang
How about in your SayHelloWorld func, adding something like:
w.Header().Set("Access-Control-Allow-Origin", "*")
Or, possibly better:
if origin := r.Header.Get("Origin"); origin != "" {
w.Header().Set("Access-Control-Allow-Origin", origin)
}
I get the similar problerm with normal ajax call. It require more work in both front-end and backend. I belive most popular front-end libs liek JQuery or AngularJS handle these very well.
I see you're using the https://github.com/rs/cors package but you don't include the usage of that package, here is the implement with only Go std package:
type CrossOriginServer struct {}
func (s *CrossOriginServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
// you may need to add some more headers here
allowHeaders := "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization"
if origin := req.Header.Get("Origin"); validOrigin(origin) {
rw.Header().Set("Access-Control-Allow-Origin", origin)
rw.Header().Set("Access-Control-Allow-Methods", "POST, PUT, PATCH, GET, DELETE")
rw.Header().Set("Access-Control-Allow-Headers", allowHeaders)
}
if req.Method == "OPTIONS" {
return
}
// if you want, you can use gorilla/mux or any routing package here
mux := http.NewServeMux()
mux.Handle("/socket.io/", c.Handler(server))
mux.HandleFunc("/", SayHelloWorld)
mux.ServeHTTP(rw, req)
}
func validOrigin(origin string) bool {
allowOrigin := []string{
"http://localhost:8081",
"http://example.com"
}
for _, v := range allowOrigin {
if origin == v {
return true
}
}
return false
}
func main() {
// do you stuff
// ...
// ...
http.ListenAndServe(":8080", &CrossOriginServer{})
}

Gorilla Mux router from inside handler only works once then gives 404 page not found

I'm using Gorilla mux as my router and I'm having a very strange behaviour. On the first request to the server, I get a valid response. But on subsequent requests, I receive a 404 page not found. There are no errors in the console.
My code is pretty straightforward (it can be copy-pasted to test it right out):
package main
import (
"fmt"
"github.com/gorilla/mux"
"log"
"net/http"
)
func main() {
router := mux.NewRouter()
router.HandleFunc("/", RootHandler).Name("root")
http.Handle("/", router)
log.Println("Listening on port 1337...")
if err := http.ListenAndServe(":1337", nil); err != nil {
log.Fatal("http.ListenAndServe: ", err)
}
}
func RootHandler(w http.ResponseWriter, r *http.Request) {
content := "Welcome to "
rootUrl, err := mux.CurrentRoute(r).Subrouter().Get("root").URL()
if err != nil {
log.Printf("mux.CurrentRoute(r).Subrouter().Get(\"root\").URL(): ", err)
}
response := content + rootUrl.String()
fmt.Fprintf(w, response)
}
After some code commenting and tests, it seems the following line is the culprit:
rootUrl, err := mux.CurrentRoute(r).Subrouter().Get("root").URL()
This method of getting the router inside the handler using the current request comes from another StackOverflow post: How to call a route by its name from inside a handler?
But for a strange reason, it only works once:
shell-1$ go run servertest.go
2014/10/30 13:31:34 Listening on port 1337...
shell-2$ curl http://127.0.0.1:1337
Welcome to /
shell-2$ curl http://127.0.0.1:1337
404 page not found
As you can see, there are no errors in the console.
Does someone have an idea of why it only works once ?
The problem is Subrouter() isn't made to return the router, but to create one, thus it changes the matcher of the router it is called on, making you lose the handler.
You could try passing the router to the handler using closures instead.
func RootHandler(router *mux.Router) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
...
}
}
i run to this problem and fixe it by re initiating the methods
//create a subrouter separately \
subRoute := mux.CurrentRoute(req).Subrouter() \
//Call the Route u want and store the URL
url, err := subRoute.Get("check_authorization").URL("id", key, "password", token)
// re-initiate the method to GET or whatever u had before
subRoute.Methods(http.MethodGet)

Resources