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

Ric*_*her 7 google-app-engine go

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

在内部处理其中的一些任务似乎是合理的init,但大多数需要Http.Request或者appengine.Context.据我所知,这有三个选择:

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

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

  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)
}
Run Code Online (Sandbox Code Playgroud)

什么让我失望的是需要使用router.Handle而不是router.HandleFunc.假设数据存储区中有适当的实体,输出如下:

Locale: en_US, timezone: UTC.
Run Code Online (Sandbox Code Playgroud)

min*_*omi 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)
}
Run Code Online (Sandbox Code Playgroud)

如果要将某些内容传递给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)
}
Run Code Online (Sandbox Code Playgroud)