Я думаю, вы могли бы смешивать «промежуточное ПО» с реальными обработчиками.
http-обработчики
Типы, которые реализуют метод ServeHTTP(w http.ResponseWriter, r *http.Request)
, удовлетворяют http.Handler
интерфейс и, следовательно, экземпляры этих типов могут, например, использоваться в качестве второго аргумента функции http.Handle
или эквивалентного метода http.ServeMux.Handle
.
Пример может прояснить это:
type myHandler struct {
// ...
}
func (h myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`hello world`))
}
func main() {
http.Handle("/", myHandler{})
http.ListenAndServe(":8080", nil)
}
Функции обработчика http
Функции с подписью func(w http.ResponseWriter, r *http.Request)
- это функции обработчика http, которые можно преобразовать в http.Handler
с помощьютип http.HandlerFunc
.Обратите внимание, что подпись совпадает с подписью метода http.Handler
ServeHTTP
.
Например:
func myHandlerFunc(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`hello world`))
}
func main() {
http.Handle("/", http.HandlerFunc(myHandlerFunc))
http.ListenAndServe(":8080", nil)
}
Выражение http.HandlerFunc(myHandlerFunc)
преобразует myHandlerFunc
функция типа http.HandlerFunc
, которая реализует метод ServeHTTP
, поэтому результирующее значение этого выражения является действительным http.Handler
и поэтому может быть передано в вызов функции http.Handle("/", ...)
в качестве второго аргумента.
Использование простых функций обработчика http вместо типов обработчиков http, которые реализуют метод ServeHTTP
, достаточно распространено, и стандартная библиотека предоставляет альтернативы http.HandleFunc
и http.ServeMux.HandleFunc
.Все, что делает HandleFunc
, - это то, что мы делаем в приведенном выше примере: он преобразует переданную функцию в http.HandlerFunc
и вызывает http.Handle
с результатом.
http middleware
Функции с подписью, аналогичной этой func(h http.Handler) http.Handler
, считаются промежуточным программным обеспечением.Имейте в виду, что сигнатура промежуточного программного обеспечения не ограничена, у вас может быть промежуточное программное обеспечение, которое принимает больше аргументов, чем просто один обработчик, и также возвращает больше значений, но в целом функция, которая принимает по крайней мере один обработчик и перезапускает по крайней мере одинНовый обработчик можно рассматривать как промежуточное программное обеспечение.
В качестве примера рассмотрим http.StripPrefix
.
Давайте теперь проясним некоторые очевидные недоразумения.
# 1
func (s *Server) HandleSayHello(h http.Handler) http.Handler {
Название метода и способ, которым вы использовали его ранее, передавая его непосредственно HandleFunc
, предполагают, что вы хотите, чтобы это был обычный функционал http-обработчика, ноподпись - это промежуточное ПО, и это причина ошибки, которую вы получили:
cannot use s.HandleSayHello (type func(http.Handler) http.Handler) as type func(http.ResponseWriter, *http.Request) in argument to s.Router.HandleFunc
Таким образом, обновление вашего кода до чего-то вроде приведенного ниже кода избавит от этой ошибки компиляции и также правильно отобразит "Hello."
текст при посещении /sayhello
.
func (s *Server) HandleSayHello(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello."))
}
func (s *Server) Routes(){
s.Router.HandleFunc("/sayhello", s.HandleSayHello)
}
# 2
Так как сейчас, я верну только вызов 404 localhost:8091/sayhello
.
Проблема в этих двух строках
http.Handle("/sayhello", s.HandleSayHello(s.Router))
и
http.ListenAndServe(":"+config.ServerPort, s.Router)
Функция http.Handle
регистрирует переданный обработчик с экземпляром ServeMux по умолчанию , он не регистрирует его в экземпляре маршрутизатора gorilla в s.Router
, как вы, вероятно, предполагаете, и затем вы передаете s.Router
в функцию ListenAndServe
, которая использует его для обслуживания каждого запроса, поступающего в localhost:8091
, и с тех порУ s.Router
нет зарегистрированного обработчика, вы получаете 404
.
# 3
Но теперь, как мне предотвратить выполнение фактической функции при установкемои маршруты?"before"
в моем операторе печати появляется перед запуском сервера.
func (s *Server) Routes(){
http.Handle("/sayhello", s.HandleSayHello(s.Router))
}
func (s *Server) HandleSayHello(h http.Handler) http.Handler {
log.Println("before")
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
w.Write([]byte("Hello."))
h.ServeHTTP(w, r)
})
}
Зависит от того, что вы подразумеваете под «фактической функцией».В Go вы выполняете функции, добавляя круглые скобки в конце их имени.То, что выполняется здесь, когда вы устанавливаете маршруты, это функция http.Handle
и метод HandleSayHello
.
Метод HandleSayHello
имеет по существу два оператора в своем теле, функцию-выражение-вызовоператор log.Println("before")
и оператор возврата return http.HandlerFunc(...
, и оба они будут выполняться каждый раз, когда вы вызываете HandleSayHello
.Однако операторы внутри возвращенной функции-обработчика не будут выполняться при вызове HandleSayHello
, вместо этого они будут выполняться при вызове возвращенного обработчика.
Вы не хотите, чтобы "before"
былнапечатано, когда вызывается HandleSayHello
, но вы хотите, чтобы оно печаталось при вызове возвращаемого обработчика?Все, что вам нужно сделать, это переместить строку журнала вниз к возвращаемому обработчику:
func (s *Server) HandleSayHello(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
log.Println("before")
w.Write([]byte("Hello."))
h.ServeHTTP(w, r)
})
}
Этот код, конечно, теперь не имеет большого смысла, даже в качестве примера для образовательных целей он будет сбивать с толку, а не разъяснять концепцию обработчиков и промежуточного программного обеспечения.
Вместо этого, возможно, рассмотрим что-то вроде этого:
// the handler func
func (s *Server) HandleSayHello(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello."))
}
// the middleware
func (s *Server) PrintBefore(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
log.Println("before") // execute before the actual handler
h.ServeHTTP(w, r) // execute the actual handler
})
}
func (s *Server) Routes(){
// PrintBefore takes an http.Handler but HandleSayHello is an http handler func so
// we first need to convert it to an http.Hanlder using the http.HandlerFunc type.
s.Router.HandleFunc("/sayhello", s.PrintBefore(http.HandlerFunc(s.HandleSayHello)))
}
# 4
r := mux.NewRouter()
r.HandleFunc("/", handler)
r.Use(loggingMiddleware)
Какова цель r.Use
, когда он не регистрирует URL-маршрут?Как используется handler
?
Use
регистрирует промежуточное программное обеспечение на уровне маршрутизатора, что означает, что всем обработчикам, зарегистрированным на этом маршрутизаторе, будет выполнено промежуточное программное обеспечение, прежде чем они сами будут выполнены.
Например, приведенный выше код эквивалентен следующему:
r := mux.NewRouter()
r.HandleFunc("/", loggingMiddleware(handler))
Конечно, Use
не является ненужным и запутанным, это полезно, если у вас много конечных точек с разными обработчиками и всемииз них требуется связка промежуточного программного обеспечения.
Затем код, подобный этому:
r.Handle("/foo", mw1(mw2(mw3(foohandler))))
r.Handle("/bar", mw1(mw2(mw3(barhandler))))
r.Handle("/baz", mw1(mw2(mw3(bazhandler))))
// ... hundreds more
Может быть радикально упрощен:
r.Handle("/foo", foohandler)
r.Handle("/bar", barhandler)
r.Handle("/baz", bazhandler)
// ... hundreds more
r.Use(mw1, mw2, m3)