LINUX.ORG.RU

Golang HTTP Middleware

 , ,


0

3

Пошаговая инструкция понимания Golang HTTP Middleware.

Вариант №1:

  1. Надо представить сервер как процесс с STDIN/STDOUT.
  2. Надо понять что STDIN/STDOUT обслуживаются объектами созданными по лекалу интерфейсов. И названными r/w. Ну и порядок перевернут w,r в сигнатуре.
  3. Вот ту ключевое - обрабатывается ОДНОЙ ФУНКЦИЕЙ которую вызывают. ТОЧКА.
  4. А дальше расширить понимание, что Эту функцию можно вызывать каскадом.
  5. И последнее уже переходное, то MiddleWare КОНСТРУИРУЕТ функцию. По этому каждый этап MiddleWare Возвращает валидный ServerHTTP! Который либо Пишет сам. Либо завет чужой хендлер по замыканию.

Вариант №2:

  1. Представить HTTP сервер как UNIX Процесс с STDIN/STDOUT.
  2. Понять что STDIN и STDOUT обрабатываются через инстансы созданные по «чертежам интерфейсов». STDIN - Resquest, STDOUT - Response. (STDIN=Request=Read):(STDOUT=Response=Write).
  3. Запрос обрабатывается одной функцией. Точка.
  4. Расширить понимание, функцию можно вызывать каскадом.
  5. Middleware не функция для вызова, это функция которая ВОЗВРАЩАЕТ функцию для Вызова.

🎆 Отливаем в бронзе, лаконичная формула от Qwen:

Middleware в Go — это не хук. Это фабрика функций.

Она принимает Handler, возвращает Handler, а внутри — через замыкание — решает, когда и как вызвать следующий.

🎆 От ChatGPT:

Middleware в Go — это не механизм перехвата, а функция высшего порядка, которая принимает handler и возвращает новый handler, формируя цепочку через замыкания и управляя моментом вызова следующего обработчика.

🎆 От DeepSeek:

Middleware в Go — это не цепочка вызовов. Это фабрика, которая конструирует один метод ServeHTTP как матрёшку из замыканий, где каждый слой хранит ссылку на следующий в своей области видимости.

🎆 От Grok:

Middleware в Go — это фабрика функций. Она принимает http.Handler, возвращает http.Handler, а внутри через замыкание решает, когда и как вызвать следующий (или прервать цепочку). Это не «добавление хука». Это оборачивание одного обработчика в другой, создающее конвейер (pipeline).

📀 P.S.

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

type HandlerFunc func(http.ResponseWriter, *http.Request)

func (f HandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    f(w, r)
}

По этому функцию можно приводить к типу этого интефейса Hanlder. Изюминка заключается в том, что http.HandlerFunc(hello) - это не вызов метода. Это приведение к типу (type conversion) HandlerFunc.

🎆 Полировка от DeepSeek:

Благодаря http.HandlerFunc любую функцию с сигнатурой (ResponseWriter, *Request) можно привести к типу http.Handler, что делает оборачивание элегантным и типобезопасным.

🎆 Полировка от Qwen:

http.HandlerFunc(f) — это статический адаптер, который превращает функцию в объект, реализующий интерфейс, через навешивание метода на тип-функцию.

🎆 Полировка от ChatGPT:

В выражении http.HandlerFunc(hello) происходит приведение обычной функции к функциональному типу HandlerFunc, что позволяет этому значению получить метод ServeHTTP и тем самым реализовать интерфейс http.Handler.

🎆 Полировка от Grok:

http.HandlerFunc — статический адаптер, который позволяет любой функции с правильной сигнатурой стать http.Handler и участвовать в этой цепочке.

Middleware в Go — это фабрика функций.

Она принимает http.Handler, возвращает http.Handler, а внутри через замыкание конструирует новый ServeHTTP, который решает, когда вызвать следующий слой.

Это не «добавление хука», а оборачивание одного обработчика в другой, создающее гибкий конвейер (pipeline).

📀📀 P.P.S.

Что еще важно, так как в итоге ServeHTTP метод который вызывается для обработки запроса представляет собой наслоение замыканий «кольцами», то из любого кольца можно принят решение о немедленном выходе, ни погружаясь дальше. Вот так вот изящно формируется в Golang единственная функция обработчик, которая в себе имеет сколь хватит памяти уровней, и к каждый уровень это Middleware или отдельный метод. Метод либо прерывающий цепочку и отвечающий в http.Response, или вызывающий следующий метод, переходя в новый слой.

🎆 Полировка от Qwen:

Middleware в Go — это управляемый конвейер с возможностью аварийной остановки. Каждый слой обладает полным правом вето: он может обработать запрос самостоятельно и завершить цепочку, не беспокоя следующие уровни.

