[Go language] Seien Sie vorsichtig, wenn Sie einen Server mit mux + cors + alice erstellen. Besonders darüber, wovon ich in Bezug auf CORS süchtig war.

Einführung

gorilla / mux, rs / cors, [justinas / alice](https: // github. Ich werde Ihnen zeigen, wie Sie einen Server erstellen, indem Sie com / justinas / alice kombinieren. Insbesondere als ich versuchte, die anzuwendende Middleware nach dem Pfad zu unterteilen, war ich von CORS abhängig, daher werde ich mich auf den Teil konzentrieren, der damit zusammenhängt. In dem Code, der veröffentlicht werden soll, handelt es sich im Grunde genommen um eine Schreibweise, bei der Fehler ignoriert werden. Schreiben Sie sie daher gegebenenfalls neu. Beachten Sie außerdem, dass "..." im Code einfach Abkürzung bedeutet und keine grammatikalische Bedeutung hat. Seien Sie also beim Kopieren vorsichtig.

tl;dr

Achten Sie auf die OPTIONS-Methode der Preflight-Anfrage.

Ein kurzer Überblick über Mux, Cors und Alice

Es werden einige Beispiele gezeigt, aber da es sich um den in jeder README-Datei beschriebenen Basisinhalt handelt, fahren Sie, falls Sie ihn kennen, mit [Implementierungssubjekt](# Implementierungssubjekt) fort.

gorilla/mux

Hier ist ein Beispiel für einen Server, der mux verwendet.

package main

import (
  "encoding/json"
  "net/http"

  "github.com/gorilla/mux"
)

type Character struct {
  Name string `json:"name"`
}

func pilotFunc(w http.ResponseWriter, r *http.Request) {
  res, _ := json.Marshal(Character{"Shinji"})
  w.WriteHeader(http.StatusOK)
  w.Write(res)
}

func angelFunc(w http.ResponseWriter, r *http.Request) {
  res, _ := json.Marshal(Character{"Adam"})
  w.WriteHeader(http.StatusOK)
  w.Write(res)
}

func main() {
  r := mux.NewRouter()  //r ist*mux.Routertyp
  r.Methods("GET").Path("/pilot").HandlerFunc(pilotFunc)  // r.zu Routen*mux.Route hinzufügen.
  r.Methods("GET").Path("/angel").HandlerFunc(angelFunc)  // r.Routen ist[]*mux.Da es sich um einen Routentyp handelt, können Sie immer mehr hinzufügen.

  http.ListenAndServe(":8000", r)
}

Damit ist ein kleiner Server fertig.

$ go run main.go

$ curl http://localhost:8000/pilot
{"name": "Shinji"}

Wenn ich jedoch "fetch" aus einem Browser verwende, wird folgende Fehlermeldung angezeigt:

Access to fetch at 'http://localhost:8000/user' from origin 'null' has been blocked by CORS policy: 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.

rs/cors

Hier kommt rs / cors ins Spiel. Für CORS ist die MDN-Seite leicht zu verstehen. Um CORS zu unterstützen, muss im Antwortheader auf der Serverseite ein geeigneter Wert festgelegt werden. Dafür ist Rs / cors verantwortlich. Ich habe dem vorherigen Beispiel rs / cors hinzugefügt.

import (
  ...
  "github.com/rs/cors"
)

func main() {
  r := mux.NewRouter()
  r.Methods("GET").Path("/pilot").HandlerFunc(pilotFunc)
  r.Methods("GET").Path("/angel").HandlerFunc(angelFunc)
  c := cors.Default().Handler(r)  //Hinzugefügt.

  http.ListenAndServe(":8000", c)  //Geändert von r nach c.
}

Sie können CORS nur durch Hinzufügen einer Zeile unterstützen.

justinas/alice

Darüber hinaus ist justinas / alice nützlich, wenn Sie Middleware hinzufügen möchten, die den Inhalt der Anforderung jedes Mal in das Protokoll ausgibt, oder Middleware, die den Wert aus dem Anforderungsheader abruft, bevor Sie die Hauptverarbeitung ausführen. (: //github.com/justinas/alice). Hier ist ein Beispiel für das Erstellen und Hinzufügen von Middleware, die Anforderungsinformationen im Protokoll ausgibt.

import (
  ...
  "log"

  "github.com/justinas/alice"
)

func logHandler(h http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    log.Printf("Method: %v; URL: %v; Protocol: %v", r.Method, r.URL, r.Proto)
    h.ServeHTTP(w, r)
  })
}

func main() {
  r := mux.NewRouter()
  r.Methods("GET").Path("/pilot").HandlerFunc(pilotFunc)
  r.Methods("GET").Path("/angel").HandlerFunc(angelFunc)
  c := cors.Default()
  chain := alice.New(c.Handler, logHandler).Then(r)  //Hinzugefügt.

  http.ListenAndServe(":8000", chain)  //Von c zu Kette geändert.
}

Implementierungsgegenstand

Übrigens gorilla / mux, rs / cors, [justinas / alice](https: // github.com/justinas/alice) wird erneut veröffentlicht (die Definitionen von packge, import und struct werden weggelassen).

...

func pilotFunc(w http.ResponseWriter, r *http.Request) {
  res, _ := json.Marshal(Character{"Shinji"})
  w.WriteHeader(http.StatusOK)
  w.Write(res)
}

func angelFunc(w http.ResponseWriter, r *http.Request) {
  res, _ := json.Marshal(Character{"Adam"})
  w.WriteHeader(http.StatusOK)
  w.Write(res)
}

func logHandler(h http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    log.Printf("Method: %v; URL: %v; Protocol: %v", r.Method, r.URL, r.Proto)
    h.ServeHTTP(w, r)
  })
}

