這是一個最佳實踐問題,可能沒有一個正確的答案。看起來我的大部分處理程序在開始處理程序特定的工作之前需要執行許多常見的初始化工作。例子是用戶身份驗證,檢測語言環境和加載翻譯的字符串,檢查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應用更新了問題。 –