Go & Gorilla/Mux: Alles, was Sie zum Erstellen einer Web-App benötigen
Takashi Yamamoto
Infrastructure Engineer · Leapcell

Einführung
gorilla/mux
ist eine Routing-Verwaltungsbibliothek im gorilla Web
-Entwicklungstoolkit. Das gorilla Web
-Entwicklungspaket ist ein Toolkit, das bei der Entwicklung von Web
-Servern in der Go
-Sprache hilft. Es deckt verschiedene Aspekte ab, wie z. B. Formulardatenverarbeitung (gorilla/schema
), WebSocket
-Kommunikation (gorilla/websocket
), Middleware (gorilla/handlers
), Session
-Verwaltung (gorilla/sessions
) und sichere Cookie
-Behandlung (gorilla/securecookie
).
mux
hat die folgenden Vorteile:
- Es implementiert die Standard-
http.Handler
-Schnittstelle und kann in Kombination mit der Standardbibliotheknet/http
verwendet werden. Es ist sehr leichtgewichtig. - Es kann Handler basierend auf dem Hostnamen, dem Pfad, dem Pfadpräfix, dem Protokoll, den
HTTP
-Headern, der Abfragezeichenfolge und derHTTP
-Methode der Anfrage abgleichen. Es unterstützt auch benutzerdefinierte Matching-Logik. - Variablen können in Hostnamen, Pfaden und Anfrageparametern verwendet werden, und es können reguläre Ausdrücke für sie angegeben werden.
- Es kann Parameter an einen bestimmten Handler übergeben, um eine vollständige
URL
zu erstellen. - Es unterstützt die Routengruppierung, was die Verwaltung und Wartung erleichtert.
Schnellstart
Der Code in diesem Artikel verwendet Go Modules
.
Installieren Sie die Bibliothek gorilla/mux
:
go get -u github.com/gorilla/gorilla/mux
Als Nächstes schreiben wir einen Web
-Dienst zur Verwaltung von Musikinformationen. Jedes Musikstück wird eindeutig durch MusicID
identifiziert.
- Definieren Sie die Struktur der Musik:
type Music struct { MusicID string `json:"music_id"` Name string `json:"name"` Artists []string `json:"artists"` Album string `json:"album"` ReleasedAt string `json:"released_at"` } var ( mapMusics map[string]*Music slcMusics []*Music )
- Definieren Sie die Funktion
init()
, um Daten aus einer Datei zu laden:
func init() { mapMusics = make(map[string]*Music) slcMusics = make([]*Music, 0, 1) data, err := ioutil.ReadFile("../data/musics.json") if err != nil { log.Fatalf("failed to read musics.json:%v", err) } err = json.Unmarshal(data, &slcMusics) if err != nil { log.Fatalf("failed to unmarshal musics:%v", err) } for _, music := range slcMusics { mapMusics[music.MusicID] = music } }
- Definieren Sie zwei Handler-Funktionen, eine zur Rückgabe der gesamten Liste und die andere für ein bestimmtes Musikstück:
func MusicsHandler(w http.ResponseWriter, r *http.Request) { enc := json.NewEncoder(w) enc.Encode(slcMusics) } func MusicHandler(w http.ResponseWriter, r *http.Request) { music, ok := mapMusics[mux.Vars(r)["music_id"]] if!ok { http.NotFound(w, r) return } enc := json.NewEncoder(w) enc.Encode(music) }
- Registrieren Sie die Handler:
func main() { r := mux.NewRouter() r.HandleFunc("/", MusicsHandler) r.HandleFunc("/musics/{music_id}", MusicHandler) http.Handle("/", r) log.Fatal(http.ListenAndServe(":8080", nil)) }
Die Verwendung von mux
ist der von net/http
sehr ähnlich. Rufen Sie zuerst mux.NewRouter()
auf, um ein Routing-Objekt vom Typ *mux.Router
zu erstellen. Die Art und Weise, wie dieses Routing-Objekt Handler registriert, ist genau die gleiche wie die von *http.ServeMux
in der Standardbibliothek, d. h. rufen Sie die Methode HandleFunc()
auf, um eine Handler-Funktion vom Typ func(http.ResponseWriter, *http.Request)
zu registrieren, und rufen Sie die Methode Handle()
auf, um ein Handler-Objekt zu registrieren, das die Schnittstelle http.Handler
implementiert. Oben sind zwei Handler-Funktionen registriert, eine zur Anzeige der Musikinformationsliste und die andere zur Anzeige der Informationen eines bestimmten Musikstücks.
Beachten Sie, dass der Pfad /musics/{music_id}
eine Variable verwendet. Der Variablenname wird innerhalb von {}
angegeben. Er kann einen bestimmten Teil des Pfads abgleichen. In der Handler-Funktion können die Routing-Variablen der Anfrage r
über mux.Vars(r)
abgerufen werden, das map[string]string
zurückgibt und dann über den Variablennamen aufgerufen werden kann, z. B. der Zugriff auf die Variable music_id
im obigen MusicHandler
.
Da *mux.Router
auch die Schnittstelle http.Handler
implementiert, kann es direkt als Handler-Objektparameter von http.Handle("/", r)
registriert werden. Hier wird der Stammpfad /
registriert, was dem Übertragen der Verarbeitung aller Anfragen an *mux.Router
entspricht.
Schließlich wird immer noch http.ListenAndServe(":8080", nil)
verwendet, um einen Web
-Server zu starten und auf eingehende Anfragen zu warten.
Nach dem Ausführen zeigt die Eingabe von localhost:8080
im Browser die Musikliste an. Die Eingabe von localhost:8080/musics/[specific MusicID]
zeigt die detaillierten Informationen der entsprechenden Musik an. Aus dem Verwendungsprozess können wir ersehen, dass die mux
-Bibliothek sehr leichtgewichtig ist und gut in die Standardbibliothek net/http
integriert werden kann.
Wir können auch reguläre Ausdrücke verwenden, um das Muster von Variablen einzuschränken. Angenommen, MusicID
hat ein festes Muster (z. B. M001-001
, d. h. beginnend mit dem Buchstaben M
, gefolgt von 3 Ziffern, und dann verbunden durch -
und 3 Ziffern, was durch den regulären Ausdruck M\d{3}-\d{3}
dargestellt werden kann). Fügen Sie nach dem Variablennamen ein :
hinzu, um die Variable und den regulären Ausdruck zu trennen:
r.HandleFunc("/musics/{music_id:M\\d{3}-\\d{3}}", MusicHandler)
Flexible Matching-Methoden
mux
bietet eine Vielzahl von Möglichkeiten zum Abgleichen von Anfragen. Im Gegensatz dazu kann net/http
nur einen bestimmten Pfad angeben, was etwas umständlich ist.
- Geben Sie den Domänennamen oder Subdomänennamen der Route an:
r.Host("musicplatform.com") r.Host("{subdomain:[a-zA-Z0-9]+}.musicplatform.com")
Die obigen Routen akzeptieren nur Anfragen von der Domäne musicplatform.com
oder ihren Subdomänen. Reguläre Ausdrücke können verwendet werden, wenn der Domänenname angegeben wird. Die zweite Codezeile schränkt ein, dass der erste Teil des Subdomänennamens mehrere Buchstaben oder Zahlen sein muss.
- Geben Sie das Pfadpräfix an:
// Behandeln Sie nur Anfragen mit dem Pfadpräfix `/musics/` r.PathPrefix("/musics/")
- Geben Sie die Anfragemethode an:
// Behandeln Sie nur GET/POST-Anfragen r.Methods("GET", "POST")
- Das verwendete Protokoll (
HTTP
/HTTPS
):
// Behandeln Sie nur https-Anfragen r.Schemes("https")
- Header:
// Behandeln Sie nur Anfragen, bei denen der Wert des Headers X-Requested-With XMLHTTPRequest ist r.Headers("X-Requested-With", "XMLHTTPRequest")
- Abfrageparameter (d. h. der Teil nach
?
in derURL
):
// Behandeln Sie nur Anfragen, bei denen die Abfrageparameter key=value enthalten r.Queries("key", "value")
- Kombinierte Bedingungen:
r.HandleFunc("/", HomeHandler) .Host("musicstore.com") .Methods("GET") .Schemes("http")
Darüber hinaus ermöglicht mux
auch benutzerdefinierte Matcher. Ein benutzerdefinierter Matcher ist eine Funktion vom Typ func(r *http.Request, rm *RouteMatch) bool
, die anhand der Informationen in der Anfrage r
bestimmt, ob die Übereinstimmung erfolgreich ist. Die Struktur http.Request
enthält viele Informationen: HTTP
-Methode, HTTP
-Versionsnummer, URL
, Header usw. Wenn wir beispielsweise nur Anfragen von HTTP/1.1
verarbeiten möchten, können wir dies wie folgt schreiben:
r.MatchrFunc(func(r *http.Request, rm *RouteMatch) bool { return r.ProtoMajor == 1 && r.ProtoMinor == 1 })
Es ist zu beachten, dass mux
in der Reihenfolge der Routenregistrierung übereinstimmt. Daher wird normalerweise empfohlen, spezielle Routen nach vorne und allgemeine Routen nach hinten zu stellen. Wenn es andersherum ist, werden spezielle Routen nicht übereinstimmen:
r.HandleFunc("/specific", specificHandler) r.PathPrefix("/").Handler(catchAllHandler)
Sub-Routen
Manchmal kann das Gruppieren und Verwalten von Routen die Programmmodule übersichtlicher und einfacher zu verwalten machen. Angenommen, die Website erweitert ihr Geschäft und fügt Informationen zu verschiedenen Arten von Musik hinzu (z. B. Pop, Rock usw.). Wir können mehrere Sub-Routen für die separate Verwaltung definieren:
r := mux.NewRouter() ps := r.PathPrefix("/pop_musics").Subrouter() ps.HandleFunc("/", PopMusicsHandler) ps.HandleFunc("/{music_id}", PopMusicHandler) rs := r.PathPrefix("/rock_musics").Subrouter() rs.HandleFunc("/", RockMusicsHandler) rs.HandleFunc("/{music_id}", RockMusicHandler)
Sub-Routen sind im Allgemeinen durch Pfadpräfixe begrenzt. r.PathPrefix()
gibt ein Objekt vom Typ *mux.Route
zurück. Das Aufrufen der Methode Subrouter()
erstellt ein Sub-Routenobjekt *mux.Router
und registriert dann Handler-Funktionen über die Methoden HandleFunc/Handle
dieses Objekts.
Mit der Methode der Sub-Routen können die Routen jedes Teils auch auf ihre jeweiligen Module zum Laden verteilt werden. Definieren Sie eine Methode InitPopMusicsRouter()
in der Datei pop_music.go
, die für die Registrierung von Routen im Zusammenhang mit Popmusik verantwortlich ist:
func InitPopMusicsRouter(r *mux.Router) { ps := r.PathPrefix("/pop_musics").Subrouter() ps.HandleFunc("/", PopMusicsHandler) ps.HandleFunc("/{music_id}", PopMusicHandler) }
Definieren Sie eine Methode InitRockMusicsRouter()
in der Datei rock_music.go
, die für die Registrierung von Routen im Zusammenhang mit Rockmusik verantwortlich ist:
func InitRockMusicsRouter(r *mux.Router) { rs := r.PathPrefix("/rock_musics").Subrouter() rs.HandleFunc("/", RockMusicsHandler) rs.HandleFunc("/{music_id}", RockMusicHandler) }
In der Hauptfunktion von main.go
:
func main() { r := mux.NewRouter() InitPopMusicsRouter(r) InitRockMusicsRouter(r) http.Handle("/", r) log.Fatal(http.ListenAndServe(":8080", nil)) }
Es ist zu beachten, dass die Sub-Routenübereinstimmung das Pfadpräfix enthalten muss, d. h. /pop_musics/
kann mit dem PopMusicsHandler
übereinstimmen.
Konstruieren von Routen-URL
s
Wir können einer Route einen Namen geben. Zum Beispiel:
r.HandleFunc("/musics/{music_id}", MusicHandler).Name("music")
Die obige Route enthält Parameter. Wir können Parameterwerte übergeben, um einen vollständigen Pfad zu erstellen:
fmt.Println(r.Get("music").URL("music_id", "M001-001")) // /musics/M001-001 <nil>
Was zurückgegeben wird, ist ein Objekt vom Typ *url.URL
, und sein Pfadteil ist /musics/M001-001
. Dies gilt auch für Hostnamen und Abfrageparameter:
r := mux.Router() r.Host("{name}.musicplatform.com"). Path("/musics/{music_id}"). HandlerFunc(MusicHandler). Name("music") url, err := r.Get("music").URL("name", "user1", "music_id", "M001-001")
Alle Parameter im Pfad müssen angegeben werden, und die Werte müssen die angegebenen regulären Ausdrücke erfüllen (falls vorhanden). Die Ausführungsausgabe ist:
$ go run main.go http://user1.musicplatform.com/musics/M001-001
Sie können URLHost()
aufrufen, um nur den Hostnamensteil zu generieren, und URLPath()
, um nur den Pfadteil zu generieren.
Middleware
mux
definiert den Middleware-Typ MiddlewareFunc
:
type MiddlewareFunc func(http.Handler) http.Handler
Alle Funktionen, die diesem Typ entsprechen, können als Middleware für mux
verwendet werden. Die Middleware wird angewendet, indem die Methode Use()
des Routing-Objekts *mux.Router
aufgerufen wird. Beim Schreiben von Middleware wird im Allgemeinen der ursprüngliche Handler übergeben. In der Middleware wird die ursprüngliche Handler-Funktion manuell aufgerufen, und dann wird vor und nach allgemeine Verarbeitungslogik hinzugefügt:
func loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Println(r.RequestURI) next.ServeHTTP(w, r) }) }
Angenommen, die Website erfordert die Anmeldung, um auf Musik-bezogene Seiten zuzugreifen. Wir können eine Middleware schreiben, um diese Logik zu verarbeiten. Wenn das Cookie
nicht vorhanden oder ungültig ist, wird es zur Anmeldeseite weitergeleitet. Nach erfolgreicher Anmeldung wird ein Cookie
mit dem Schlüssel token
generiert, um eine erfolgreiche Anmeldung anzuzeigen:
func login(w http.ResponseWriter, r *http.Request) { // Hier wird davon ausgegangen, dass html/template verwendet wird, um die Vorlage zu analysieren und die Anmeldeseite anzuzeigen loginTemplate.ExecuteTemplate(w, "login.tpl", nil) } func doLogin(w http.ResponseWriter, r *http.Request) { r.ParseForm() username := r.Form.Get("username") password := r.Form.Get("password") if username != "user" || password != "123456" { http.Redirect(w, r, "/login", http.StatusFound) return } token := fmt.Sprintf("username=%s&password=%s", username, password) data := base64.StdEncoding.EncodeToString([]byte(token)) http.SetCookie(w, &http.Cookie{ Name: "token", Value: data, Path: "/", HttpOnly: true, Expires: time.Now().Add(24 * time.Hour), }) http.Redirect(w, r, "/", http.StatusFound) }
Um die Anmeldeseite anzuzeigen, werden mehrere template
-Vorlagendateien erstellt und mit html/template
analysiert:
- Anmeldeseite anzeigen:
<!-- login.tpl --> <form action="/login" method="post"> <label>Benutzername:</label> <input name="username"><br> <label>Passwort:</label> <input name="password" type="password"><br> <button type="submit">Login</button> </form>
- Hauptseite:
<ul> <li><a href="/pop_musics/">Popmusik</a></li> <li><a href="/rock_musics/">Rockmusik</a></li> </ul>
- Musikliste und Detailseite (Beispiel):
<!-- pop_musics.tpl --> <ol> {{ range . }} <li> <p>Songname: <a href="/pop_musics/{{ .MusicID }}">{{ .Name }}</a></p> <p>Veröffentlichungsdatum: {{ .ReleasedAt }}</p> <p>Künstler: {{ range .Artists }}{{ . }}{{ if not $.Last }}, {{ end }}{{ end }}</p> <p>Album: {{ .Album }}</p> </li> {{ end }} </ol>
<!-- pop_music.tpl --> <p>MusicID: {{ .MusicID }}</p> <p>Songname: {{ .Name }}</p> <p>Veröffentlichungsdatum: {{ .ReleasedAt }}</p> <p>Künstler: {{ range .Artists }}{{ . }}{{ if not $.Last }}, {{ end }}{{ end }}</p> <p>Album: {{ .Album }}</p>
Als Nächstes analysieren Sie die Vorlagen:
var ( loginTemplate *template.Template ) func init() { var err error loginTemplate, err = template.New("").ParseGlob("./tpls/*.tpl") if err != nil { log.Fatalf("load templates failed:%v", err) } }
Die Logik für den Zugriff auf die entsprechenden Seiten:
func PopMusicsHandler(w http.ResponseWriter, r *http.Request) { loginTemplate.ExecuteTemplate(w, "pop_musics.tpl", slcMusics) } func PopMusicHandler(w http.ResponseWriter, r *http.Request) { music, ok := mapMusics[mux.Vars(r)["music_id"]] if!ok { http.NotFound(w, r) return } loginTemplate.ExecuteTemplate(w, "pop_music.tpl", music) }
Führen Sie die entsprechende Vorlage aus und übergeben Sie die Musikliste oder die Informationen eines bestimmten Musikstücks. Schreiben Sie nun eine Middleware, um einzuschränken, dass nur angemeldete Benutzer auf Musik-bezogene Seiten zugreifen können, und leiten Sie nicht angemeldete Benutzer zur Anmeldeseite weiter, wenn sie darauf zugreifen:
func authenticateMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie("token") if err!= nil { // kein Cookie http.Redirect(w, r, "/login", http.StatusFound) return } data, _ := base64.StdEncoding.DecodeString(cookie.Value) values, _ := url.ParseQuery(string(data)) if values.Get("username")!= "user" || values.Get("password")!= "123456" { // fehlgeschlagen http.Redirect(w, r, "/login", http.StatusFound) return } next.ServeHTTP(w, r) }) }
Dann wenden wir die Middleware authenticateMiddleware
(die eine Anmeldebestätigung erfordert) auf die Sub-Routen von Popmusik und Rockmusik an, während die Anmelde-Sub-Route dies nicht benötigt:
func InitPopMusicsRouter(r *mux.Router) { ps := r.PathPrefix("/pop_musics").Subrouter() // Hier ps.Use(authenticateMiddleware) ps.HandleFunc("/", PopMusicsHandler) ps.HandleFunc("/{music_id}", PopMusicHandler) } func InitRockMusicsRouter(r *mux.Router) { rs := r.PathPrefix("/rock_musics").Subrouter() // Hier rs.Use(authenticateMiddleware) rs.HandleFunc("/", RockMusicsHandler) rs.HandleFunc("/{music_id}", RockMusicHandler) } func InitLoginRouter(r *mux.Router) { ls := r.PathPrefix("/login").Subrouter() ls.Methods("GET").HandlerFunc(login) ls.Methods("POST").HandlerFunc(doLogin) }
Führen Sie das Programm aus (beachten Sie die Art und Weise, wie ein Programm mit mehreren Dateien ausgeführt wird):
$ go run .
Beim Zugriff auf localhost:8080/pop_musics/
wird auf localhost:8080/login
weitergeleitet. Geben Sie den Benutzernamen user
und das Passwort 123456
ein, und nach erfolgreicher Anmeldung wird die Hauptseite angezeigt. Nachfolgende Anfragen müssen nicht erneut überprüft werden, solange das Cookie
gültig ist.
Fazit
Dieser Artikel stellt die leichtgewichtige und leistungsstarke Routing-Bibliothek gorilla/mux
vor. Sie unterstützt eine Vielzahl von Methoden zur Anfrageübereinstimmung, und Sub-Routen erleichtern die Routenverwaltung erheblich. Da sie mit der Standardbibliothek net/http
kompatibel ist, kann sie nahtlos in Programme integriert werden, die net/http
verwenden, und die für net/http
geschriebenen Middleware-Ressourcen nutzen. Im nächsten Artikel werden wir gorilla/handlers
vorstellen – einige häufig verwendete Middleware.
Leapcell: Das Beste aus Serverlosem Webhosting
Schließlich empfehle ich eine Plattform, die sich am besten für die Bereitstellung von Go-Diensten eignet: Leapcell
🚀 Erstellen mit Ihrer Lieblingssprache
Entwickeln Sie mühelos in JavaScript, Python, Go oder Rust.
🌍 Stellen Sie unbegrenzt Projekte kostenlos bereit
Bezahlen Sie nur das, was Sie verwenden – keine Anfragen, keine Gebühren.
⚡ Pay-as-You-Go, keine versteckten Kosten
Keine Leerlaufgebühren, nur nahtlose Skalierbarkeit.
📖 Entdecken Sie unsere Dokumentation
🔹 Folgen Sie uns auf Twitter: @LeapcellHQ