func main() {
  r := mux.NewRouter()
  r.Methods("GET").Path("/pilot").HandlerFunc(pilotFunc)
  r.Methods("GET").Path("/angel").HandlerFunc(angelFunc)
  c := cors.Default()

  chain := alice.New(c.Handler, logHandler).Then(r)

  http.ListenAndServe(":8000", chain)
}

Bei dieser Methode wird die festgelegte Middleware jedoch auf alle Anforderungen angewendet.

Beispielsweise prüft / pilot, ob Sie angemeldet sind, und gibt nur Daten zurück, wenn Sie angemeldet sind . / angel kann jedoch nicht auf Anforderungen antworten, die Daten zurückgeben, selbst wenn Sie nicht angemeldet sind. Mit Qiita können Sie die Artikelseite auch dann sehen, wenn Sie nicht angemeldet sind. Stellen Sie sich jedoch eine Situation vor, in der Sie den Entwurf meiner Seite nur sehen können, wenn Sie angemeldet sind.

Versuchen Sie daher Folgendes. Wie ich bereits sagte, bin ich süchtig nach dem Schreibstil unten (lacht).

type funcHandler struct {
  handler func(w http.ResponseWriter, r *http.Request)
}

func (h funcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  h.handler(w, r)
}

func pilotFunc(w http.ResponseWriter, r *http.Request) {...}
func angelFunc(w http.ResponseWriter, r *http.Request) {...}
func logHandler(h http.Handler) http.Handler {...}

func authHandler(h http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    log.Println("Authentication")
    h.ServeHTTP(w, r)
  })
}

func main() {
  c := cors.Default()

  logChain := alice.New(c.Handler, logHandler)
  authChain := logChain.Append(authHandler)

  r := mux.NewRouter()
  r.Methods("GET").Path("/pilot").Handler(authChain.Then(funcHandler{pilotFunc}))
  r.Methods("GET").Path("/angel").Handler(logChain.Then(funcHandler{angelFunc}))

  http.ListenAndServe(":8000", r)
}

Nur wenn "Abrufen" zu "/ Pilot" erfolgt, wird "Authentifizierung" an die Standardausgabe ausgegeben. Jetzt ist es möglich, die anzuwendende Middleware nach dem Pfad zu trennen.

