使用Go完成常见的App Engine处理任务

时间:2013-01-16 00:34:03

标签: google-app-engine go

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

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

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

  2. 使用分叉版本的mux(不太理想)。

  3. 在整个应用程序的每个处理程序的开头放置一个startHandler函数。看起来很麻烦,虽然我认为它确切地说明了正在发生的事情而不是“隐藏”ServeHTTP中的公共代码。

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


    这是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.
    

1 个答案:

答案 0 :(得分: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)
}