🎆 Полировка от DeepSeek:

В итоге формируется единая функция-обработчик, которая в себе содержит сколько угодно уровней логики, а каждый уровень — это либо точка остановки, либо переход на следующий уровень.

🎆 Полировка от ChatGPT:

Итоговый ServeHTTP — это одна функция, собранная как композиция вложенных замыканий (цепочка middleware), где каждый слой принимает решение: вызвать следующий обработчик или завершить обработку немедленно.

В Go HTTP middleware формирует один итоговый обработчик через композицию замыканий. Каждый слой получает управление и решает: вызвать следующий обработчик или завершить обработку, сформировав ответ. Таким образом, вся цепочка — это одна функция с контролируемым потоком выполнения.

🎆 Полировка от Grok:

В результате вся обработка запроса — это одна функция, внутри которой спрятана вся цепочка замыканий, и поток выполнения полностью контролируется на каждом слое. Это один из самых элегантных и мощных паттернов в стандартной библиотеке Go.

🐳 Итоговая формула (собранная из цитат) от DeepSeek:

Middleware в Go — это не цепочка вызовов и не система перехвата событий. Это фабрика, которая конструирует единый метод ServeHTTP как 🪆 матрёшку из замыканий.



Последнее исправление: lbvf50txt (всего исправлений: 11)
Ответ на: комментарий от paddlewan

Напротив, это тема уровня trainee. Потому что все сводится, в сущности, к пониманию замыканий, как вам ИИшечка и подсказала.

Смотри. Ни один критик в этой теме не дал архитектурного разбора механизма. И ты в их числе. Всё что есть это неумелые попытки обесценить проведенную мной работу.

Смысл статьи еще до LLM - это указание анкерных точек для понимания, смещение фокуса с обработчика, на фабрику. Вся критика идет на уровне Junior/newMiddle - цепляние к синтаксису и попытка обесценить, для возвышения.

Вот я вам предлагаю разберите механизм обработки HTTP запроса полней, приведите строение сервера, сущность мультиплексора в пакете net/http. Тогда это будет разговора на уровне Middle+/Senior. Разговор про архитектуру.

Все что я слышу пока в этой теме, это истерики по поводу использования LLM, и попытки обесценить. Да это просто, да это всегда было, да это мелочи. Ну хорошо, мелочи, всегда было.

Ваш выход. Прошу. Давайте продолжим про архитектуру обработки запроса. Примеры рассуждения я многократно продемонстрировал в описании приведение функций к инстансам через type conversion, и так далее по общей архитектуре решения.

lbvf50txt
() автор топика
Последнее исправление: lbvf50txt (всего исправлений: 1)
Ответ на: комментарий от paddlewan

Уважаемый, а зачем этот пафос «подумайте почему так» в одном из базовых примеров использования defer?

Мне не понятно зачем лезть поправлять и обесценивать, ну когда вы явно не тяните. Ну видно же по тексту, что ваш собеседник знает и про defer, и про порядок вызова, и про счётчик команд.

Постоянна попытка диктовать сверху, уличать, унижать. Можно спокойно сказать, еще defer используется как инструмент для восстановления. Нет, надо «подумайте почему так». У-у-у-у-у-у «эксперт педагог» в комнате.

lbvf50txt
() автор топика
Ответ на: комментарий от monk

Кто сказал про сетевые?

Golang создавался под конкретные задачи, конкретной корпорации. Это язык для обработки большого количества HTTP запросов. Это является целью языка.

Так претензии про наличие GC имеют смысл только в контексте реального времени.

На форумах претензии к Golang без контекста. Вот как пример вашего высказывания.

«нормальный программист уже 30 лет может писать»

Стало быть, тот кто не знает Lisp, он уже не «нормальный программист». Оказывается не сложность решенной задачи определяет разработчика, а инструмент.

И оказывается не умение писать в определенных парадигмах определяет качество кода. А инструмент на которых эти парадигмы реализованы.

  • Lisp - респект, «настоящий пацан».
  • JS, Go - «слабак, не настоящий».

Что это такое? Это типичная форумная критика Go.

lbvf50txt
() автор топика
Последнее исправление: lbvf50txt (всего исправлений: 1)
Ответ на: комментарий от lbvf50txt

попытки обесценить проведенную мной работу

«Мной»! Бггг.

На данный момент в треде 54 камента, слово «обесценивание» в разных формах встречается 11 раз.

Пора тебе в ретрит, хватит находиться в токсичной среде.

thesis ★★★★★
()
Ответ на: комментарий от lbvf50txt

Стало быть, тот кто не знает Lisp, он уже не «нормальный программист».

