Передача переменных в http обработчики
Что ни день то вопрос. В общем делал я как все: Слушал порт, с nil в качестве хендлера
err := http.ListenAndServeTLS(configuration.TLSPort, TLSFolderAdress+"Certificate.pem", TLSFolderAdress+"Key.pem", nil)
(на случай если не понятно про что)
Дальше обычное http.HandleFunc с паттерном строки и функцией обработчиком. Когда нужно было взять что-то снаружи этой функции закидывал это в глобальные переменные (обьявленные вне функций) и брал просто используя такие переменные.
Но что-то подсказывает, что вообще так делать не надо, что вызывает вопрос:
Как передать в функцию обработчик что-то кроме http.ResponseWriter и *http.Request которые по дефолту передаёт http.HandleFunc и больше ничего передавать не хочет?
Чтобы отбросить глупые вопросы - у программы есть большая часть которая держит данные в памяти и работает с ними и сохранять их как файлы чтобы тут-же прочитать другой функцией - ну как-то звучит неразумно.
Ответы (1 шт):
Вообще глобальным HTTP-обработчиком пользоваться крайне
не рекомендую. Да и глобальным состоянием
в общем. Тем более, что пакет
net/http вам предоставляет всё необходимое для работы
с нормально инкапсулированными сущностями. Делаете свою
собственную сущность обработчика:
type APIHandler struct {
db DB
repo DBRepo
// …
}
func NewAPIHandler(db DB, repo DBRepo, /* … */) (h *APIHandler, err error) {
// …
}
func (h *APIHandler) Users(w http.ResponseWriter, r *http.Request) { /* … */ }
func (h *APIHandler) Posts(w http.ResponseWriter, r *http.Request) { /* … */ }
Делаете свой роутер и сервер (глобальный сервер по умолчанию без таймаутов, если я правильно помню, так что любой Вася Кулхацкер его вам положит) и запускаете:
h, err = NewAPIHandler(db, repo)
// Check err.
m := http.NewServeMux()
m.HandleFunc("/users", h.Users)
m.HandleFunc("/posts", h.Posts)
var srv = &http.Server{
Addr: ":8080",
Handler: m,
TLSConfig: /* … */,
IdleTimeout: 120 * time.Second,
MaxHeaderBytes: 16 * 1024,
ReadHeaderTimeout: 10 * time.Second,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
err = srv.ListenAndServe()
// Check err.
Можете сделать отдельные обработчики для отдельных методов API, а можете как здесь использовать один. Пакет гибкий, всё зависит от сложности и ширины вашего задания.
После уточнений из комментариев, стало понятно,
что ваш вопрос также и про спуск данных «вниз
по стеку». Сейчас для этого в основном
используется
context.Context. Пример middleware:
func (h *APIHandler) Middleware(next http.Handler) (wrapped http.Handler) {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var id, err = GetID()
// Check err.
r = r.WithContext(context.WithValue(r.Context(), ctxKeyID, id))
next.ServeHTTP(w, r)
})
}
m.Handle("/users", h.Middleware(http.HandlerFunc(h.Users)))
Пример использования контекста в обработчике:
func (h *APIHandler) Users(w http.ResponseWriter, r *http.Request) {
var ctx = r.Context()
var v = ctx.Value(ctxKeyID)
var id, ok = v.(ID)
if !ok {
// Ошибка: либо неверен тип, либо значения нету вообще.
}
// …
}
О ключах для контекста я писал на английском StackOverflow.