Написание промежуточного программного обеспечения для каждого обработчика
Я пытаюсь извлечь некоторую повторяющуюся логику из моих обработчиков и поместить ее в некоторое промежуточное ПО для каждого обработчика: в частности, такие как проверки CSRF, проверка существующего значения сеанса (например, для аутентификации или для страниц предварительного просмотра) и т. Д.
Я прочитал несколько статей по этому вопросу, но многие примеры посвящены промежуточному программному обеспечению для каждого сервера (упаковка http.Handler
): У меня есть меньший набор обработчиков, которые нуждаются в промежуточном программном обеспечении. Большинство других моих страниц нет, и поэтому, если я могу избежать проверки сессий / и т.д. для этих запросов, тем лучше.
Пока что мое промежуточное ПО обычно выглядит примерно так:
func checkCSRF(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// get the session, check/validate/create the token based on HTTP method, etc.
// return HTTP 403 on a failed check
// else invoke the wrapped handler h(w, r)
}
}
Тем не менее, во многих случаях я хочу передать переменную обработчику-оболочке: сгенерированный токен CSRF для передачи в шаблон или структуру, содержащую данные формы - одна промежуточная программа проверяет сеанс на наличие некоторых сохраненных данных формы, прежде чем пользователь нажимает /preview/
URL, иначе он перенаправляет их прочь (так как им нечего предварительно просматривать!).
Я хотел бы передать эту структуру обернутому обработчику, чтобы избавить от необходимости дублировать сессию. Получите / напишите утверждение / проверку логики ошибок, которую я только что написал в промежуточном программном обеспечении.
Я мог бы написать что-то вроде:
type CSRFHandlerFunc func(w http.ResponseWriter, r *http.Request, t string)
... а затем напишите промежуточное программное обеспечение следующим образом:
func csrfCheck(h CSRFHandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// get the session, check/validate/create the/a token based on HTTP method, etc.
// return HTTP 403 on a failed check
// else invoke the wrapped handler and pass the token h(w, r, token)
}
... но это поднимает несколько вопросов:
- Является ли это разумным способом реализации промежуточного программного обеспечения для каждого обработчика и передачи переменных по запросу?
- Перед тестированием (если у меня нет доступа к моей машине разработчика!), Если мне нужно обернуть обработчик несколькими частями промежуточного программного обеспечения, я предполагаю, что могу просто
r.HandleFunc("/path/preview/", checkCSRF(checkExisting(previewHandler)))
? Проблема, которую я вижу здесь, заключается в том, что промежуточное программное обеспечение теперь тесно связано: обернутое промежуточное программное обеспечение теперь должно получать и затем передавать переменную из внешнего промежуточного программного обеспечения. Это делает расширение http.HandlerFunc более сложным и запутанным. - Подойдет ли здесь горилла / контекст и позволит ли я избежать написания 2-3 пользовательских типов обработчиков (или универсальных типов обработчиков) - и если да, то как бы я их использовал? Или я мог бы реализовать свою собственную "контекстную" карту (и столкнуться с проблемами с одновременным доступом?).
Там, где это возможно, я стараюсь не попасть в ловушку "не попасться при написании библиотеки", но промежуточное ПО - это то, что я, скорее всего, добавлю / дополню позже в жизни проекта, и я бы хотел " поймите правильно "в первый раз.
Некоторые рекомендации по этому вопросу будут высоко оценены. Go отлично подходит для написания веб-приложений, но на данном этапе его жизни не так много примеров, и поэтому я немного опираюсь на SO.
1 ответ
Если я правильно понял ваш вопрос, вы ищете удобный способ передачи дополнительных параметров в ваше промежуточное ПО, верно?
Теперь важно определить, что это за параметры. Это могут быть некоторые значения конфигурации для вашего промежуточного программного обеспечения - они могут быть установлены при создании типа Handler). Вместо NewMyMiddleware(MyHandler)
, ты сделаешь NewMyMiddleware(MyHandler, "parameter")
Нет проблем здесь.
Но в вашем случае кажется, что вы хотите передать параметры для каждого запроса, например, токен CSRF. Передача их в функцию-обработчик изменит ее сигнатуру и будет отклоняться от стандартной Handler[Func]
интерфейс. Вы правы в том, что промежуточное ПО в этом случае более тесно связано.
Вы как бы сами упомянули решение - контекстная карта, на мой взгляд, является жизнеспособным инструментом для этого. Это не так сложно написать самому - вам в основном нужен map[*http.Request]interface{}
и RWMutex
для безопасного одновременного доступа. Тем не менее, просто используя gorilla/context
должно хватить - это похоже на (относительно) зрелый, хорошо написанный пакет с хорошим API.
Бесстыдный плагин: если вы имеете дело с проверками CSRF, почему бы не попробовать мой пакет nosurf?