Всегда так было.

ugoday ★★★★★
()
Ответ на: комментарий от monk

Вот я вам просто объясню смысл Go. Весь смысл языка Go, в реализации Goroutine. Обратите внимание на игру Go язык, Go игра, Google, Gorounite.

Что такое Горутины и зачем они нужны?

Весь огород с новым языком создавали для того, чтоб полный цикл Web приложения упаковать в одно адрессное пространство и один процесс-контейнер.

Если раньше обработка HTTP разделялась на нескольоко процессов HTTP сервер, потом запуск интерпретатора, потом CGI и передавание инфы черзе потоки. То в Go - все упокавали в один процесс. Это на минуточку 2009 года, когда Perl еще во всю использовался как и Apache.

Едем дальше. В Golang реализовали свой собственный планировщик. И назвали эту тему так G/P/M. G - горутина, P виртуальный процееор в внутри рантайма Go, М - это поток в Машине, он же Thread.

Что получили, что при IO-bound задачах, когда поток зависает в ожидании чего-то, можно отложить Горутину с подвисшим потоком, и взять на исполнение другу горутину, отклеив P виртуальный процессор от потока.

Другими словами Go решает ту же задачу, которую решали на мейнфреймах в 1960ых годах, когда обратили внимания, что большинство задач ожиают отклика от пользователя, а CPU простаивает. Тогда создали Preemptive Multitasking. Тут то же самое, но в рамках процесса. Пока куча Горутин ждет отклика по сети, другие выполняются.

По этому смысл Go не в синтаксисе, а в механизме планировщика, который снимает с разработчика кучу головников. И позволяет оптимально расходовать CPU время, при IO-bound задачах.

lbvf50txt
() автор топика
Ответ на: комментарий от lbvf50txt

Golang

Извини, слух режет. :)

Is the language called Go or Golang?

The language is called Go. The “golang” moniker arose because the web site was originally golang.org. (There was no .dev domain then.) Many use the golang name, though, and it is handy as a label. For instance, the social media tag for the language is “#golang”. The language’s name is just plain Go, regardless.


https://go.dev/doc/faq#go_or_golang

urxvt ★★★★★
()
Ответ на: комментарий от lbvf50txt

Вот я вам предлагаю разберите механизм обработки HTTP запроса полней

А что там разбирать? Открой (раз уж перешел на «ты») код стандартной библиотеки и почитай просто, никто не прячет же.

Насчет «обесценивания». Ну конечно, ты придумал проблему, которой не существует, титаническими усилиями решил ее и теперь обижаешься, что никто не оценил. Да, никто не оценил, т.к. проблемы не было, а было твое частное упражнение по изучению стандартной библиотеки, которое, внезапно, оказалось мало кому интересно, о чем в первых же двух ответах тебе сообщили.

paddlewan
()
Ответ на: комментарий от lbvf50txt

Уважаемый, а зачем этот пафос «подумайте почему так» в одном из базовых примеров использования defer?

Потому что ты сказал выше, что defer - это чтоб ресурсы высвобождать. Это неправильно. Я процитировал ошибочный тезис и предложил подумать, почему он неверен, и даже пример привел.

У-у-у-у-у-у «эксперт педагог» в комнате.

Бро, я на прошлой неделе провел n собеседований на ведущего разработчика на Go. Да, кой-чего смыслю в языке. И да, бываю даже педагогом для менее опытных коллег. Ты читай, что тебе по существу пишут, не ищи кругом врагов, они сами тебя найдут :)

paddlewan
()
Последнее исправление: paddlewan (всего исправлений: 1)
Ответ на: комментарий от paddlewan

Бро, я на прошлой неделе провел 10 собеседований на ведущего разработчика на Go. Да, кой-чего смыслю в языке.

Слушай, Вася, не надо трясти своими медальками. Сколько лет ты обработал и кого и как ты собеседвал и какую вывеску вы себе повесели на ваш отдел.

Ты пришел «блатовать» тебе предложили побеседовать о Архитектуре net/http в Go. После чего ты технично слился со словами: «Иди читай код» и начал опять обесценивать чужой труд и чужие задачи.

lbvf50txt
() автор топика
Последнее исправление: lbvf50txt (всего исправлений: 1)
Ответ на: комментарий от paddlewan

А что там разбирать? Открой (раз уж перешел на «ты») код стандартной библиотеки и почитай просто, никто не прячет же.

Понятно.

lbvf50txt
() автор топика
Ответ на: комментарий от lbvf50txt

Ох, нет, бро, с тобой мы беседовать больше ни о чем не будем, потому что ты хамишь :) :) :)

Давай дальше развлекайся сам с собой тут :)