$go run main.go
2020/09/29 20:41:18 Method: GET; URL: /pilot; Protocol: HTTP/1.1
2020/09/29 20:41:18 Method: GET; URL: /pilot; Protocol: HTTP/1.1; Authentication
2020/09/29 20:41:18 Method: GET; URL: /angel; Protocol: HTTP/1.1

Ich werde die Erklärung hinterlassen, warum dies süchtig macht, und kurz den Mechanismus des Umschreibens auf diese Weise erläutern. Wenn Sie den Grund sehen möchten, warum Sie zuerst süchtig sind, fahren Sie mit [Grund für die Sucht] fort (# Grund für die Sucht).

Grund für das Umschreiben (Ergänzung)

Lassen Sie uns zunächst überprüfen, was "http.ListenAndServer" tut, aber [Go] Lesen des net / http-Pakets und Ausführen von http.HandleFunc / items / 1d1c64d05f7e72e31a98) ist ziemlich detailliert, also werde ich es hier werfen. Es scheint, dass die Informationen etwas alt sind und die geschriebene Zeilennummer nicht mit der tatsächlichen Zeilennummer übereinstimmt oder dass der Schreibstil etwas anders ist, aber ich denke, dass es kein Problem beim grundlegenden Verständnis gibt.

Abschließend übergibt "http.ListenAndServe" die empfangene Anforderung im zweiten Argument an "ServeHTTP" (diesmal "mux.Router"). Daher muss das zweite Argument "ServeHTTP" implementieren. "ServeHTTP" ist in "mux.Router" und im "http" -Paket implementiert.

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

