上周这篇文章受到了一些启发,我正在重构一个应用程序,我必须更明确地将上下文(数据库池,会话存储等)传递给我的处理程序.
但是,我遇到的一个问题是,如果没有全局模板映射,ServeHTTP
我的自定义处理程序类型(满足http.Handler
)上的方法将无法再访问映射以呈现模板.
我需要保留全局templates
变量,或者将我的自定义处理程序类型重新定义为结构.
有没有更好的方法来实现这一目标?
func.go
package main import ( "fmt" "log" "net/http" "html/template" "github.com/gorilla/sessions" "github.com/jmoiron/sqlx" "github.com/zenazn/goji/graceful" "github.com/zenazn/goji/web" ) var templates map[string]*template.Template type appContext struct { db *sqlx.DB store *sessions.CookieStore } type appHandler func(w http.ResponseWriter, r *http.Request) (int, error) func (ah appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // templates must be global for us to use it here status, err := ah(w, r) if err != nil { log.Printf("HTTP %d: %q", status, err) switch status { case http.StatusNotFound: // Would actually render a "http_404.tmpl" here... http.NotFound(w, r) case http.StatusInternalServerError: // Would actually render a "http_500.tmpl" here // (as above) http.Error(w, http.StatusText(status), status) default: // Would actually render a "http_error.tmpl" here // (as above) http.Error(w, http.StatusText(status), status) } } } func main() { // Both are 'nil' just for this example context := &appContext{db: nil, store: nil} r := web.New() r.Get("/", appHandler(context.IndexHandler)) graceful.ListenAndServe(":8000", r) } func (app *appContext) IndexHandler(w http.ResponseWriter, r *http.Request) (int, error) { fmt.Fprintf(w, "db is %q and store is %q", app.db, app.store) return 200, nil }
struct.go
package main import ( "fmt" "log" "net/http" "html/template" "github.com/gorilla/sessions" "github.com/jmoiron/sqlx" "github.com/zenazn/goji/graceful" "github.com/zenazn/goji/web" ) type appContext struct { db *sqlx.DB store *sessions.CookieStore templates map[string]*template.Template } // We need to define our custom handler type as a struct type appHandler struct { handler func(w http.ResponseWriter, r *http.Request) (int, error) c *appContext } func (ah appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { status, err := ah.handler(w, r) if err != nil { log.Printf("HTTP %d: %q", status, err) switch status { case http.StatusNotFound: // Would actually render a "http_404.tmpl" here... http.NotFound(w, r) case http.StatusInternalServerError: // Would actually render a "http_500.tmpl" here // (as above) http.Error(w, http.StatusText(status), status) default: // Would actually render a "http_error.tmpl" here // (as above) http.Error(w, http.StatusText(status), status) } } } func main() { // Both are 'nil' just for this example context := &appContext{db: nil, store: nil} r := web.New() // A little ugly, but it works. r.Get("/", appHandler{context.IndexHandler, context}) graceful.ListenAndServe(":8000", r) } func (app *appContext) IndexHandler(w http.ResponseWriter, r *http.Request) (int, error) { fmt.Fprintf(w, "db is %q and store is %q", app.db, app.store) return 200, nil }
是否有更简洁的方法将context
实例传递给ServeHTTP
?
请注意,go build -gcflags=-m
在堆分配的组中,两个选项似乎都没有变得更糟:&appContext
在两种情况下,文字都转义到堆(如预期的那样),尽管我的解释是基于结构的选项确实context
在每个上传递了第二个指针(to )请求 - 纠正我,如果我在这里错了,因为我希望能够更好地理解这一点.
我并不完全相信全局包在主包中是坏的(即不是lib)只要它们以这种方式安全使用(只读/互斥/池),但我确实喜欢明确传递上下文提供的清晰度.