paddlewan
()
Ответ на: комментарий от kaldeon

Но я же правильно понимаю, что серебряной пули не существует и ручная статика имеет преимущества в определённых областях применения?

Ручная статика всегда лучше там, где она применима. Поэтому в Common Lisp есть declare type, а в Racket есть Typed Racket.

Но применима она не везде, поэтому в C есть *void, а в Go есть interface{}.

monk ★★★★★
()
Ответ на: комментарий от lbvf50txt

По этому смысл Go не в синтаксисе, а в механизме планировщика, который снимает с разработчика кучу головников. И позволяет оптимально расходовать CPU время, при IO-bound задачах.

Erlang придумали раньше. И даже NodeJS в том же 2009. И в Racket зелёные потоки примерно в том же году были.

Смысл Go именно в том, что он был удобен для Google. И кастрировали его из тех же соображений, что и Java. Чтобы разброс времени решения задачи у разных программистов был не 50 раз как на C++, а 3-4 раза.

monk ★★★★★
()
Ответ на: комментарий от lbvf50txt

Стало быть, тот кто не знает Lisp, он уже не «нормальный программист». Оказывается не сложность решенной задачи определяет разработчика, а инструмент.

Не инструмент, а возможности предоставляемые инструментом. Ты пишешь «а эта «копипаста», это как раз упрощение до максимума», я отвечаю, что уже давно есть нормальные инструменты, позволяющие действительно упростить написание, чтобы не копипастить бессмысленно текст.

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

monk ★★★★★
()
Ответ на: комментарий от lbvf50txt

Стаж не тождественен классу. Зачем вы трясете этой медалькой?

Ну ты же бесконтактным вин-чунем зачем-то тряс в теме про TCP.

slackwarrior ★★★★★
()
Ответ на: комментарий от urxvt

Поддержу. Я последние годы особо за Го не слежу, но, судя по всему, изначальное ядро авторов свалило и язык начал скатываться в сторону от их философии.

В Go занимаются оптимизацией Scheduler и GC. Когда начинаешь читать про эти вопросы, то бесконечные минорные версии Go начинают иметь свой смысл. Точно я не помню, но что запомнил ключевые - механизмы меняют серьезно. А так как фактический смысл Go - это оптимизация распихивания Горутин по «Ядрам», то это самые важные обновления.

Потом, честно говоря, меньше меньше всего волнует какое новое синтаксическое ухищрение будет прикручено к языку. Пока есть обратная совместимость меня это не колышит.

Синтаксис в принципе сильно вторичен. Даже это Middleware решение - не особо нужно, можно не заворачиваясь слепить один большой объект с одним методом и отдать его мультиплексору. Работать будет.

Ну так да, красиво, мне формат «Композиции через Замыкание» понравился как идея. Но опять же, такие изыски абсолютно не критичны. Если выстроить по порядку набор методов с if/else - ничего не изменится.

P.S.

Всё изящество Архитектуры Go в том, что он не навязывает какой-то сверх изысканных синтаксис. Хочешь писать просто - пиши. Есть один Объект с методом в котором STDOUT/STDIN. И всё: тут прочитал из Request, туда байты залил в Response. Заработало. А дальше твое дело.

При желании можно «жонглировать» и «джигитовать», запускать замыкания в defer меняя named return. Честно говоря, я этого насмотрелся в RubyOnRails, развернулся и ушел в Go. Этап джигитовки и code golf я прошел с Ruby, вот там верх изящества всё возможные способы деления массива, удельный вес строки кода огромный. А в RubyOnRails код не возможно читать без отладчика и буквально не работают vim tags - есть масса вхождений определений, переопределий, дозаливок-замешниваний.

Исходный код RubyOnRails перестал быть книгой, он стал именно кодом для запуска, надо иметь специальную программу, чтоб она контекст подсвечивала и стек наследований.

По этому меня синтаксическая эквилибристика не впечатляет, на оборот, она у меня вызывает приступ скуки. Наигрался. Ну и потом, разбирать даже свои собственные шедевры занятие то еще. По этому, в итоге, свободу Ruby ограничивают добавляя линтеры с жесткими правилами, например, требуется вместо reduce писать with_object.

lbvf50txt
() автор топика
Последнее исправление: lbvf50txt (всего исправлений: 8)
Ответ на: комментарий от lbvf50txt

Всё изящество Архитектуры Go в том, что он не навязывает какой-то сверх изысканных синтаксис.

Это путь Java. При наличии нормального IDE, который генерирует и рефакторит весь этот текстовый шум, даже нормально. Без IDE что на Java что на Go приходится писать слишком много букв. COBOL наших дней.

monk ★★★★★
()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.