7

これはベスト プラクティスの質問であり、正解はおそらく 1 つではありません。私のハンドラーのほとんどは、ハンドラーに固有の作業を開始する前に、多くの一般的な初期化ジョブを実行する必要があるようです。例としては、ユーザー認証、ロケールの検出と翻訳された文字列の読み込み、memcached 値のチェックなどがあります。

これらのタスクの一部は 内で処理するのが合理的と思われますinitが、ほとんどの場合、Http.Requestまたはが必要ですappengine.Context。私が見る限り、これには 3 つの選択肢があります。

  1. ServeHTTP最後にカスタム init 関数を実行する機能を実装して追加します。問題は、独自の を実装する Gorilla mux を使用できないことServeHTTPです。

  2. フォークされたバージョンの mux を使用します (理想的とは言えません)。

  3. startHandlerアプリ全体のすべてのハンドラーの先頭に関数を配置します。面倒なように思えますが、一般的なコードを「隠す」のではなく、何が起こっているのかを正確に理解できると思いますServeHTTP

すべてのハンドラーに共通のジョブを処理するための推奨される方法は何ですか? 別のアプローチがありませんか?


これは、ミニコミの回答で概説されているアプローチの完全な 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.
4

1 に答える 1

8

必要なすべてを実行する (func) 型を作成しServeHTTP、元の関数を内部的に呼び出してから、ハンドラーをその型に変換できます。

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)
}

handler()func に何かを渡したい場合は、それを署名に追加できます。

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)
}
于 2013-01-16T08:47:00.767 に答える