web-dev-qa-db-fra.com

Comment gérer les demandes CORS avant le vol sur un serveur Go

J'écris donc ce backend RESTful dans Go, qui sera appelé avec des requêtes HTTP intersites, c'est-à-dire à partir de contenu servi par un autre site (en fait, juste un autre port, mais la politique de même origine s'applique, alors nous y sommes) .

Dans ce scénario, l'agent d'utilisateur enverra, dans certains cas, des demandes OPTIONS de contrôle en amont pour vérifier si la demande réelle peut être envoyée en toute sécurité.

Ma question est de savoir comment gérer au mieux ces requêtes de contrôle en amont dans un contexte Go et y répondre de manière adéquate. Les façons que j'ai conçues ne me paraissent pas très élégantes, et je me demande s'il existe une autre approche à laquelle je n'ai pas pensé.

En utilisant le paquetage net/http standard, je peux vérifier la méthode de requête dans le gestionnaire func, peut-être comme ceci:

func AddResourceHandler(rw http.ResponseWriter, r *http.Request) {
  switch r.Method {
  case "OPTIONS":
    // handle preflight
  case "PUT":
    // respond to actual request
  }
}

Je peux aussi utiliser le paquet Gorillamux et enregistrer un gestionnaire de contrôle en amont "OPTIONS" pour chaque chemin d'URL pertinent.

r := mux.NewRouter()
r.HandleFunc("/someresource/item", AddResourceHandler).Methods("PUT")
r.HandleFunc("/someresource/item", PreflightAddResourceHandler).Methods("OPTIONS")

Peut-être que la réponse à cette question est simplement: Ouais, ce sont vos options de base. Mais je pensais qu'il y avait peut-être une meilleure pratique à ce sujet dont je ne suis pas au courant.

29
ivarg

Un moyen simple de séparer votre logique et de réutiliser le gestionnaire CORS que vous définissez consiste à envelopper votre gestionnaire REST. Par exemple, si vous utilisez net/http et la méthode Handle, vous pouvez toujours faire quelque chose comme:

func corsHandler(h http.Handler) http.HandlerFunc {
  return func(w http.ResponseWriter, r *http.Request) {
    if (r.Method == "OPTIONS") {
      //handle preflight in here
    } else {
      h.ServeHTTP(w,r)
    }
  }
}

Vous pouvez envelopper comme ceci:

http.Handle("/endpoint/", corsHandler(restHandler))
26
photoionized

Personnellement, je trouve fastidieux d’ajouter des routes de contrôle en amont pour chaque chemin qui recevra une demande OPTIONS. C’est pourquoi, j’ajoute simplement mon gestionnaire à toute méthode OPTIONS que Gorilla gère comme suit:

router.Methods("OPTIONS").HandlerFunc(
    func(w http.ResponseWriter, r *http.Request){
    myHttpLib.OptionsForBrowserPreflight(w, r)
})

Notez cependant que cela devrait précéder le mappage d’autres routes car si, par exemple, vous avez un chemin tel que "/foo" et que vous l’enregistrez d’abord sans spécifier de méthode pour cette route, une requête OPTIONS sur exécutez à la place de votre code de contrôle en amont car c’est la première correspondance.

De cette façon, vous pouvez: (1) avoir un seul enregistrement de routage pour tous les vols préalables, et (2) avoir un seul gestionnaire pour réutiliser le code et appliquer la logique/les règles à un seul endroit pour les demandes OPTIONS.

5
MikeM

Voici un extrait qui a fonctionné pour moi:

addCorsHeader(res)
if req.Method == "OPTIONS" {
    res.WriteHeader(http.StatusOK)
    return
} else {
    h.APIHandler.ServeHTTP(res, req)
}


func addCorsHeader(res http.ResponseWriter) {
    headers := res.Header()
    headers.Add("Access-Control-Allow-Origin", "*")
    headers.Add("Vary", "Origin")
    headers.Add("Vary", "Access-Control-Request-Method")
    headers.Add("Vary", "Access-Control-Request-Headers")
    headers.Add("Access-Control-Allow-Headers", "Content-Type, Origin, Accept, token")
    headers.Add("Access-Control-Allow-Methods", "GET, POST,OPTIONS")
}
2
notdrone

gorilla/handlers dispose également d’un gestionnaire Nice CORS: cors.go

Exemple d'utilisation:

import (
    "net/http"

    "github.com/gorilla/handlers"
    "github.com/gorilla/mux"
)

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/users", UserEndpoint)
    r.HandleFunc("/projects", ProjectEndpoint)

    // Apply the CORS middleware to our top-level router, with the defaults.
    http.ListenAndServe(":8000", handlers.CORS()(r))
}
1
shanab