Ich bin relativ neu zu Golang und versuche, die beste Möglichkeit, dies idiomatisch zu tun herauszufinden.Kontext an Gorilla Mux übergeben - Go Idiome
Ich habe ein Array von Routen, die ich statisch definiere und an gorilla/mux
übergebe. Ich wickle jede Handler-Funktion mit etwas ein, um die Anfrage zeitlich anzupassen und Paniken zu behandeln (hauptsächlich, damit ich verstehen konnte, wie die Umhüllung funktionierte).
Ich möchte, dass sie Zugriff auf einen "Kontext" haben können - eine Struktur, die ein-pro-HTTP-Server sein wird, der Dinge wie Datenbank-Handles, Config usw. haben könnte. Was ich nicht tue möchte eine statische globale Variable verwenden.
Die Art, wie ich es gerade mache, kann ich den Wrappern Zugriff auf die Kontextstruktur geben, aber ich kann nicht sehen, wie dies in den tatsächlichen Handler zu bekommen, wie es ein http.HandlerFunc
sein will. Ich dachte, was ich tun könnte, ist http.HandlerFunc
in eine Art meiner eigenen konvertieren, dass ein Empfänger für Context
war (und tun in ähnlicher Weise für die Wrapper, aber (nach viel spielen etwa) konnte ich dann nicht Handler()
bekommen dies zu akzeptieren.
kann ich nicht helfen, aber denke ich etwas offensichtlich hier bin fehlt. unten-Code.
package main
import (
"fmt"
"github.com/gorilla/mux"
"html"
"log"
"net/http"
"time"
)
type Route struct {
Name string
Method string
Pattern string
HandlerFunc http.HandlerFunc
}
type Context struct {
route *Route
// imagine other stuff here, like database handles, config etc.
}
type Routes []Route
var routes = Routes{
Route{
"Index",
"GET",
"/",
index,
},
// imagine lots more routes here
}
func wrapLogger(inner http.Handler, context *Context) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
inner.ServeHTTP(w, r)
log.Printf(
"%s\t%s\t%s\t%s",
r.Method,
r.RequestURI,
context.route.Name,
time.Since(start),
)
})
}
func wrapPanic(inner http.Handler, context *Context) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
log.Printf("panic caught: %+v", err)
http.Error(w, http.StatusText(500), 500)
}
}()
inner.ServeHTTP(w, r)
})
}
func newRouter() *mux.Router {
router := mux.NewRouter().StrictSlash(true)
for _, route := range routes {
// the context object is created here
context := Context {
&route,
// imagine more stuff here
}
router.
Methods(route.Method).
Path(route.Pattern).
Name(route.Name).
Handler(wrapLogger(wrapPanic(route.HandlerFunc, &context), &context))
}
return router
}
func index(w http.ResponseWriter, r *http.Request) {
// I want this function to be able to have access to 'context'
fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
}
func main() {
fmt.Print("Starting\n");
router := newRouter()
log.Fatal(http.ListenAndServe("127.0.0.1:8080", router))
}
hier ein Weg, es zu tun, aber es scheint ziemlich schrecklich. ich denke nicht helfen kann, aber es muss sei ein besserer Weg, es zu tun - vielleicht zur Unterklasse (?) http.Handler
.
package main
import (
"fmt"
"github.com/gorilla/mux"
"html"
"log"
"net/http"
"time"
)
type Route struct {
Name string
Method string
Pattern string
HandlerFunc ContextHandlerFunc
}
type Context struct {
route *Route
secret string
}
type ContextHandlerFunc func(c *Context, w http.ResponseWriter, r *http.Request)
type Routes []Route
var routes = Routes{
Route{
"Index",
"GET",
"/",
index,
},
}
func wrapLogger(inner ContextHandlerFunc) ContextHandlerFunc {
return func(c *Context, w http.ResponseWriter, r *http.Request) {
start := time.Now()
inner(c, w, r)
log.Printf(
"%s\t%s\t%s\t%s",
r.Method,
r.RequestURI,
c.route.Name,
time.Since(start),
)
}
}
func wrapPanic(inner ContextHandlerFunc) ContextHandlerFunc {
return func(c *Context, w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
log.Printf("panic caught: %+v", err)
http.Error(w, http.StatusText(500), 500)
}
}()
inner(c, w, r)
}
}
func newRouter() *mux.Router {
router := mux.NewRouter().StrictSlash(true)
for _, route := range routes {
context := Context{
&route,
"test",
}
router.Methods(route.Method).
Path(route.Pattern).
Name(route.Name).
HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
wrapLogger(wrapPanic(route.HandlerFunc))(&context, w, r)
})
}
return router
}
func index(c *Context, w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, %q secret is %s\n", html.EscapeString(r.URL.Path), c.secret)
}
func main() {
fmt.Print("Starting\n")
router := newRouter()
log.Fatal(http.ListenAndServe("127.0.0.1:8080", router))
}
Werfen Sie einen Blick auf http://github.com/ Alexedwards/Stack - Es bietet eine schöne Zusammenfassung für das Stapeln von Middlewares (Handler) mit einem einfachen Weg zu pa ss Kontext durch die Kette. – Nadh
Gorilla selbst hat ein Kontext-Paket. – freeformz
@freeformz Das Context-Paket von Gorilla ist eher für den Anfragekontext als für den Programmkontext gedacht. Insbesondere wird es am Ende einer Anfrage automatisch gelöscht (wenn Sie gorilla/mux verwenden). –