2013-01-16 1 views
7

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:

  1. 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łasny ServeHTTP.

  2. Użyj rozwidlonej wersji mux (mniej niż idealna).

  3. 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 w ServeHTTP.

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. 

Odpowiedz

8

Można utworzyć (FUNC) typ, który ma ServeHTTP, który robi wszystko, czego potrzebujesz, a następnie wewnętrznie wywołuje pierwotną funkcję, a następnie konwertować teleskopowe do tego typu:

package main 

import (
     "fmt" 
     "log" 
     "net/http" 
) 

type wrappedHandler func(w http.ResponseWriter, r *http.Request) 

func (h wrappedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 
     log.Println("Do Other GAE Stuff") 
     h(w, r) 
} 

func handler(w http.ResponseWriter, r *http.Request) { 
     fmt.Fprintf(w, "Hi!") 
} 

func main() { 
     http.Handle("/", wrappedHandler(handler)) 
     http.ListenAndServe(":8080", nil) 
} 

Jeśli chcesz przekazać coś do handler() func, możesz dodać go do podpisu, np:

type wrappedHandler func(w http.ResponseWriter, r *http.Request, conn *db.Connection) 

func (h wrappedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 
     conn := db.CreateConnection(); 
     h(w, r, conn) 
} 


func handler(w http.ResponseWriter, r *http.Request, conn *db.Connection) { 
     data := conn.AllTheData() 
     fmt.Fprintf(w, data) 
} 
+1

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 :) –

+1

Ś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

+1

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. –