Передача контекста в методы интерфейса

Несколько вдохновленЭта статья На прошлой неделе я занимаюсь рефакторингом приложения, которое мне нужно более явно передать контексту (пулы БД, хранилища сессий и т. д.) моим обработчикам.

Однако у меня есть одна проблема: без карты глобальных шаблоновServeHTTP метод на мой тип пользовательского обработчика (как удовлетворитьhttp.Handler) больше не может получить доступ к карте для визуализации шаблона.

Мне нужно либо сохранить глобальныйtemplates переменная, или переопределите мой пользовательский тип обработчика как структуру.

Есть ли лучший способ добиться этого?

func.go

package main

import (
    "fmt"
    "log"
    "net/http"

    "html/template"

    "github.com/gorilla/sessions"
    "github.com/jmoiron/sqlx"
    "github.com/zenazn/goji/graceful"
    "github.com/zenazn/goji/web"
)

var templates map[string]*template.Template

type appContext struct {
    db    *sqlx.DB
    store *sessions.CookieStore
}

type appHandler func(w http.ResponseWriter, r *http.Request) (int, error)

func (ah appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // templates must be global for us to use it here
    status, err := ah(w, r)
    if err != nil {
        log.Printf("HTTP %d: %q", status, err)
        switch status {
        case http.StatusNotFound:
            // Would actually render a "http_404.tmpl" here...
            http.NotFound(w, r)
        case http.StatusInternalServerError:
            // Would actually render a "http_500.tmpl" here
            // (as above)
            http.Error(w, http.StatusText(status), status)
        default:
            // Would actually render a "http_error.tmpl" here
            // (as above)
            http.Error(w, http.StatusText(status), status)
        }
    }
}

func main() {
    // Both are 'nil' just for this example
    context := &appContext{db: nil, store: nil}

    r := web.New()
    r.Get("/", appHandler(context.IndexHandler))
    graceful.ListenAndServe(":8000", r)
}

func (app *appContext) IndexHandler(w http.ResponseWriter, r *http.Request) (int, error) {
    fmt.Fprintf(w, "db is %q and store is %q", app.db, app.store)
    return 200, nil
}

struct.go

package main

import (
    "fmt"
    "log"
    "net/http"

    "html/template"

    "github.com/gorilla/sessions"
    "github.com/jmoiron/sqlx"
    "github.com/zenazn/goji/graceful"
    "github.com/zenazn/goji/web"
)

type appContext struct {
    db        *sqlx.DB
    store     *sessions.CookieStore
    templates map[string]*template.Template
}

// We need to define our custom handler type as a struct
type appHandler struct {
    handler func(w http.ResponseWriter, r *http.Request) (int, error)
    c       *appContext
}

func (ah appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    status, err := ah.handler(w, r)
    if err != nil {
        log.Printf("HTTP %d: %q", status, err)
        switch status {
        case http.StatusNotFound:
            // Would actually render a "http_404.tmpl" here...
            http.NotFound(w, r)
        case http.StatusInternalServerError:
            // Would actually render a "http_500.tmpl" here
            // (as above)
            http.Error(w, http.StatusText(status), status)
        default:
            // Would actually render a "http_error.tmpl" here
            // (as above)
            http.Error(w, http.StatusText(status), status)
        }
    }
}

func main() {
    // Both are 'nil' just for this example
    context := &appContext{db: nil, store: nil}

    r := web.New()
    // A little ugly, but it works.
    r.Get("/", appHandler{context.IndexHandler, context})
    graceful.ListenAndServe(":8000", r)
}

func (app *appContext) IndexHandler(w http.ResponseWriter, r *http.Request) (int, error) {
    fmt.Fprintf(w, "db is %q and store is %q", app.db, app.store)
    return 200, nil
}

Есть ли более чистый способ пройтиcontext экземпляр дляServeHTTP?

Обратите внимание, чтоgo build -gcflags=-m показывает, что ни один из вариантов не выглядит хуже в командах распределения кучи:&appContext в обоих случаях литерал сбрасывается в кучу (как и ожидалось), хотя моя интерпретация заключается в том, что опция на основе структуры передает второй указатель (наcontext) по каждому запросупоправьте меня если я не прав как я хотел бы получить лучшее понимание этого.

Я не совсем уверен, что глобальные переменные плохи в основном пакете (то есть не в lib), при условии, что они безопасны для использования таким образом (только для чтения / mutexes / a pool), но мне нравится ясность, необходимая для явной передачи контекста.

Ответы на вопрос(2)

Ваш ответ на вопрос