Negroni vs. andere Go Middleware Lösungen: Was macht es einzigartig?
Ethan Miller
Product Engineer · Leapcell

Einführung in die Negroni-Bibliothek
Negroni ist eine Bibliothek, die sich auf HTTP-Middleware konzentriert. Sie ist klein und nicht-intrusiv und fördert die Verwendung von Handlern aus der Standardbibliothek net/http
. Dieser Artikel bietet eine detaillierte Einführung in diese Bibliothek.
I. Warum Middleware verwenden
Während des Entwicklungsprozesses gibt es einige gängige logische Codes, wie z. B. Statistiken, Protokollierung, Debugging usw., und diese Logiken können in jedem Handler erforderlich sein. Wenn diese Codes einzeln zu jedem Handler hinzugefügt werden, ist dies nicht nur umständlich, sondern auch anfällig für Fehler und Auslassungen.
Nehmen wir das Beispiel der Zählung des Zeitverbrauchs eines Handlers. Wenn der Code zum Zählen des Zeitverbrauchs zu jedem Handler hinzugefügt wird, sieht das Beispiel wie folgt aus:
package main import ( "fmt" "net/http" "time" ) func index(w http.ResponseWriter, r *http.Request) { start := time.Now() fmt.Fprintf(w, "leapcell page") fmt.Printf("index elasped:%fs", time.Since(start).Seconds()) } func greeting(w http.ResponseWriter, r *http.Request) { start := time.Now() name := r.FormValue("name") if name == "" { name = "world" } fmt.Fprintf(w, "hello %s", name) fmt.Printf("greeting elasped:%fs\n", time.Since(start).Seconds()) } func main() { mux := http.NewServeMux() mux.HandleFunc("/", index) mux.HandleFunc("/greeting", greeting) http.ListenAndServe(":8000", mux) }
Dieser Ansatz hat jedoch viele Nachteile:
- Mangelnde Flexibilität: Jedes Mal, wenn ein neuer Handler hinzugefügt wird, muss dieser Teil des Codes zum Zählen des Zeitverbrauchs hinzugefügt werden, und diese Codes haben keine direkte Beziehung zur eigentlichen Handler-Logik.
- Anfällig für Auslassungen: Es ist leicht, das Hinzufügen solcher Codes beim Schreiben von Handlern zu vergessen, insbesondere wenn alle möglichen Rückgabepfade berücksichtigt werden, was die Programmierlast erhöht.
- Nicht förderlich für Änderungen: Wenn ein Fehler im statistischen Code vorliegt oder dieser angepasst werden muss, müssen alle beteiligten Handler geändert werden.
- Schwierig, neue Logik hinzuzufügen: Wenn andere statistische Logiken hinzugefügt werden müssen, muss auch der Code aller Handler geändert werden.
Durch die Nutzung der Closure-Funktion der Go-Sprache kann der eigentliche Handler-Code in einer Funktion gekapselt werden, und zusätzliche Logik kann in dieser Funktion ausgeführt werden. Dies wird wie folgt dargestellt:
func elasped(h func(w http.ResponseWriter, r *http.Request)) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { path := r.URL.Path start := time.Now() h(w, r) fmt.Printf("path:%s elasped:%fs\n", path, time.Since(start).Seconds()) } } func index(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "leapcell page") } func greeting(w http.ResponseWriter, r *http.Request) { name := r.FormValue("name") if name == "" { name = "world" } fmt.Fprintf(w, "hello %s", name) } func main() { mux := http.NewServeMux() mux.HandleFunc("/", elasped(index)) mux.HandleFunc("/greeting", elasped(greeting)) http.ListenAndServe(":8000", mux) }
In diesem Beispiel wird der zusätzliche Code, der nichts mit dem Handler zu tun hat, in der Funktion elasped
platziert. Beim Registrieren der Handler-Funktion wird sie nicht direkt mit der ursprünglichen Handler-Funktion verwendet, sondern mit der Funktion elasped
gekapselt. Tatsächlich ist eine Funktion wie elasped
eine Middleware. Sie kapselt die ursprüngliche Handler-Funktion und gibt eine neue Handler-Funktion zurück, die zum Einfügen von Code vor und nach der eigentlichen Verarbeitungslogik geeignet ist und für das Hinzufügen, Ändern und Warten von Vorteil ist.
II. Schnellstart mit Negroni
(I) Installation
Führen Sie den folgenden Befehl aus, um die Negroni-Bibliothek zu installieren:
$ go get github.com/urfave/negroni
(II) Anwendungsbeispiel
package main import ( "fmt" "net/http" "github.com/urfave/negroni" ) func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") }) n := negroni.Classic() n.UseHandler(mux) http.ListenAndServe(":8080", n) }
Negroni ist relativ einfach zu bedienen und kann bequem mit http.Handler
zusammenarbeiten. negroni.Classic()
bietet mehrere häufig verwendete Middleware:
- negroni.Recovery: Sie wird verwendet, um sich von
panic
zu erholen, das während des Programmablaufs auftritt. Wenn einpanic
im Handler-Code auftritt, fängt diese Middleware die Ausnahme ab und verhindert, dass das Programm beendet wird. - negroni.Logger: Sie ist für die Aufzeichnung grundlegender Informationen über Anfragen und Antworten zuständig und implementiert die Protokollierungsfunktion.
- negroni.Static: Sie kann statische Dateidienste im Verzeichnis
public
bereitstellen.
Durch Aufrufen von n.UseHandler(mux)
werden diese Middleware auf den Multiplexer mux
angewendet. Nach dem Ausführen des Programms geben Sie localhost:3000
in den Browser ein, und Sie können die folgende ähnliche Ausgabe in der Konsole sehen:
[negroni] 2025-03-22T18:48:53+08:00 | 200 | 10.9966ms | localhost:8080 | GET /
III. Tiefgehende Funktionen von Negroni
(I) negroni.Handler-Schnittstelle
Die negroni.Handler
-Schnittstelle bietet eine flexiblere Kontrolle über den Ausführungsprozess der Middleware. Die Schnittstelle ist wie folgt definiert:
type Handler interface { ServeHTTP(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) }
Die Signatur der geschriebenen Middleware muss func(http.ResponseWriter, *http.Request, http.HandlerFunc)
sein oder die negroni.Handler
-Schnittstelle implementieren. Zum Beispiel:
func RandomMiddleware(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { if rand.Int31n(100) <= 50 { fmt.Fprintf(w, "hello from RandomMiddleware") } else { next(w, r) } } func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") }) n := negroni.New() n.Use(negroni.HandlerFunc(RandomMiddleware)) n.UseHandler(mux) http.ListenAndServe(":8080", n) }
Der obige Code implementiert eine zufällige Middleware. Es besteht eine Wahrscheinlichkeit von 50 %, dass die Antwort direkt von der RandomMiddleware
-Middleware zurückgegeben wird, und es besteht eine Wahrscheinlichkeit von 50 %, dass die eigentliche Handler-Funktion ausgeführt wird. Führen Sie nach dem Ausführen des Programms ein kontinuierliches Aktualisieren von localhost:8080
im Browser durch, um den Effekt zu beobachten.
Tatsächlich ist func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc)
eine bequeme Art des Schreibens. Beim Aufrufen von n.Use
wird es mit negroni.HandlerFunc
gekapselt, und negroni.HandlerFunc
implementiert die negroni.Handler
-Schnittstelle. Im Folgenden finden Sie den relevanten Code:
// src/github.com/urfave/negroni/negroni.go type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) func (h HandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { h(rw, r, next) }
In ähnlicher Weise wird in net/http
auch func(http.ResponseWriter, *http.Request)
mit http.HandlerFunc
gekapselt, um die http.Handler
-Schnittstelle zu implementieren.
(II) negroni.With-Methode
Wenn es mehrere Middleware gibt, ist es umständlich, sie einzeln mit der Methode n.Use()
hinzuzufügen. Negroni bietet die Methode With()
, die einen oder mehrere negroni.Handler
-Parameter akzeptiert und ein neues Objekt zurückgibt. Das Beispiel ist wie folgt:
func Middleware1(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { fmt.Println("Middleware A begin") next(w, r) fmt.Println("Middleware A end") } func Middleware2(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) { fmt.Println("Middleware B begin") next(w, r) fmt.Println("Middleware B end") } func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") }) n := negroni.New() n = n.With( negroni.HandlerFunc(Middleware1), negroni.HandlerFunc(Middleware2), ) n.UseHandler(mux) http.ListenAndServe(":8080", n) }
(III) Run-Methode
Das Negroni-Objekt bietet die Methode Run()
, mit der das Serverprogramm bequem ausgeführt werden kann. Diese Methode akzeptiert denselben Adressparameter (Addr) wie http.ListenAndServe()
. Das Beispiel ist wie folgt:
func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") }) n := negroni.New() n.UseHandler(mux) n.Run(":8080") }
Wenn der Port nicht angegeben ist, versucht die Methode Run()
, die Umgebungsvariable PORT
zu verwenden. Wenn die Umgebungsvariable PORT
ebenfalls nicht festgelegt ist, wird der Standardport :8080
verwendet.
(IV) Verwendung als http.Handler
Negroni kann problemlos in net/http
-Programmen verwendet werden, und das Objekt negroni.Negroni
kann direkt als http.Handler
an die entsprechende Methode übergeben werden. Das Beispiel ist wie folgt:
func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello World!") }) n := negroni.Classic() n.UseHandler(mux) s := &http.Server{ Addr: ":8080", Handler: n, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, } s.ListenAndServe() }
IV. Eingebaute Middleware
(I) Static
Die negroni.Static
-Middleware kann Dateidienste im angegebenen Verzeichnis bereitstellen. Der Beispielcode lautet wie folgt:
func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "hello world") }) n := negroni.New() n.Use(negroni.NewStatic(http.Dir("./public"))) n.UseHandler(mux) http.ListenAndServe(":8080", n) }
Erstellen Sie ein public
-Verzeichnis im laufenden Verzeichnis des Programms und legen Sie einige Dateien (z. B. 1.txt
, 2.jpg
) darin ab. Nachdem das Programm ausgeführt wurde, können Sie in Ihrem Browser auf localhost:8080/1.txt
und localhost:8080/2.jpg
zugreifen, um diese Dateien anzufordern. Es sollte besonders beachtet werden, dass die Static
-Middleware die Anfrage an die nächste Middleware- oder Handler-Funktion weiterleitet, wenn die entsprechende Datei nicht gefunden werden kann. Wenn Sie beispielsweise localhost:3000/none - exist.txt
in den Browser eingeben, wird die Antwort hello world
angezeigt.
(II) Logger
Im Abschnitt „Schnellstart“ wurde diese Middleware über negroni.Classic()
verwendet. Sie kann auch einzeln verwendet werden, um Anfrageinformationen aufzuzeichnen, und die Methode SetFormat()
kann aufgerufen werden, um das Protokollformat festzulegen. Das Beispiel ist wie folgt:
func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "hello world") }) n := negroni.New() logger := negroni.NewLogger() logger.SetFormat("[{{.Status}} {{.Duration}}] - {{.Request.UserAgent}}") n.Use(logger) n.UseHandler(mux) http.ListenAndServe(":8080", n) }
Der obige Code legt das Protokollformat auf [{{.Status}} {{.Duration}}] - {{.Request.UserAgent}}
fest, d. h. er zeichnet den Antwortstatus, den Zeitverbrauch und den UserAgent
auf.
V. Drittanbieter-Middleware
Zusätzlich zu den integrierten Middleware gibt es in Negroni auch viele Drittanbieter-Middleware. Die vollständige Liste finden Sie unter: https://github.com/urfave/negroni?tab=readme-ov-file#third-party-middleware.
VI. Fazit
Negroni konzentriert sich auf die Middleware-Funktionalität und verfügt nicht über zu viele umständliche und selten verwendete ausgefallene Funktionen. Seine nicht-intrusive Designfunktion ermöglicht die nahtlose Zusammenarbeit mit der Standardbibliothek net/http
und anderen Webbibliotheken (z. B. gorilla/mux
). Diese Funktion bietet Entwicklern großen Komfort beim Erstellen von HTTP-Diensten. Entwickler können Middleware flexibel auswählen und kombinieren, um verschiedene Funktionen wie Protokollierung, Fehlerbehandlung und Anfragestatistiken zu erzielen, ohne dass umfangreiche Änderungen an der bestehenden Code-Architektur erforderlich sind.
Insgesamt ist Negroni eine sehr wertvolle Bibliothek für die Entwicklung von Go-Sprach-HTTP-Diensten. Für diejenigen, die eine effiziente, flexible und wartungsfreundliche Webanwendungsentwicklung anstreben, ist Negroni zweifellos ein hervorragendes Werkzeug, das ein tiefes Verständnis und eine breite Anwendung verdient.
Leapcell: The Best of Serverless Web Hosting
Schließlich möchte ich die beste Plattform für die Bereitstellung von Go-Diensten vorstellen: Leapcell
🚀 Mit Ihrer Lieblingssprache entwickeln
Entwickeln Sie mühelos in JavaScript, Python, Go oder Rust.
🌍 Unbegrenzte Projekte kostenlos bereitstellen
Zahlen Sie nur für das, was Sie nutzen – 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