这是一个最佳实践问题,可能没有一个正确答案。似乎我的大多数处理程序都需要执行一些常见的初始化作业,然后才能开始处理特定于处理程序的工作。示例包括用户身份验证、检测语言环境和加载翻译后的字符串、检查 memcached 值等等。
在内部处理其中一些任务似乎是合理的init
,但大多数都需要Http.Request
或appengine.Context
. 据我所知,这留下了三个选择:
最后实现
ServeHTTP
并添加执行自定义初始化函数的能力。问题是,我将无法使用实现自己的ServeHTTP
.使用多路复用器的分叉版本(不太理想)。
startHandler
在整个应用程序的每个处理程序的开头放置一个函数。看起来很麻烦,尽管我认为它可以清楚地说明正在发生的事情,而不是在ServeHTTP
.
处理所有处理程序共有的工作的首选方法是什么?我错过了另一种方法吗?
这是 minikomi 的答案中概述的方法的完整 App Engine 示例。访问Jeff Wendling 的教程也很值得。
package app
import (
"fmt"
"log"
"net/http"
"appengine"
"appengine/datastore"
"github.com/gorilla/context"
"github.com/gorilla/mux"
)
type Config struct {
DefaultLocale string
DefaultTimezone string
}
type ContextKey int
const (
SiteConfig ContextKey = iota
// ...
)
type InitHandler func(http.ResponseWriter, *http.Request, appengine.Context)
func (h InitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// All handler initialisation tasks go here
c := appengine.NewContext(r)
k := datastore.NewKey(c, "Config", "site:config", 0, nil)
config := new(Config)
if err := datastore.Get(c, k, config); err != nil {
log.Fatal("Couldn't read config from datastore: %s\n", err.Error())
}
context.Set(r, SiteConfig, config)
// Finally, call the handler itself
h(w, r, c)
}
func init () {
r := mux.NewRouter()
r.Handle("/", InitHandler(home)) // Note: NOT r.HandleFunc!
http.Handle("/", r)
}
func home(w http.ResponseWriter, r *http.Request, c appengine.Context) {
site := context.Get(r, SiteConfig).(*Config)
fmt.Fprintf(w, "Locale: %s, timezone: %s.", site.DefaultLocale, site.DefaultTimezone)
}
让我有一阵子的是需要使用router.Handle
而不是router.HandleFunc
. 假设数据存储中有适当的实体,输出如下所示:
Locale: en_US, timezone: UTC.