【问题标题】:Accomplishing common App Engine handler tasks with Go使用 Go 完成常见的 App Engine 处理程序任务
【发布时间】:2012-12-30 05:58:38
【问题描述】:

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

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

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

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

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

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


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

【问题讨论】:

    标签: google-app-engine go


    【解决方案1】:

    您可以创建一个 (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)
    }
    

    【讨论】:

    • 我最初以为我不能以这种方式使用 Gorilla mux,然后我注意到了演员表!好的。本教程提供了更多示例代码:shadynasty.biz/blog/2012/08/07/painless-web-handlers-in-go。我将使用 GAE 特定示例编辑第一篇文章。谢谢:)
    • 太棒了!一开始你的想法有点小技巧(一个func有一个函数?)但它非常强大!我认为那篇博文也是我第一次学习它的地方。好系列。
    • 我很难意识到你可以有效地串联两个 ServeHTTP:第一个是 mux 的,然后是你自己的。使用示例 App Engine 应用更新了问题。
    猜你喜欢
    • 2019-03-04
    • 2013-11-24
    • 1970-01-01
    • 1970-01-01
    • 2013-09-22
    • 1970-01-01
    • 1970-01-01
    • 2014-03-08
    • 1970-01-01
    相关资源
    最近更新 更多