[Go language] Soyez prudent lors de la création d'un serveur avec mux + cors + alice. Surtout à propos de ce à quoi j'étais accro autour de CORS.

introduction

gorilla / mux, rs / cors, [justinas / alice](https: // github. Je vais vous montrer comment faire un serveur en combinant com / justinas / alice). En particulier, lorsque j'ai essayé de diviser le middleware à appliquer en fonction du chemin, j'étais accro à CORS, donc je vais me concentrer sur la partie liée à cela. Dans le code à publier, il s'agit essentiellement d'une manière d'écrire qui ignore les erreurs, veuillez donc réécrire comme il convient. Notez également que «...» dans le code signifie simplement une abréviation et n'a pas de signification grammaticale, alors soyez prudent lors de la copie.

tl;dr

Attention à la méthode ʻOPTIONS` de la demande de contrôle en amont.

Un bref aperçu de mux, cors et alice

Quelques exemples sont présentés, mais comme il s'agit du contenu de base tel que décrit dans chaque README, si vous le connaissez, veuillez passer à [Sujet de l'implémentation](# Sujet de l'implémentation).

gorilla/mux

Voici un exemple de serveur utilisant mux.

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 est*mux.Type de routeur
  r.Methods("GET").Path("/pilot").HandlerFunc(pilotFunc)  // r.aux itinéraires*mux.Ajouter une route.
  r.Methods("GET").Path("/angel").HandlerFunc(angelFunc)  // r.routes est[]*mux.Puisqu'il s'agit d'un type d'itinéraire, vous pouvez en ajouter de plus en plus.

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

Avec cela, un petit serveur est terminé.

$ go run main.go

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

Cependant, lorsque j'utilise fetch depuis un navigateur, j'obtiens l'erreur suivante:

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

C'est là que rs / cors entre en jeu. Pour CORS, la page MDN est facile à comprendre. Afin de prendre en charge CORS, il est nécessaire de définir une valeur appropriée dans l'en-tête de réponse côté serveur. Rs / cors en est responsable. J'ai ajouté rs / cors à l'exemple précédent.

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)  //Ajoutée.

  http.ListenAndServe(":8000", c)  //Changé de r à c.
}

Vous pouvez prendre en charge CORS simplement en ajoutant une ligne.

justinas/alice

De plus, justinas / alice est utile lorsque vous souhaitez ajouter un middleware qui affiche le contenu de la requête dans le journal à chaque fois, ou un middleware qui récupère la valeur de l'en-tête de la requête avant d'effectuer le traitement principal. (: //github.com/justinas/alice). Voici un exemple de création et d'ajout de middleware qui génère des informations de demande dans le journal.

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)  //Ajoutée.

  http.ListenAndServe(":8000", chain)  //Changé de c en chaîne.
}

Sujet de mise en œuvre

À propos, gorilla / mux, rs / cors, [justinas / alice](https: // Je republierai la combinaison de toutes les définitions de github.com/justinas/alice (packge, import, struct omises).

...

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)
}

Cependant, avec cette méthode, le middleware défini sera appliqué à toutes les requêtes.

Par exemple, / pilot vérifie si vous êtes connecté et ne renvoie des données que si vous êtes connecté, mais / angel ne peut pas répondre aux demandes qui renvoient des données même si vous n'êtes pas connecté. Avec Qiita, vous pouvez voir la page de l'article même si vous n'êtes pas connecté, mais imaginez une situation où vous ne pouvez pas voir le brouillon de Ma page à moins que vous ne soyez connecté.

Par conséquent, essayez ce qui suit. Comme je l'ai dit plus tôt, je suis accro au style d'écriture ci-dessous (rires).

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)
}

Maintenant, seulement quand fetch est fait vers / pilot, ʻAuthentication` est sortie vers la sortie standard. Il est maintenant possible de séparer le middleware à appliquer en fonction du chemin.

$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

Je vais laisser l'explication de pourquoi cela crée une dépendance et expliquer brièvement le mécanisme de réécriture de cette manière. Si vous voulez d'abord savoir pourquoi vous êtes accro, veuillez passer à [Raison de la dépendance](# Raison de la dépendance).

Raison de la réécriture (supplément)

Commençons par vérifier ce que fait http.ListenAndServer, mais [Go] Comment lire le package net / http et exécuter http.HandleFunc / items / 1d1c64d05f7e72e31a98) est assez détaillé, je vais donc le jeter ici. Il semble que les informations soient un peu anciennes et que le numéro de ligne écrit ne correspond pas au numéro de ligne réel, ou que le style d'écriture soit légèrement différent, mais je pense qu'il n'y a pas de problème de compréhension de base.

En conclusion, http.ListenAndServe transmet la requête reçue à ServeHTTP dans le deuxième argument (cette fois mux.Router). Par conséquent, le deuxième argument doit implémenter «ServeHTTP». ServeHTTP est implémenté dans mux.Router, et dans le paquet http,

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

Toute variable de type http.Handler peut être le deuxième argument de ListenAndServe. Dans ce "ServeHTTP", appelez le "ServeHTTP" d'un autre "http.Hanlder", puis dans ce "ServeHTTP", appelez le "ServeHTTP" de l'autre "http.Hanlder", et ainsi de suite. En chaînant .Handler`, vous pouvez traiter les demandes dans l'ordre.

En d'autres termes, vous pouvez enchaîner des gestionnaires en utilisant une fonction qui prend http.Handler comme argument et renvoie un type http.Handler, ** c'est-à-dire un middleware **. À titre d'exemple, considérons la chaîne de «logHandler» et «authHandler».

chain := logHandler(authHandler(router))

Une telle chaîne peut être envisagée. Cependant, avec cette méthode, il devient plus difficile à comprendre à mesure que le nombre de middleware augmente, donc en utilisant alice,

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

Est écrit brièvement.

Ici, comparons avant et après la réécriture afin que le middleware appliqué puisse être modifié en fonction du chemin.

//Changer avant
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)
}

//Après le changement
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)
}

Par conséquent, le flux que la demande avant et après le changement suit est

précédent: request -> CORS    -> logging -> routing           -> pilotFunc or angelFunc
arrière: request -> routing -> CORS    -> logging (-> auth) -> pilotFunc or angelFunc 

Vous pouvez voir que l'ordre de routage a changé. C'est l'une des raisons pour lesquelles j'en suis accro.

Ensuite, j'expliquerai pourquoi je suis accro à CORS, qui est le sujet principal de cet article.

Raisons de devenir accro

Tout d'abord, je décrirai une implémentation du mécanisme d'authentification (les détails seront décrits plus loin) qui est un peu plus proche de l'actuel.

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)
  })
}
code entier
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)
}

Dans cet état

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

Lorsque vous exécutez ...

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.

** Une erreur se produira. ** **

Pour comprendre pourquoi tu es accro

  1. Mécanisme d'authentification
  2. Demande de contrôle en amont
  3. Demander un ordre de traitement

Il est nécessaire de comprendre les trois points de. Je vais expliquer dans l'ordre.

1. Mécanisme d'authentification

Lors de l'authentification, que vous utilisiez des cookies ou des jetons, vous devez les mettre dans l'en-tête de la demande et les transmettre du front-end au back-end. Par conséquent, si vous réimplémentez ʻauthHandler` utilisé dans l'exemple en quelque chose de plus réaliste,

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)
  })
}

