Milan Poudel
Milan Poudel

Reputation: 792

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("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?

Upvotes: 5

Views: 8780

Answers (1)

mkopriva
mkopriva

Reputation: 38233

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

Upvotes: 11

Related Questions