Jede Variable vom Typ "http.Handler" kann das zweite Argument von "ListenAndServe" sein. Rufen Sie in diesem "ServeHTTP" das "ServeHTTP" eines anderen "http.Hanlder" auf, und rufen Sie in diesem "ServeHTTP" das "ServeHTTP" des anderen "http.Hanlder" auf und so weiter. Durch Verketten von .Handler` können Sie Anfragen der Reihe nach bearbeiten.

Mit anderen Worten, Sie können Handler verketten, indem Sie eine Funktion verwenden, die "http.Handler" als Argument verwendet und einen "http.Handler" -Typ zurückgibt, dh Middleware **. Betrachten Sie als Beispiel die Kette von "logHandler" und "authHandler".

chain := logHandler(authHandler(router))

Eine solche Kette kann in Betracht gezogen werden. Mit dieser Methode wird es jedoch schwieriger zu verstehen, wenn die Anzahl der Middleware zunimmt.

chain := alice.New(logHandler, authHandler).Then(router)

Wird kurz geschrieben.

Vergleichen wir hier vor und nach dem Umschreiben, damit die angewendete Middleware je nach Pfad geändert werden kann.

//Vorher ändern
func main() {
  r := mux.NewRouter()
  r.Methods("GET").Path("/pilot").HandlerFunc(pilotFunc)
  r.Methods("GET").Path("/angel").HandlerFunc(angelFunc)
  c := cors.Default()
  chain := alice.New(c.Handler, logHandler).Then(r)
  http.ListenAndServe(":8000", chain)
}

//Nach der veränderung
func main() {
  c := cors.Default()
  logChain := alice.New(c.Handler, logHandler)
  authChain := logChain.Append(authHandler)
  r := mux.NewRouter()
  r.Methods("GET").Path("/pilot").Handler(authChain.Then(funcHandler{pilotFunc}))
  r.Methods("GET").Path("/angel").Handler(logChain.Then(funcHandler{angelFunc}))
  http.ListenAndServe(":8000", r)
}

Daher ist der Ablauf, dem die Anforderung vor und nach der Änderung folgt

Bisherige: request -> CORS    -> logging -> routing           -> pilotFunc or angelFunc
Rückseite: request -> routing -> CORS    -> logging (-> auth) -> pilotFunc or angelFunc 

Sie können sehen, dass sich die Reihenfolge des Routings geändert hat. Dies ist einer der Gründe, warum ich süchtig danach bin.

Als nächstes werde ich erklären, warum ich von CORS abhängig bin, was das Hauptthema dieses Artikels ist.

Gründe, süchtig zu werden

Zunächst werde ich eine Implementierung des Authentifizierungsmechanismus beschreiben (Details werden später beschrieben), die dem tatsächlichen etwas näher kommt.

func authHandler(h http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    token := r.Header.Get("Token")
    if token == "" {
      w.WriteHeader(http.StatusUnauthorized)
      return
    }
    h.ServeHTTP(w, r)
  })
}
gesamter Code
package main

import (
  "encoding/json"
  "log"
  "net/http"

  "github.com/gorilla/mux"
  "github.com/justinas/alice"
  "github.com/rs/cors"
)

type Character struct {
  Name string `json:"name"`
}

type funcHandler struct {
  handler func(w http.ResponseWriter, r *http.Request)
}

func (h funcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  h.handler(w, r)
}

func pilotFunc(w http.ResponseWriter, r *http.Request) {
  res, _ := json.Marshal(Character{"Shinji"})
  w.WriteHeader(http.StatusOK)
  w.Write(res)
}

func angelFunc(w http.ResponseWriter, r *http.Request) {
  res, _ := json.Marshal(Character{"Adam"})
  w.WriteHeader(http.StatusOK)
  w.Write(res)
}

func logHandler(h http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    log.Printf("Method: %v; URL: %v; Protocol: %v", r.Method, r.URL, r.Proto)
    h.ServeHTTP(w, r)
  })
}

func authHandler(h http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    token := r.Header.Get("Token")
    if token == "" {
      w.WriteHeader(http.StatusUnauthorized)
      return
    }
    h.ServeHTTP(w, r)
  })
}

func main() {
  c := cors.Default()
  logChain := alice.New(c.Handler, logHandler)
  authChain := logChain.Append(authHandler)
  r := mux.NewRouter()
  r.Methods("GET").Path("/pilot").Handler(authChain.Then(funcHandler{pilotFunc}))
  r.Methods("GET").Path("/angel").Handler(logChain.Then(funcHandler{angelFunc}))
  http.ListenAndServe(":8000", r)
}

In diesem Staat

fetch(url, {
  headers: new Headers({
    Token: "abcd"
  })
})

Wenn Sie ausführen ...

Access to fetch at 'http://localhost:8000/pilot' from origin 'null' 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.

** Ein Fehler wird auftreten. ** **.

Um zu verstehen, warum du süchtig bist

  1. Authentifizierungsmechanismus
  2. Preflight-Anfrage
  3. Verarbeitungsauftrag anfordern

Es ist notwendig, die drei Punkte von zu verstehen. Ich werde in der Reihenfolge erklären.

1. Authentifizierungsmechanismus

Unabhängig davon, ob Sie Cookies oder Token verwenden, müssen Sie diese bei der Authentifizierung in den Anforderungsheader einfügen und vom Front-End zum Back-End übergeben. Wenn Sie daher den im Beispiel verwendeten "authHandler" erneut in etwas Realistischeres implementieren,

func authHandler(h http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    token := r.Header.Get("Token")
    if token == "" {
    ...
    }
    ...
    h.ServeHTTP(w, r)
  })
}

Es wird so etwas sein. Wichtig hierbei ist, dass Sie einen neuen ** Headernamen hinzufügen müssen, der ihn als Cookie oder Token ** identifiziert.

2. Preflight-Anfrage

Tatsächlich hat CORS eine Einschränkung, und Sie können eine "einfache Anforderung" nur dann stellen, wenn die Werte, die in der Methode und im Header festgelegt werden können, die folgenden Bedingungen erfüllen. Hier ist eine einfache Anfrage eine allgemeine Anfrage.

method : GET, POST, HEAD header : Accept, Accept-Language, Content-Language, Content-Type(application/x-www-form-urlencoded, multipart/form-data, text/plain), DPR, Downlink, Save-Data, Viewport-Width, Width

Wenn Sie im ** -Header eigene Werte wie "Cookie" oder "Token" festlegen, können Sie daher keine einfache Anfrage stellen **. In diesem Fall stellen Sie im Voraus eine Anfrage mit dem Namen ** Preflight Request **. Weitere Informationen finden Sie unter MDN. Wenn Sie nur die erforderlichen Informationen schreiben, wird die Anforderung als Preflight-Anforderung mit der Methode "OPTIONS" gesendet. Nur wenn eine normale Antwort auf diese Preflight-Anforderung zurückgegeben wird, wird ein Wert wie "Token" in den Header eingefügt und eine "GET" - oder "POST" -Anforderung gesendet. Daher wird bei Verwendung von "Abrufen" die Anforderung zweimal hinter den Kulissen gesendet.

3. Verarbeitungsauftrag anfordern

Zusammenfassend wird eine Preflight-Anfrage gesendet, wenn Sie Ihren eigenen Header-Wert verwenden. Hier werde ich den Flow vor und nach dem Ändern erneut veröffentlichen, damit mehrere Middleware angewendet werden kann.

Bisherige: request -> CORS    -> logging -> routing           -> pilotFunc or angelFunc
Rückseite: request -> routing -> CORS    -> logging (-> auth) -> pilotFunc or angelFunc 

Betrachten Sie die CORS-Middleware Handler.

func (c *Cors) Handler(h http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    if r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Method") != "" {
      ...
      w.WriteHeader(http.StatusNoContent)
    } else {
      ...
      h.ServeHTTP(w, r)
    }
  })
}

Der Prozess ist also verzweigt, je nachdem, ob es sich um die Options-Methode handelt oder nicht, und die Preflight-Anforderung wird entsprechend behandelt. Schauen Sie sich als Nächstes ServeHTTP von mux.Router an.

func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
  ...
  if r.Match(req, &match) {
    handler = match.Handler
    ...
  }

  if handler == nil && match.MatchErr == ErrMethodMismatch {
    handler = methodNotAllowedHandler()
  }

  if handler == nil {
    handler = http.NotFoundHandler()
  }

  handler.ServeHTTP(w, req)
}

Es sucht nach einem "Handler", der dem Wert von "url" in der Anforderung entspricht, und wenn nicht, verzweigt es sich je nachdem, ob die Methode fehlerhaft ist oder das Routing an erster Stelle steht, und behandelt den Fehler. ..

Daher hat sich die Verarbeitung für Preflight-Anforderungen vor und nach dem Ändern, um mehrere Middleware anwenden zu können, wie folgt geändert.

Bisherige: preflight request -> CORS check    -> 204 No content
Rückseite: preflight request -> routing check -> 405 Method Not Allowed

Aus diesem Grund tritt der CORS-Fehler auf.

So beheben Sie den Fehler

Sobald Sie dies wissen, ist es einfach, damit umzugehen. Ist es zum Beispiel am einfachsten, "OPTINOS" hinzuzufügen, wenn Sie "mux.Router" routen? Entschuldigung für das spätere, aber in rs / cors müssen Sie beim Hinzufügen Ihres eigenen Headers angeben, welche Art von Header zulässig ist. Siehe READ ME für Details. Basierend darauf wird die Implementierung wie folgt sein.

func main() {
  c := cors.New(cors.Options{
    AllowedOrigins: []string{"*"},
    AllowedMethods: []string{
      http.MethodHead,
      http.MethodGet,
      http.MethodPost,
    },
    AllowedHeaders:   []string{"*"},
    AllowCredentials: false,
  })
  logChain := alice.New(c.Handler, logHandler)
  authChain := logChain.Append(authHandler)
  r := mux.NewRouter()
  r.Methods("GET", "OPTIONS").Path("/pilot").Handler(authChain.Then(funcHandler{pilotFunc}))
  r.Methods("GET", "OPTIONS").Path("/angel").Handler(logChain.Then(funcHandler{angelFunc}))
  http.ListenAndServe(":8000", r)
}
Gesamtcode
package main

import (
  "encoding/json"
  "log"
  "net/http"

  "github.com/gorilla/mux"
  "github.com/justinas/alice"
  "github.com/rs/cors"
)

type Character struct {
  Name string `json:"name"`
}

type funcHandler struct {
  handler func(w http.ResponseWriter, r *http.Request)
}

func (h funcHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  h.handler(w, r)
}

func pilotFunc(w http.ResponseWriter, r *http.Request) {
  res, _ := json.Marshal(Character{"Shinji"})
  w.WriteHeader(http.StatusOK)
  w.Write(res)
}

func angelFunc(w http.ResponseWriter, r *http.Request) {
  res, _ := json.Marshal(Character{"Adam"})
  w.WriteHeader(http.StatusOK)
  w.Write(res)
}

func logHandler(h http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    log.Printf("Method: %v; URL: %v; Protocol: %v", r.Method, r.URL, r.Proto)
    h.ServeHTTP(w, r)
  })
}

func authHandler(h http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    token := r.Header.Get("Token")
    if token == "" {
      w.WriteHeader(http.StatusUnauthorized)
      return
    }
    h.ServeHTTP(w, r)
  })
}

func main() {
  c := cors.New(cors.Options{
    AllowedOrigins: []string{"*"},
    AllowedMethods: []string{
      http.MethodHead,
      http.MethodGet,
      http.MethodPost,
    },
    AllowedHeaders:   []string{"*"},
    AllowCredentials: false,
  })
  logChain := alice.New(c.Handler, logHandler)
  authChain := logChain.Append(authHandler)
  r := mux.NewRouter()
  r.Methods("GET", "OPTIONS").Path("/pilot").Handler(authChain.Then(funcHandler{pilotFunc}))
  r.Methods("GET", "OPTIONS").Path("/angel").Handler(logChain.Then(funcHandler{angelFunc}))
  http.ListenAndServe(":8000", r)
}

Alternativ können Sie "PathPrefix" verwenden, um alle "OPTIONEN" zu verarbeiten. (Ich weiß nicht, ob diese Methode gut ist, tut mir leid.)

func main() {
  c := cors.New(cors.Options{
    AllowedOrigins: []string{"*"},
    AllowedMethods: []string{
      http.MethodHead,
      http.MethodGet,
      http.MethodPost,
    },
    AllowedHeaders:   []string{"*"},
    AllowCredentials: false,
  })
  logChain := alice.New(c.Handler, logHandler)
  authChain := logChain.Append(authHandler)
  r := mux.NewRouter()
  r.Methods("OPTIONS").PathPrefix("/").HandlerFunc(c.HandlerFunc)
  r.Methods("GET").Path("/pilot").Handler(authChain.Then(funcHandler{pilotFunc}))
  r.Methods("GET").Path("/angel").Handler(logChain.Then(funcHandler{angelFunc}))
  http.ListenAndServe(":8000", r)
}

Übrigens, wie der Name schon sagt, fügt "Pathprefix" der URL ein gemeinsames Präfix hinzu. Zum Beispiel

r.PathPrefix("/products/")

Wenn diese Option aktiviert ist, gelten Anforderungen wie "http: // localhost: 8000 / products / hoge" und "http: // localhost: 8000 / products / fuga".

Fazit

Wenn Sie CORS unterstützen möchten, stellen Sie sicher, dass Sie "OPTIONEN" unterstützen.

Beiseite

Vielen Dank für das Lesen des Langtextes. Wenn Sie es verstehen, scheint es eine einfache Sache zu sein, aber es gibt noch etwas zu tun. Eigentlich habe ich es nicht von Grund auf neu geschrieben, sondern geändert, was andere Leute geschrieben haben, und ich war dabei süchtig danach. Der von anderen Personen geschriebene Code lautet

func main() {
  c := cors.New(cors.Options{
    AllowedOrigins: []string{"*"},
    AllowedMethods: []string{
      http.MethodHead,
      http.MethodGet,
      http.MethodPost,
    },
    AllowedHeaders:   []string{"*"},
    AllowCredentials: false,
  })
  logChain := alice.New(c.Handler, logHandler)
  authChain := logChain.Append(authHandler)
  r := mux.NewRouter()
  r.Methods("GET").Path("/pilot").Handler(authChain.Then(funcHandler{pilotFunc}))
  r.Methods("GET").Path("/angel").Handler(logChain.Then(funcHandler{angelFunc}))
  
  r.PathPrefix("").Handler(logChain.Then(http.StripPrefix("/img", http.FileServer(http.Dir("./img")))))  //Da war das.
  
  http.ListenAndServe(":8000", r)
}

Es war so. Um das Bild zurückzugeben, heißt es "r.PathPrefix (" "). Handler (logChain.Then (http.StripPrefix (" / img ", http.FileServer (http.Dir (" ./ img ")))) Da war etwas. Ich hatte jedoch keine Möglichkeit, das Bild auf meinem Server zurückzugeben, daher habe ich diese Zeile gelöscht. Dann trat plötzlich ein Fehler in CORS auf und es war Panik. Außerdem führte das Verschieben dieser Zeile an den Anfang zu einem Fehler. Es war jedoch schwierig, die Fehlerursache zu identifizieren.

Ich wusste zum Zeitpunkt des Schreibens dieses Artikels

CORS kann mit verschiedenen Fehlern fehlschlagen. Aus Sicherheitsgründen wird jedoch festgelegt, dass die Fehler in JavaScript nicht bekannt sind. Der Code sagt Ihnen nur, dass ein Fehler aufgetreten ist. Die einzige Möglichkeit, genau zu wissen, was schief gelaufen ist, besteht darin, die Details in der Browserkonsole zu überprüfen.

... anscheinend ... Mit anderen Worten, egal welcher Fehler auftritt

Access to fetch at 'http://localhost:8000/pilot' from origin 'null' 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.

Wird angezeigt. Dies machte das Debuggen schwierig. Wenn ich jetzt darüber nachdenke, kann ich das Gefühl nicht leugnen, dass ich meine Zeit damit verschwendet habe zu denken, dass ich erraten könnte, welche Art von Fehler auftreten könnte, wenn ich die Arten von 4XX-Fehlern auf der Registerkarte Netzwerk mit Chrom betrachte. ist. Als ich das "Pathprefix (" ")" entfernte und es an den Anfang brachte, war der auf der Konsole angezeigte Fehlertext derselbe, aber der Status war 405 bzw. 404.

Warum wird "r.PathPrefix (" "). Handler (logChain.Then (http.StripPrefix (" / img ", http.FileServer (http.Dir (" ./ img ")))") hinzugefügt? Wenn ja, ist der Fehler nicht aufgetreten, die Anforderung "OPTIONEN http: // localhost: 8000 / pilot"

r.Methods("GET").Path("/pilot").Handler(authChain.Then(funcHandler{pilotFunc}))

Ich erhalte den Fehler "Methode nicht zulässig" für

r.PathPrefix("").Handler(logChain.Then(http.StripPrefix("/img", http.FileServer(http.Dir("./img")))))

Passt dagegen. Eigentlich wird der bei Gorilla / Mux registrierte Pfad in einen regulären Ausdruck konvertiert. Wenn Sie jedoch ein leeres Zeichen "" in "PathPrefix" registrieren, lautet der entsprechende reguläre Ausdruck "^". Daher wird "/ pilot" für das obige Routing nicht gefunden, und die Verarbeitung wird fortgesetzt.

Wenn die Methode "OPTIONS" lautet, gibt rs / cors sofort 204 No Content zurück, sodass das nachfolgende "http.StripPrefix" ("/ img", http.FileServer (http.Dir ("./ img")) Anfragen, die einen Fehler mit))) `verursachen, werden ebenfalls übergeben. Infolgedessen wurden durch das Routing, das Sie für die Rückgabe des Bilds eingerichtet haben, CORS-Fehler unbeabsichtigt vermieden.