Ce sera quelque chose comme ça. L'important ici est que vous devez ajouter un nouveau ** nom d'en-tête qui l'identifie en tant que cookie ou Token **.

2. Demande de contrôle en amont

En fait, CORS a une limitation, et vous ne pouvez faire une "demande simple" que lorsque les valeurs qui peuvent être définies dans la méthode et l'en-tête remplissent les conditions suivantes. Ici, une simple demande est une demande générale.

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

Par conséquent, si vous définissez vos propres valeurs telles que «Cookie» ou «Token» dans l'en-tête **, vous ne pouvez pas faire une simple requête **. Dans ce cas, faites une demande appelée ** Demande de contrôle en amont ** à l'avance. Voir MDN pour plus d'informations. Si vous n'écrivez que les informations nécessaires, la requête sera envoyée en tant que requête de contrôle en amont avec la méthode ʻOPTIONS`. Seulement lorsqu'une réponse normale est renvoyée à cette demande de contrôle en amont, une valeur telle que «Token» est placée dans l'en-tête et une demande «GET» ou «POST» est envoyée. Par conséquent, lors de l'utilisation de «fetch», la requête est envoyée deux fois dans les coulisses.

3. Demander un ordre de traitement

En résumé, si vous utilisez votre propre valeur d'en-tête, une demande de contrôle en amont sera envoyée. Ici, je republierai le flux avant et après le changement afin que plusieurs intergiciels puissent être appliqués.

précédent: request -> CORS    -> logging -> routing           -> pilotFunc or angelFunc
arrière: request -> routing -> CORS    -> logging (-> auth) -> pilotFunc or angelFunc 

En regardant le middleware CORS 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)
    }
  })
}

Ainsi, le processus est ramifié selon qu'il s'agit de la méthode Options ou non, et la demande de contrôle en amont est traitée de manière appropriée. Ensuite, si vous regardez ServeHTTP de mux.Router,

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)
}

Il recherche un handler qui correspond à la valeur de ʻurl` dans la requête, et si ce n'est pas le cas, il se branche selon que la méthode est en erreur ou que le routage est en premier lieu, et gère l'erreur. ..

