Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript fetch() is pinging my golang rest end point more than once?

I have a bug that I don't understand is if I'm doing something wrong with Golang or if I'm doing something wrong with javascript fetch() statement. It's a simple situation where I want to make fetch() call to a golang endpoint and simply print hello world. But for some reason, the golang code is firing twice. Here's my code:

// main.go

package main

import (

  "route/page"
  "github.com/gorilla/mux"
  "log"
  "net/http"
)

func main() {

    router := mux.NewRouter()
    router.HandleFunc("/page", page.Search).Methods("GET","OPTIONS")
    log.Fatal(http.ListenAndServe(":8000", router))

}


//route/page.go

package page
import (
  "net/http"
  "log"
)

func Search(w http.ResponseWriter, r *http.Request) {

  w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, Authorization, Organization")
  w.Header().Set("Access-Control-Allow-Origin", "*")
  w.Header().Set("Content-Type", "application/json")

  log.Println(r.Header.Get("authorization"))

  log.Println("Hello World")
  w.Write([]byte("{}"))
  return
}

I then went to JSFiddle.net and pasted in the following JS code and pressed Run:

fetch('http://192.168.0.21:8000/page', {
  method: 'GET',
  headers: {
    'Content-Type': 'text/plain',
    'Organization': 'afae3@@@@@%2Fajfoij',
    'Authorization': 'Basic tajoie#$@asdfall%DF;++JI%2F3255',
  }
})

And for some reason my console prints Hello World twice like this:

john@ubuntu:~/go/src/myproject$ go run main.go
2018/09/20 18:42:29
2018/09/20 18:42:29 Hello World
2018/09/20 18:42:29 Basic tajoie#$@asdfall%DF;++JI%2F3255
2018/09/20 18:42:29 Hello World

I transcribed my JS code into PHP, put my PHP code on the same localhost as my golang code, ran the PHP code from console, and got the expected result of the golang code executing only ONCE. Does anyone know what's going on? Did I mis-understand something about fetch()? Is there something unusual going on with the way I set the headers?

like image 387
John Avatar asked Sep 20 '18 22:09

John


Video Answer


1 Answers

router.HandleFunc("/page", page.Search).Methods("GET","OPTIONS")

Tells the router to direct both GET and OPTIONS method to the Search handler.

CORS preflight is usually handled by a separate handler. In your case, you're telling Go to handle both the pre-flight check and the get request with Search handler.

Typically you would want to handle the pre-flight check separately from the follow on request, so the request isn't executing twice (once for no reason.)

An extremely simple version of a pre-flight handler would be:

func PreFlightHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Access-Control-Allow-Origin", "http://localhost")
    w.Header().Set("Vary", "Origin")
    w.Header().Set("Vary", "Access-Control-Request-Method")
    w.Header().Set("Vary", "Access-Control-Request-Headers")
    w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Origin, Accept")
    w.Header().Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS")
    w.Header().Set("Access-Control-Allow-Credentials", "true")
}

Full Golang code would be:

// main.go

package main

import (

  "route/page"
  "github.com/gorilla/mux"
  "log"
  "net/http"
)

func main() {

    router := mux.NewRouter()
    router.HandleFunc("/page", page.PreFlightHandler).Methods("OPTIONS")

    router.HandleFunc("/page", page.Search).Methods("GET")

    log.Fatal(http.ListenAndServe(":8000", router))

}

// route/page.go
package page
import (
  "net/http"
  "log"
)

func PreFlightHandler(w http.ResponseWriter, r *http.Request) {

    w.Header().Set("Access-Control-Allow-Origin", "*")
    w.Header().Set("Vary", "Origin")
    w.Header().Set("Vary", "Access-Control-Request-Method")
    w.Header().Set("Vary", "Access-Control-Request-Headers")
    w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Origin, Accept, Authorization, Organization")
    w.Header().Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS")
    w.Header().Set("Access-Control-Allow-Credentials", "true")
}
func Search(w http.ResponseWriter, r *http.Request) {

  log.Println(r.Header.Get("authorization"))

  log.Println("Hello World")
  w.Write([]byte("{}"))
  return
}
like image 79
spencdev Avatar answered Sep 21 '22 17:09

spencdev