web-dev-qa-db-fra.com

Caractères génériques dans le modèle pour http.HandleFunc

Lors de l'enregistrement de gestionnaires dans Go (langue), existe-t-il un moyen de spécifier des caractères génériques dans le modèle?

Par exemple:

http.HandleFunc("/groups/*/people", peopleInGroupHandler)

Où le * peut être n'importe quelle chaîne d'URL valide. Ou est la seule solution pour correspondre à /groups et comprendre le reste depuis le gestionnaire (peopleInGroupHandler) func?

61
Mat Ryer

Les modèles pour http.Handler et http.HandleFunc ne sont pas des expressions régulières ou des globes. Il n'y a aucun moyen de spécifier des caractères génériques. Ils sont documentés ici .

Cela dit, il n'est pas trop difficile de créer votre propre gestionnaire qui peut utiliser des expressions régulières ou tout autre type de modèle que vous souhaitez. En voici une qui utilise des expressions régulières (compilées, mais non testées):

type route struct {
    pattern *regexp.Regexp
    handler http.Handler
}

type RegexpHandler struct {
    routes []*route
}

func (h *RegexpHandler) Handler(pattern *regexp.Regexp, handler http.Handler) {
    h.routes = append(h.routes, &route{pattern, handler})
}

func (h *RegexpHandler) HandleFunc(pattern *regexp.Regexp, handler func(http.ResponseWriter, *http.Request)) {
    h.routes = append(h.routes, &route{pattern, http.HandlerFunc(handler)})
}

func (h *RegexpHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    for _, route := range h.routes {
        if route.pattern.MatchString(r.URL.Path) {
            route.handler.ServeHTTP(w, r)
            return
        }
    }
    // no pattern matched; send 404 response
    http.NotFound(w, r)
}
83
Evan Shaw

Depuis 2011, vous pouvez désormais (2014+) trouver d'autres solutions.
Par exemple, le package mux du Gorilla Web toolkit fournit toutes sortes d'options de routage:

  • Correspondance de modèle sur les chemins de demande, avec des expressions régulières facultatives.
  • Correspondance sur l'hôte URL et le schéma, la méthode de demande, l'en-tête et les valeurs de requête.
  • Correspondance basée sur des fonctions personnalisées.
  • Utilisation de sous-routeurs pour un routage imbriqué facile.

Il peut être facilement intégré à n'importe quelle bibliothèque http BYOR (Bring your own Router), comme negroni .

Voici un exemple de l'article " Gorilla vs Pat vs Routes: A Mux Showdown ":

package main

import (
  "github.com/gorilla/mux"
  "log"
  "net/http"
)

func main() {
  rtr := mux.NewRouter()
  rtr.HandleFunc("/user/{name:[a-z]+}/profile", profile).Methods("GET")

  http.Handle("/", rtr)

  log.Println("Listening...")
  http.ListenAndServe(":3000", nil)
}

func profile(w http.ResponseWriter, r *http.Request) {
  params := mux.Vars(r)
  name := params["name"]
  w.Write([]byte("Hello " + name))
}

Parfois, il vaut mieux ne pas simplement utiliser un autre package "magique", mais comprendre ce qui se passe sous le capot

Dans ce cas, la "magie" est définie dans " gorilla/mux/regexp.go ", et testé ici .
.

54
VonC

Je voulais juste ajouter julienschmidt/httprouter, qui se comporte comme net/http mais avec un paramètre supplémentaire pour les valeurs d'url et la prise en charge des méthodes de demande:

https://github.com/julienschmidt/httprouter

package main

import (
    "fmt"
    "github.com/julienschmidt/httprouter"
    "net/http"
    "log"
)

func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
    fmt.Fprint(w, "Welcome!\n")
}

func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
}

func main() {
    router := httprouter.New()
    router.GET("/", Index)
    router.GET("/hello/:name", Hello)

    log.Fatal(http.ListenAndServe(":8080", router))
}

Il semble également être légèrement plus populaire que gorilla/mux (selon GitHub) et il prétend également avoir besoin de moins de mémoire.

https://github.com/julienschmidt/go-http-routing-benchmark

6
Daveman

Vous pouvez vérifier comment violetear gère les modèles dynamiques + catchall (wildcard), c'est juste pour le complément par exemple:

uuid := `[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}`
router.AddRegex(":uuid")
router.HandleFunc("/test/:uuid/:uuid", handleUUID, "GET,HEAD")

Dans ce cas, la demande peut avoir 2 UUIDS différents

Pour un dynamique/caractère générique, cela pourrait s'appliquer:

http://api.violetear.org/command/ping/127.0.0.1
                        \______/\___/\________/
                            |     |      |
                             static      |
                                      dynamic

Une expression régulière peut être utilisée pour correspondre à l'IP:

router.AddRegex(":ip", `^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$`)
router.HandleFunc("/command/ping/:ip", ipHandler, "GET")

Ou tout simplement un catch all permettant uniquement les méthodes GET et HEAD:

router.HandleFunc("/command/ping/*", anyHandler, "GET, HEAD")

Plus d'exemples peuvent être trouvés ici: https://violetear.org/post/how-it-works/

1
nbari

Voici un exemple d'utilisation de l'exemple de code de @evanshaw

func handleDigits(res http.ResponseWriter, req *http.Request) {
    res.Write([]byte("Digits in the URL\n"))
}

func handleStrings(res http.ResponseWriter, req *http.Request) {
    res.Write([]byte("Strings in the URL\n"))
}

func main() {
    handler := &RegexpHandler{}

    reg1, _ := regexp.Compile("/foo-\\d+")
    handler.HandleFunc(reg1, handleDigits)

    reg2, _ := regexp.Compile("/foo-\\w+")
    handler.HandleFunc(reg2, handleStrings)

    http.ListenAndServe(":3000", handler)
}
1
caike