Написание промежуточного программного обеспечения для каждого обработчика

Я пытаюсь извлечь некоторую повторяющуюся логику из моих обработчиков и поместить ее в некоторое промежуточное ПО для каждого обработчика: в частности, такие как проверки 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?

Другие вопросы по тегам