向gorilla mux-go习语传递上下文
我对golang还比较陌生,我正在努力找到最好的方法来习惯性地做这件事 我有一个静态定义并传递给向gorilla mux-go习语传递上下文,go,gorilla,Go,Gorilla,我对golang还比较陌生,我正在努力找到最好的方法来习惯性地做这件事 我有一个静态定义并传递给gorilla/mux的路由数组。我正在用一些东西来包装每个处理函数,以对请求进行计时并处理恐慌(主要是为了了解包装是如何工作的) 我希望他们每个人都能够访问一个“上下文”——一个每个http服务器一个的结构,可能有数据库句柄、配置等。我不想做的是使用静态全局变量 按照我目前的做法,我可以让包装器访问上下文结构,但我看不出如何将其放入实际的处理程序中,因为它希望它是一个http.HandlerFunc
gorilla/mux
的路由数组。我正在用一些东西来包装每个处理函数,以对请求进行计时并处理恐慌(主要是为了了解包装是如何工作的)
我希望他们每个人都能够访问一个“上下文”——一个每个http服务器一个的结构,可能有数据库句柄、配置等。我不想做的是使用静态全局变量
按照我目前的做法,我可以让包装器访问上下文结构,但我看不出如何将其放入实际的处理程序中,因为它希望它是一个http.HandlerFunc
。我想我能做的是将http.HandlerFunc
转换成我自己的一种类型,它是Context
的接收者(对包装器也是如此,但是(在反复玩弄之后)我无法让Handler()
接受这一点
我忍不住想我遗漏了一些明显的东西。代码如下
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))
}
这里有一种方法,但它看起来很可怕。我忍不住想一定有更好的方法来做这件事——也许是对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))
}
我正在学习GO,目前正处于一个几乎相同的问题的中间,这就是我如何处理它:
首先,我认为您遗漏了一个重要的细节:Go中没有全局变量。它是包的作用域。Go中唯一真正的全局变量是true
和false
(您不能更改这些变量或创建自己的变量)
因此,将变量的作用域设置为package main
来保存程序的上下文是非常好的。我来自C/C++背景,这花了我一点时间来适应。因为变量是包作用域,所以它们不会受到影响。如果另一个包中的某个东西需要这样一个变量,您必须显式地传递它。
在有意义的时候不要害怕使用包变量。这可以帮助您降低程序的复杂性,并且在很多情况下使自定义处理程序更简单(调用http.HandlerFunc()
并传递闭包就足够了)
这样一个简单的处理程序可能如下所示:
func simpleHandler(c Context, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// FIXME Do something with our context
next.ServeHTTP(w, r)
})
}
并由以下人员使用:
r = mux.NewRouter()
http.Handle("/", simpleHandler(c, r))
如果您的需求更复杂,您可能需要实现自己的http.Handler
。请记住http.Handler
只是一个实现ServeHTTP(w http.ResponseWriter,r*http.Request)
的接口
这是未经测试的,但应该可以让您实现95%的目标:
package main
import (
"net/http"
)
type complicatedHandler struct {
h http.Handler
opts ComplicatedOptions
}
type ComplicatedOptions struct {
// FIXME All of the variables you want to set for this handler
}
func (m complicatedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// FIXME Do stuff before serving page
// Call the next handler
m.h.ServeHTTP(w, r)
// FIXME Do stuff after serving page
}
func ComplicatedHandler(o ComplicatedOptions) func(http.Handler) http.Handler {
return func(h http.Handler) http.Handler {
return complicatedHandler{h, o}
}
}
要使用它:
r := mux.NewRouter()
// FIXME: Add routes to the mux
opts := ComplicatedOptions{/* FIXME */}
myHandler := ComplicatedHandler(opts)
http.Handle("/", myHandler(r))
有关更发达的处理程序示例,请参见,该示例是从中无耻地剽窃的
进一步阅读:
- (用于链接大量处理程序)
看一看-它为堆叠中间件(处理程序)提供了一个很好的抽象,并提供了通过链传递上下文的简单方法。Gorilla本身有一个上下文包。@freeformz Gorilla的上下文包用于请求上下文,而不是程序上下文。特别是,它在请求结束时会自动清除(如果您使用的是gorilla/mux)。