这是一个最佳实践问题,可能没有一个正确的答案。看起来我的大部分处理程序在开始处理程序特定的工作之前需要执行许多常见的初始化工作。例子是用户身份验证,检测语言环境和加载翻译的字符串,检查memcached值,等等。使用Go完成常见App Engine处理程序任务
在init
内处理这些任务中的一部分似乎是合理的,但大多数需要Http.Request
或appengine.Context
。至于我可以看到这留下三个选择:
实施
ServeHTTP
,并在结尾处加上执行自定义的初始化功能的能力。问题是,我不能使用实现自己的ServeHTTP
的大猩猩多路复用器。使用多路复用器的分叉版本(低于理想值)。
将
startHandler
函数放在整个应用程序中每个处理程序的开头。看起来很麻烦,尽管我想它确切地说明发生了什么,而不是'隐藏'ServeHTTP
中的常见代码。
如何照顾所有处理者共同的工作的首选方式是什么?我错过了另一种方法吗?
下面是minikomi的答案中概述的完整的App Engine示例。这也是值得的访问Jeff Wendling's tutorial。
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.
我最初以为我不能用这种方式使用大猩猩多路复用器,然后我注意到演员!尼斯。本教程提供了更多示例代码:http://shadynasty.biz/blog/2012/08/07/painless-web-handlers-in-go/。我将用GAE特定的示例编辑第一篇文章。谢谢:) –
太棒了!首先打包你的头脑是一个小技巧(一个func有一个函数?),但它非常强大!我认为这篇博文是我第一次了解它的地方。好系列。 – minikomi
我很难意识到你可以有效地连接两个ServeHTTP:第一个多路复用器,然后是你自己的。使用示例App Engine应用更新了问题。 –