How does middleware work in chi routing in Go and what does http.Handler argument refers to in middleware?

huangapple go评论81阅读模式
英文:

How does middleware work in chi routing in Go and what does http.Handler argument refers to in middleware?

问题

-- routes.go --
package main

import (
	"hotelsystem/pkg/config"
	"hotelsystem/pkg/handlers"
	"net/http"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
)

func routes(app *config.AppConfig) http.Handler {
	mux := chi.NewRouter()
	mux.Use(middleware.Recoverer)
	mux.Use(WriteToConsole)
	mux.Get("/", handlers.Repo.Home)
	mux.Get("/about", handlers.Repo.About)
	return mux

}
-- middleware.go --
package main

import (
	"fmt"
	"net/http"
)

func WriteToConsole(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("Hit the page")
		next.ServeHTTP(w, r)
	})
}
-- main.go --
package main

import (
	"hotelsystem/pkg/config"
	"hotelsystem/pkg/handlers"
	"hotelsystem/pkg/render"
	"log"
	"net/http"
)

const portNumber = ":3000"

func main() {
	var app config.AppConfig
	tc, err := render.CreateTemplateCache()
	if err != nil {
		log.Fatal("无法创建模板缓存", err)
	}
	app.TemplateCache = tc
	app.UseCache = false
	repo := handlers.NewRepo(&app)
	handlers.NewHandlers(repo)
	render.NewTemplate(&app)
	// http.HandleFunc("/", handlers.Repo.Home)
	// http.HandleFunc("/about", handlers.Repo.About)
	// http.ListenAndServe(portNumber, nil)

	srv := &http.Server{
		Addr:    portNumber,
		Handler: routes(&app),
	}
	err = srv.ListenAndServe()
	if err != nil {
		log.Fatal(err)
	}
}

我很难理解中间件。我正在使用 chi 进行路由。我不明白的是 WriteToConsole 函数中的 next http.Handler 参数是指什么?它是指我们的 mux 路由器吗?当我注释掉 WriteToConsole 函数中的 next.ServeHTTP 行时,HTML 就不会被渲染或其他任何操作。有人可以解释一下 next http.Handler 是指什么,以及 next.ServeHTTP 做了什么吗?

英文:
-- routes.go --
package main

import (
	"hotelsystem/pkg/config"
	"hotelsystem/pkg/handlers"
	"net/http"

	"github.com/go-chi/chi/v5"
	"github.com/go-chi/chi/v5/middleware"
)

func routes(app *config.AppConfig) http.Handler {
	mux := chi.NewRouter()
	mux.Use(middleware.Recoverer)
	mux.Use(WriteToConsole)
	mux.Get("/", handlers.Repo.Home)
	mux.Get("/about", handlers.Repo.About)
	return mux

}
-- middleware.go --
package main

import (
	"fmt"
	"net/http"
)

func WriteToConsole(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		fmt.Println("Hit the page")
		next.ServeHTTP(w, r)
	})
}
-- main.go --
package main

import (
	"hotelsystem/pkg/config"
	"hotelsystem/pkg/handlers"
	"hotelsystem/pkg/render"
	"log"
	"net/http"
)

const portNumber = ":3000"

func main() {
	var app config.AppConfig
	tc, err := render.CreateTemplateCache()
	if err != nil {
		log.Fatal("Can't create templatecache", err)
	}
	app.TemplateCache = tc
	app.UseCache = false
	repo := handlers.NewRepo(&app)
	handlers.NewHandlers(repo)
	render.NewTemplate(&app)
	// http.HandleFunc("/", handlers.Repo.Home)
	// http.HandleFunc("/about", handlers.Repo.About)
	// http.ListenAndServe(portNumber, nil)

	srv := &http.Server{
		Addr:    portNumber,
		Handler: routes(&app),
	}
	err = srv.ListenAndServe()
	if err != nil {
		log.Fatal(err)
	}
}

I am having a hard time understanding the middleware.

I am using chi for routing.

What I didn't understand is what does that (next http.Handler) argument in the WriteToConsole refers to?

Does it refer to our mux router?

Also when I comment down the line next.ServeHTTP of function writetoconsole the html is not rendered or anything? can someone explain me what does that next http.Handler refers to and what next.serveHTTP does?

答案1

得分: 8

next是“处理程序链”中的下一个处理程序。

当你这样做:

mux.Use(middleware.Recoverer)
mux.Use(WriteToConsole)
mux.Get("/", handlers.Repo.Home)
mux.Get("/about", handlers.Repo.About)

实际上注册了两个“处理程序链”:

mux.Get("/", middleware.Recoverer(WriteToConsole(handlers.Repo.Home)))
mux.Get("/about", middleware.Recoverer(WriteToConsole(handlers.Repo.About)))

每个中间件函数返回的处理程序必须调用给定的next处理程序,即执行next.ServeHTTP(w, r),如果不调用next,则链条会中断,该链条中的其余处理程序将被忽略。


一个简化的代码示例可以更好地说明链式调用:

type handler func()

// 你的处理程序
func f() { fmt.Println("f") }

// 一个中间件
func g(next handler) handler {
	return func() {
		fmt.Print("g.")
		next()
	}
}

// 另一个中间件
func h(next handler) handler {
	return func() {
		fmt.Print("h.")
		next()
	}
}

有了上述代码,你可以这样做:

func main() {
	h1 := h(g(f))
	h1()

	h2 := g(h(f))
	h2()

	// 你可以链式调用任意数量的这些中间件
	// 以任意顺序。
	h3 := h(g(h(h(h(g(g(h(f))))))))
	h3()
}

https://play.golang.org/p/4NXquYsaljr

英文:

next is the next handler in the "handler chain".

When you do:

mux.Use(middleware.Recoverer)
mux.Use(WriteToConsole)
mux.Get("/", handlers.Repo.Home)
mux.Get("/about", handlers.Repo.About)

You are essentially registering two "handler chains":

mux.Get("/", middleware.Recoverer(WriteToConsole(handlers.Repo.Home)))
mux.Get("/about", middleware.Recoverer(WriteToConsole(handlers.Repo.About)))

Each handler returned by the middleware function has to invoke the next handler given to it, i.e. do next.ServeHTTP(w, r), if it doesn't invoke next then the chain is broken and the rest of the handlers in that chain will be ignored.


A simplified code example may illustrate the chaining better:

type handler func()

// your handler
func f() { fmt.Println("f") }

// one middleware
func g(next handler) handler {
	return func() {
		fmt.Print("g.")
		next()
	}
}

// another middleware
func h(next handler) handler {
	return func() {
		fmt.Print("h.")
		next()
	}
}

With the above you can then do:

func main() {
	h1 := h(g(f))
	h1()

	h2 := g(h(f))
	h2()

	// And you can chain as many of these as you like
	// and in any order you like.
	h3 := h(g(h(h(h(g(g(h(f))))))))
	h3()
}

https://play.golang.org/p/4NXquYsaljr

huangapple
  • 本文由 发表于 2021年8月6日 14:52:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/68677347.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定