To jest pytanie dotyczące najlepszej praktyki, które prawdopodobnie nie ma jednej prawidłowej odpowiedzi. Wydaje się, że większość moich programów obsługi musi wykonać wiele typowych zadań inicjowania przed rozpoczęciem pracy związanej z obsługą. Przykładem może być autoryzacja użytkownika, wykrywanie ustawień regionalnych i ładowanie przetłumaczonych łańcuchów, sprawdzanie wartości zapisanych w pamięci i tak dalej.Realizacja typowych zadań obsługi App Engine za pomocą Go
Wydaje się rozsądne, aby dbać o niektórych z tych zadań w ramach init
, ale najbardziej wymagają Http.Request
lub appengine.Context
. O ile widzę, to pozostawia trzy możliwości:
wdrożyć
ServeHTTP
i dodać możliwość wykonywania funkcji niestandardowych startowy na końcu. Problem polegał na tym, że nie byłbym w stanie użyć Muilla Gorilla, który implementuje swój własnyServeHTTP
.Użyj rozwidlonej wersji mux (mniej niż idealna).
Umieść funkcję
startHandler
na początku każdego programu obsługi w całej aplikacji. Wydaje się kłopotliwe, chociaż wydaje mi się, że to wyjaśnia dokładnie, co się dzieje, w przeciwieństwie do "ukrywania" wspólnego kodu wServeHTTP
.
Jaki jest preferowany sposób dbania o miejsca pracy wspólne dla wszystkich osób obsługujących? Czy brakuje mi innego podejścia?
Oto pełna wersja App Engine opisana w odpowiedzi minikomi. Warto również odwiedzić 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)
}
Co wyrzucił mnie na chwilę jest konieczność korzystania router.Handle
i nie router.HandleFunc
. Zakładając odpowiedni podmiot w magazynie danych, wyjście wygląda następująco:
Locale: en_US, timezone: UTC.
I początkowo myślałem nie mogłem w ten sposób użyć Goryla Gorilla, potem zauważyłem obsadę! Miły. Ten samouczek zawiera przykładowy kod: http://shadynasty.biz/blog/2012/08/07/painless-web-handlers-in-go/. Będę edytować pierwszy post za pomocą przykładu specyficznego dla GAE. Dzięki :) –
Świetnie! To trochę sztuczka, aby najpierw objąć umysł (func ma funkcję?), Ale jest bardzo potężny! Myślę, że ten wpis na blogu to miejsce, w którym po raz pierwszy się tego nauczyłem. Dobra seria. – minikomi
Trudno mi było zrozumieć, że możesz efektywnie mieć dwa ServeHTTP w serii: pierwszy MUX, a potem własny. Pytanie zaktualizowane o przykładową aplikację App Engine. –