Javascript fetch()不止一次ping我的golang rest端点?

Joh*_*ohn 5 javascript go cors

我有一个我不明白的错误,如果我在Golang上做错了,或者我在使用javascript fetch()语句做错了.这是一个简单的情况,我想fetch()调用golang端点并简单地打印hello world.但出于某种原因,golang代码会发射两次.这是我的代码:

// 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
}
Run Code Online (Sandbox Code Playgroud)

然后我去了JSFiddle.net并粘贴了以下JS代码并按下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',
  }
})
Run Code Online (Sandbox Code Playgroud)

由于某种原因,我的控制台两次打印Hello World:

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
Run Code Online (Sandbox Code Playgroud)

我将我的JS代码转换为PHP,将我的PHP代码放在与golang代码相同的localhost上,从控制台运行PHP代码,并获得仅执行ONCE的golang代码的预期结果.有谁知道发生了什么?我误解了一些事情fetch()吗?我设置标题的方式是否有不寻常的事情发生?

小智 8

router.HandleFunc("/page", page.Search).Methods("GET","OPTIONS")
Run Code Online (Sandbox Code Playgroud)

告诉路由器直接 GET和OPTIONS方法来搜索处理程序.

CORS预检通常由单独的处理程序处理.在您的情况下,您告诉Go使用搜索处理程序处理飞行前检查和获取请求.

通常,您希望与后续请求分开处理飞行前检查,因此请求未执行两次(一次无理由).

一个非常简单的飞行前处理程序版本将是:

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")
}
Run Code Online (Sandbox Code Playgroud)

完整的Golang代码将是:

// 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
}
Run Code Online (Sandbox Code Playgroud)


Joh*_*ohn 0

这是spencedev的扩展答案

作为用户rmn用户所建议的,本文从CORS的角度区分了“简单请求”和“非简单请求”的概念:

https://codeburst.io/cors-story-of-requesting-twice-85219da7172d

Authorization从 CORS 的角度来看,我的标头中和 的存在Organization使我的 HTTP 请求成为“非简单请求”,并导致fetch()触发 GET 和 OPTION 请求,其中 OPTION 请求被称为 CORS 飞行前请求。正如 spencedev 指出的,我的 golang 代码

router.HandleFunc("/page", page.Search).Methods("GET","OPTIONS")
Run Code Online (Sandbox Code Playgroud)

page.Search应该处理 GET 和 OPTIONS 请求。

正如sberry指出的,Gorilla 库有一个处理 CORS 的包。下面是使用 Gorilla CORS 处理程序的 spencedev 答案的替代方案

// main.go
package main

import (

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

func main() {

    router := mux.NewRouter()

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

    headersOk := handlers.AllowedHeaders([]string{"Content-Type","Authorization","Organization"})
    originsOk := handlers.AllowedOrigins([]string{"*"})
    methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"})
    log.Fatal(http.ListenAndServe(":8000", handlers.CORS(originsOk, headersOk, methodsOk)(router)))

}

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


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

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

  log.Println("Hello World")
  w.Write([]byte("{}"))
  return
}
Run Code Online (Sandbox Code Playgroud)