Platzhalter im Muster für http.HandleFunc

74

Gibt es bei der Registrierung von Handlern in Go (Sprache) eine Möglichkeit, Platzhalter im Muster anzugeben?

Zum Beispiel:

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

Wo *könnte eine gültige URL-Zeichenfolge sein. Oder ist die einzige Lösung, um /groupsden Rest aus der Handler ( peopleInGroupHandler) -Funktion heraus abzugleichen und herauszufinden ?

Mat Ryer
quelle
Darf ich ein Beispiel für Ihre Eingabezeichenfolge sehen und was Sie derzeit für Übereinstimmungen erhalten, bitte?
Siehe goweb.googlecode.com , das Ruby on Rails-Routen in Go unterstützt - dh goweb.MapFunc ("/ people / {person_id} / groups / {group_id}", Handler)
Mat Ryer

Antworten:

94

Die Muster für http.Handler und http.HandleFunc sind keine regulären Ausdrücke oder Globs. Es gibt keine Möglichkeit, Platzhalter anzugeben. Sie sind hier dokumentiert .

Es ist jedoch nicht allzu schwierig, einen eigenen Handler zu erstellen, der reguläre Ausdrücke oder andere gewünschte Muster verwenden kann. Hier ist eine, die reguläre Ausdrücke verwendet (kompiliert, aber nicht getestet):

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)
}
Evan Shaw
quelle
2
Da ich in Go Ruby on Rails-Routen wollte, habe ich das Goweb-Projekt (siehe goweb.googlecode.com ) gestartet, das diese Art der Routenzuordnung ermöglicht: goweb.MapFunc ("/ people / {person_id}", Handler)
Mat Ryer
3
Gorillatoolkit implementiert PAT und MUX hervorragend für die Verarbeitung von Routen. Das einzige Problem ist, dass es langsam ist und ich den Code noch nicht überprüft habe. Zumindest in ihrer API ist es möglich, die Parameter zu benennen ... das ist der springende Punkt dieser Art von Funktionalität. Der obige Code bietet nichts so Anspruchsvolles und ist ohne benannte Elemente wahrscheinlich nicht nützlich.
Richard
8
Wenn Sie nur nach einem "ansonsten" *-ähnlichen the pattern "/" matches all paths not matched by other registered patterns
Allheilmittel
Vielleicht (h *RegexpHandler) Handlersollte es sein (h *RegexpHandler) Handle? Siehe hier: golang.org/pkg/net/http/#ServeMux.Handle :)
Coaku
1
Wie kann ich den Code in das ursprüngliche Beispiel integrieren net/http. Können Sie ein vollständiges Beispiel geben? Danke.
Casper
59

Seit 2011 können Sie jetzt (2014+) andere Lösungen finden.
Zum Beispiel bietet das Mux-Paket des Gorilla Web Toolkits alle Arten von Routing-Optionen:

  • Mustervergleich auf Anforderungspfaden mit optionalen regulären Ausdrücken.
  • Übereinstimmung mit URL-Host und -Schema, Anforderungsmethode, Header und Abfragewerten.
  • Matching basierend auf benutzerdefinierten Funktionen.
  • Verwendung von Sub-Routern für einfaches verschachteltes Routing.

Es kann problemlos in jede BYOR-HTTP-Bibliothek (Bring your own Router) wie negroni integriert werden .

Hier ist ein Beispiel aus dem Artikel " Gorilla vs Pat vs Routes: Ein 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))
}

Manchmal ist es besser, nicht nur ein weiteres "magisches" Paket zu verwenden, sondern zu verstehen, was unter der Haube vor sich geht

In diesem Fall wird die "Magie" in " gorilla/mux/regexp.go" definiert und hier getestet .
Die Idee ist, benannte Variablen zu extrahieren, einen zu passenden regulären Ausdruck zusammenzustellen, eine "umgekehrte" Vorlage zum Erstellen von URLs zu erstellen und reguläre Ausdrücke zu kompilieren, um die beim Erstellen von URLs verwendeten Variablenwerte zu validieren.

VonC
quelle
Weil es viel neuer ist. Geben Sie ihm Zeit :)
Vlad der Impala
5
Weil es manchmal besser ist, nicht nur ein weiteres "magisches" Paket zu verwenden, sondern zu verstehen, was unter der Haube vor sich geht :)
Timur Fayzrakhmanov
7

Ich wollte nur hinzufügen julienschmidt/httprouter, was sich einfach so verhält, net/httpaber mit einem zusätzlichen Parameter für URL-Werte und Unterstützung für Anforderungsmethoden:

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

Es scheint auch etwas beliebter zu sein als gorilla/mux(laut GitHub) und es behauptet auch, weniger Speicher zu benötigen.

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

Daveman
quelle
3

Hier ist ein Beispiel für die Verwendung des Codebeispiels von @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)
}
caike
quelle
1

Sie können überprüfen, wie Violetear mit dynamischen + Catchall-Mustern (Platzhaltermustern) umgeht. Dies ist nur eine Ergänzung zum Beispiel:

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

In diesem Fall kann die Anforderung 2 verschiedene haben UUIDS

Für eine Dynamik / einen Platzhalter könnte dies gelten:

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

Ein regulärer Ausdruck kann verwendet werden, um der IP zu entsprechen:

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

Oder einfach nur ein Fang aller erlaubten GETund HEADnur Methoden:

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

Weitere Beispiele finden Sie hier: https://violetear.org/post/how-it-works/

nbari
quelle
-12

Beego, die Antwort auf alle Fragen zum Golang-Webserver. Wetalk ist eine Blog-Site, die auf Beego basiert.

Francois
quelle
9
"Während dieser Link die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz bereitzustellen. Nur-Link-Antworten können ungültig werden, wenn sich die verlinkte Seite ändert."
Null323