Par conséquent, avant et après le changement pour appliquer plusieurs intergiciels, le traitement de la demande de contrôle en amont a changé comme suit.

précédent: preflight request -> CORS check    -> 204 No content
arrière: preflight request -> routing check -> 405 Method Not Allowed

C'est pourquoi l'erreur CORS se produit.

Comment résoudre l'erreur

Une fois que vous savez cela, c'est facile à gérer. Par exemple, est-il plus simple d'ajouter ʻOPTINOSlors du routage demux.Router`? Désolé pour le plus tard, mais dans rs / cors, lors de l'ajout de votre propre en-tête, vous devez spécifier le type d'en-tête autorisé. Voir LISEZ-MOI pour plus de détails. Sur cette base, la mise en œuvre sera la suivante.

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)
}
Code global
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)
}

Alternativement, vous pouvez utiliser «PathPrefix» pour gérer toutes les «OPTIONS». (Je ne sais pas si cette méthode est bonne, je suis désolé.)

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)
}

En passant, comme son nom l'indique, Pathprefix ajoute un préfixe commun à url. Par exemple

r.PathPrefix("/products/")

Si défini sur, les requêtes telles que «http: // localhost: 8000 / products / hoge» et «http: // localhost: 8000 / products / fuga» seront applicables.

Conclusion

Si vous souhaitez prendre en charge CORS, assurez-vous de prendre en charge ʻOPTIONS`.

De côté

Merci d'avoir lu le long texte. Quand vous le comprenez, il semble que ce soit une chose simple, mais il y a encore une chose à faire. En fait, je ne l'ai pas écrit à partir de zéro, mais j'ai changé ce que les autres ont écrit, et j'y ai été accro dans le processus. Le code écrit par d'autres personnes est

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")))))  //Il y avait ça.
  
  http.ListenAndServe(":8000", r)
}

C'était comme ça. Pour renvoyer l'image, elle s'appelle r.PathPrefix (" "). Handler (logChain.Then (http.StripPrefix (" / img ", http.FileServer (http.Dir (" ./ img ")))))) Il y avait quelque chose. Cependant, je n'ai pas eu la chance de renvoyer l'image sur mon serveur, j'ai donc supprimé cette ligne. Puis, soudainement, une erreur a commencé à se produire dans CORS et c'était la panique. De plus, déplacer cette ligne au début a entraîné une erreur. Cependant, il était difficile d'identifier la cause de l'erreur.

Je savais au moment de la rédaction de cet article

CORS peut échouer avec diverses erreurs, mais pour des raisons de sécurité, il est stipulé que les erreurs ne peuvent pas être connues à partir de JavaScript. Le code vous indique seulement qu'une erreur s'est produite. La seule façon de savoir exactement ce qui ne va pas est de regarder les détails dans la console du navigateur.

... Apparemment ... En d'autres termes, quelle que soit l'erreur qui se produit

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.

