gpt4 book ai didi

go - 将上下文传递给 gorilla mux - go 习语

转载 作者:IT王子 更新时间:2023-10-29 01:14:54 26 4
gpt4 key购买 nike

我是 golang 的新手,我正在努力找出最好的方法来做到这一点。

我有一组静态定义并传递给 gorilla/mux 的路由。我用一些东西包装了每个处理函数来计时请求和处理 panic (主要是为了让我能够理解包装是如何工作的)。

我希望他们每个人都能够访问一个“上下文”——一个每个 http 服务器一个的结构,它可能有数据库句柄、配置等东西。我不想要的要做的是使用静态全局变量。

我目前的做法是,我可以让包装器访问上下文结构,但我看不到如何将它放入实际的处理程序中,因为它希望它成为一个 http.HandlerFunc 。我想我能做的是将 http.HandlerFunc 转换成我自己的一种类型,它是 Context 的接收器(并且对包装器做类似的事情,但是(在玩了很多之后about) 然后我无法让 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 中没有全局变量。 widest scope you can have for a variable是包范围。 Go 中唯一真正的全局变量是 predeclared identifierstruefalse(你不能改变它们或自己制作)。

因此,设置一个作用域为 package main 的变量来保存程序的上下文是完全没问题的。来自 C/C++ 背景,这让我花了一些时间来适应。由于变量是包范围的,因此它们不会受到 the problems of global variables 的影响。 .如果另一个包中的某些东西需要这样的变量,您将必须显式传递它。

不要害怕在有意义的时候使用包变量。这可以帮助您降低程序的复杂性,并且在很多情况下可以使您的自定义处理程序更加简单(调用 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) 的接口(interface)。

这是未经测试的,但应该让你完成了大约 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))

有关更完善的处理程序示例,请参阅 basicAuth in goji/httpauth ,从中无耻地扯掉了这个例子。


进一步阅读:

关于go - 将上下文传递给 gorilla mux - go 习语,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31072961/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com