7

这是一个最佳实践问题,可能没有一个正确答案。似乎我的大多数处理程序都需要执行一些常见的初始化作业,然后才能开始处理特定于处理程序的工作。示例包括用户身份验证、检测语言环境和加载翻译后的字符串、检查 memcached 值等等。

在内部处理其中一些任务似乎是合理的init,但大多数都需要Http.Requestappengine.Context. 据我所知,这留下了三个选择:

  1. 最后实现ServeHTTP并添加执行自定义初始化函数的能力。问题是,我将无法使用实现自己的ServeHTTP.

  2. 使用多路复用器的分叉版本(不太理想)。

  3. startHandler在整个应用程序的每个处理程序的开头放置一个函数。看起来很麻烦,尽管我认为它可以清楚地说明正在发生的事情,而不是在ServeHTTP.

处理所有处理程序共有的工作的首选方法是什么?我错过了另一种方法吗?


这是 minikomi 的答案中概述的方法的完整 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 回答