Est affiché. Cela a rendu le débogage difficile. Quand j'y pense maintenant, je ne peux pas nier le sentiment que j'ai perdu mon temps à penser que je pourrais deviner quel type d'erreur pourrait se produire en regardant les types d'erreurs 4XX de l'onglet Réseau avec chrome. est. En fait, lorsque j'ai supprimé le préfixe `Pathprefix (" ") et que je l'ai amené au début, le texte d'erreur affiché sur la console était le même, mais l'état était respectivement 405 et 404.

Alors pourquoi r.PathPrefix (" "). Handler (logChain.Then (http.StripPrefix (" / img ", http.FileServer (http.Dir (" ./ img "))))) ʻadded Si tel est le cas, l'erreur ne s'est pas produite. La requête ʻOPTIONS http: // localhost: 8000 / pilot

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

J'obtiens une erreur Méthode non autorisée pour

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

S'adaptera contre. En fait, le chemin enregistré avec gorilla / mux est converti en une expression régulière, mais si vous enregistrez un caractère vide "" dans PathPrefix, l'expression régulière correspondante sera" ^ ". Par conséquent, «/ pilot» ne devient pas introuvable pour le routage ci-dessus, et le traitement se poursuit.

De plus, si la méthode est ʻOPTIONS, rs / cors renvoie immédiatement 204 No Content, donc le suivant http.StripPrefix (" / img ", http.FileServer (http.Dir (" ./ img ")) Les requêtes qui provoquent une erreur avec))) `seront également transmises. Par conséquent, le routage que vous configurez pour renvoyer les images évite involontairement les erreurs CORS.

finalement

Il y a une idée que le langage Go est une combinaison de langages simples, mais pour gollira / mux, rs / cors et justinas / alice cette fois, le nombre de fichiers à lire pour comprendre le comportement était d'un ou deux. , Il était facile de lire le processus dans l'ordre. Je ne pense pas que les explications de cet article peuvent tout expliquer complètement, donc si vous avez des questions, veuillez jeter un œil à chaque implémentation par vous-même.

Recommended Posts

[Go language] Soyez prudent lors de la création d'un serveur avec mux + cors + alice. Surtout à propos de ce à quoi j'étais accro autour de CORS.
Une note à laquelle j'étais accro lors de la création d'une table avec SQL Alchemy
Ce à quoi j'étais accro lors de la création d'applications Web dans un environnement Windows
Éléments à prendre en compte lors de la mise en œuvre d'Airflow avec docker-compose
Ce à quoi j'étais accro en traitant d'énormes fichiers dans un environnement Linux 32 bits
J'étais accro à la création d'un environnement Python venv avec VS Code
Une note à laquelle j'étais accro lors de l'exécution de Python avec Visual Studio Code
Une histoire à laquelle j'étais accro après la communication SFTP avec python
Ce à quoi j'étais accro lors de l'utilisation de Python tornado
Ce à quoi j'étais accro lorsque l'utilisateur de traitement est passé à Python
Ce qui m'inquiétait lors de l'affichage d'images avec matplotlib
Une histoire à laquelle j'étais accro en spécifiant nil comme argument de fonction dans Go
Ce à quoi j'étais accro en présentant ALE à Vim pour Python
Ce à quoi j'étais accro avec json.dumps dans l'encodage base64 de Python
Une note à laquelle j'étais accro lors de l'émission d'un bip sous Linux
[Python] Quand j'ai essayé de créer un outil de décompression avec un fichier zip que je connaissais juste, j'étais accro à sys.exit ()
Une histoire à laquelle j'étais accro à essayer d'obtenir une URL de vidéo avec tweepy
J'étais accro à essayer Cython avec PyCharm, alors prenez note
Ce que j'étais accro à Python autorun
Trois choses auxquelles j'étais accro lors de l'utilisation de Python et MySQL avec Docker
Lorsque je me suis connecté à un serveur Jupyter distant avec VScode, il était distant mais local.
[Ansible] Ce à quoi je fais attention lorsque j'écris ansible
Serveur de jeu avec deux PC
Ce à quoi j'étais accro en combinant l'héritage de classe et l'héritage de table commune dans SQLAlchemy
Ce que j'ai fait quand j'étais en colère de le mettre avec l'option enable-shared
J'ai écrit un outil CLI en langue Go pour afficher le flux de balises de Qiita dans CLI
C'était un souvenir douloureux quand on m'a dit TypeError: doit être de type, pas de classe obj en essayant d'hériter avec Python
J'étais accro au grattage avec Selenium (+ Python) en 2020
Une histoire à laquelle j'étais accro chez np.where
Quand j'ai essayé d'installer PIL et matplotlib dans un environnement virtualenv, j'en étais accro.
Ce à quoi j'étais accro lorsque j'ai construit mon propre réseau de neurones en utilisant les poids et les biais que j'ai obtenus avec le classificateur MLP de scikit-learn.
Ce à quoi j'étais accro dans Collective Intelligence Chaprter 3. Ce n'est pas une faute de frappe, donc je pense que quelque chose ne va pas avec mon code.