Sono ragionevolmente nuovo in Golang e sto cercando di risolvere il modo migliore per farlo in modo idiomatico.Passare il contesto a gorilla mux - go idiomi
Ho una serie di percorsi che sto definendo staticamente e passando a gorilla/mux
. Sto facendo il wrapping di ogni funzione del gestore con qualcosa per volta la richiesta e gestisco il panico (principalmente così ho potuto capire come funzionava il wrapping).
Voglio che ognuno di essi sia in grado di accedere a un "contesto" - una struttura che sarà un-per-http-server, che potrebbe avere cose come handle di database, configurazione ecc. Cosa non faccio voglio fare è usare una variabile globale statica.
Nel modo in cui lo sto facendo, posso dare ai wrapper l'accesso alla struttura di contesto, ma non riesco a vedere come ottenere questo nel gestore vero e proprio, poiché vuole che sia uno http.HandlerFunc
. Ho pensato che avrei potuto fare è convertire http.HandlerFunc
in un tipo del mio proprio che era un ricevitore per Context
(e fare allo stesso modo per gli involucri, ma (dopo molto giocare su) non ho potuto quindi ottenere Handler()
di accettare questo.
non posso fare a meno di pensare che mi manca qualcosa di ovvio qui. codice di seguito.
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))
}
Ecco un modo per farlo, ma sembra abbastanza orribile. non posso fare a meno di pensare che ci deve essere un modo migliore per farlo - forse per sottoclasse (?) 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))
}
Date un'occhiata a http://github.com/ alexedwards/stack - Fornisce un bel riassunto per l'accatastamento di middleware (handler) con un modo semplice per pa contesto ss attraverso la catena. – Nadh
Gorilla stesso ha un pacchetto di contesto. – freeformz
@freeformz Il pacchetto di contesto di Gorilla è destinato al contesto della richiesta, piuttosto che al contesto del programma. In particolare, viene cancellato automaticamente alla fine di una richiesta (se si utilizza gorilla/mux). –