Schließlich

Es gibt eine Idee, dass die Go-Sprache eine Kombination aus einfachen Sprachen ist, aber für Gollira / Mux, Rs / Cors und Justinas / Alice war diesmal die Anzahl der Dateien, die gelesen werden mussten, um das Verhalten zu verstehen, eins oder zwei. Es war einfach, den Prozess in der richtigen Reihenfolge zu lesen. Ich denke nicht, dass die Erklärungen in diesem Artikel alles vollständig erklären können. Wenn Sie also Fragen haben, schauen Sie sich bitte jede Implementierung selbst an.

Recommended Posts

[Go language] Seien Sie vorsichtig, wenn Sie einen Server mit mux + cors + alice erstellen. Besonders darüber, wovon ich in Bezug auf CORS süchtig war.
Ein Hinweis, dem ich beim Erstellen einer Tabelle mit SQL Alchemy verfallen war
Wovon ich beim Erstellen von Webanwendungen in einer Windows-Umgebung abhängig war
Dinge, die Sie bei der Implementierung von Airflow mit Docker-Compose beachten sollten
Wovon ich süchtig war, als ich mit riesigen Dateien in einer Linux 32-Bit-Umgebung umging
Ich war süchtig danach, eine Python-Venv-Umgebung mit VS Code zu erstellen
Ein Hinweis, dem ich beim Ausführen von Python mit Visual Studio Code verfallen war
Eine Geschichte, der ich nach der SFTP-Kommunikation mit Python verfallen war
Wovon ich süchtig war, als ich Python Tornado benutzte
Wovon ich süchtig war, als der Processing-Benutzer zu Python wechselte
Worüber ich mir Sorgen gemacht habe, als ich Bilder mit matplotlib angezeigt habe
Eine Geschichte, nach der ich süchtig war, als ich in Go nil als Funktionsargument angab
Wovon ich süchtig war, als ich ALE in Vim für Python einführte
Was ich mit json.dumps in Pythons base64-Codierung süchtig gemacht habe
Ein Hinweis, von dem ich süchtig war, als ich unter Linux einen Piepton machte
[Python] Als ich versuchte, ein Dekomprimierungswerkzeug mit einer Zip-Datei zu erstellen, die ich gerade kannte, war ich süchtig nach sys.exit ()
Eine Geschichte, die ich süchtig danach war, eine Video-URL mit Tweepy zu bekommen
Ich war süchtig danach, Cython mit PyCharm auszuprobieren, also machen Sie sich eine Notiz
Was ich süchtig nach Python Autorun war
Drei Dinge, von denen ich süchtig war, als ich Python und MySQL mit Docker verwendete
Als ich mit VScode eine Verbindung zu einem entfernten Jupyter-Server herstellte, war dieser entfernt, aber lokal.
[Ansible] Worauf ich beim Schreiben von Ansible achten muss
Spielen Sie den Server mit zwei PCs
Wovon ich süchtig war, als ich Klassenvererbung und gemeinsame Tabellenvererbung in SQLAlchemy kombinierte
Was ich getan habe, als ich wütend war, es mit der Option enable-shared einzufügen
Ich habe ein CLI-Tool in der Sprache Go geschrieben, um Qiitas Tag-Feed in CLI anzuzeigen
Es war eine schmerzhafte Erinnerung, als mir TypeError gesagt wurde: Muss type sein, nicht class obj, wenn versucht wird, mit Python zu erben
Ich war süchtig danach, 2020 mit Selen (+ Python) zu kratzen
Eine Geschichte, von der ich bei np.where süchtig war
Als ich versuchte, PIL und matplotlib in einer virtuellen Umgebung zu installieren, war ich süchtig danach.
Wovon ich süchtig war, als ich mein eigenes neuronales Netzwerk mit den Gewichten und Vorurteilen aufbaute, die ich mit dem MLP-Klassifikator von scikit-learn bekam.
Wovon ich in Kapitel 3 der kollektiven Intelligenz abhängig war. Es ist kein Tippfehler, daher denke ich, dass etwas mit meinem Code nicht stimmt.