19 How to Understanding HTTP Middleware in Golang
Middleware
In web creation, we often hear the concept of middleware or filter or interceptor which is a feature that we can add code to before and after a handler is executed.
sequenceDiagram
actor Client
participant Server
participant Middleware
participant Handler
Client->>Server: 1 Event
Server->>Middleware: 2 Dispatch
Middleware->>Handler: 3 Forward
Handler->>Middleware: 4 Return
Middleware->>Server: 5 Return
Server->>Client: 6 Response
Unfortunately, in Golang Web there is no middleware available, but because the handler structure uses a good interface, we can create our own middleware using the handler.
Example of Middleware Implementation
OK, we will try to create a log middleware in the project that we created previously. Create the log_middleware.go file first then fill the file with the code below.
1type LogMiddleware struct {
2 Handler http.Handler
3}
4
5func (middleware *LogMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
6 fmt.Println("Before Execute Handler")
7 middleware.Handler.ServeHTTP(w, r)
8 fmt.Println("After Execute Handler")
9}Then initialize the middleware before the HTTP server is run in the main.go file as below.
1...
2...
3logMiddleware := new(LogMiddleware)
4logMiddleware.Handler = mux
5
6server := http.Server{
7 Addr: "localhost:8080",
8 Handler: logMiddleware,
9}After that, do build and rerun the program that has added middleware. So when the program accesses a page, it should print a log on the terminal like the one below.
1➜ learn-golang-web git:(main) ✗ go build && ./learn-golang-web
2Before Execute Handler
3After Execute Handler
4Before Execute Handler
5After Execute HandlerError Handler
We can also use middleware to handle errors so that if a panic occurs in the handler we can recover in the middleware and change the panic into an error response. So, with this we can ensure that our application does not stop and continues to run as before.
Suppose we create a panic handler function as below
1mux.HandleFunc("/panic", func(w http.ResponseWriter, r *http.Request) {
2 panic("upps")
3})And the program will stop and there will be a ‘panic’ in our program.
1➜ learn-golang-web git:(main) ✗ go build && ./learn-golang-web
2Before Execute Handler
32023/09/30 15:28:34 http: panic serving 127.0.0.1:55818: upps
4goroutine 38 [running]:
5net/http.(*conn).serve.func1(0x140001cc000)
6 /opt/homebrew/Cellar/go/1.17.5/libexec/src/net/http/server.go:1802 +0xdc
7panic({0x10265bee0, 0x1026b5768})
8 /opt/homebrew/Cellar/go/1.17.5/libexec/src/runtime/panic.go:1052 +0x2ac
9main.main.func4({0x1026be490, 0x14000170000}, 0x14000156100)
10 /Users/ihsanarif/Documents/ihsan/tutorial/learn-golang-web/main.go:66 +0x38
11net/http.HandlerFunc.ServeHTTP(0x1026b4760, {0x1026be490, 0x14000170000}, 0x14000156100)
12 /opt/homebrew/Cellar/go/1.17.5/libexec/src/net/http/server.go:2047 +0x40
13net/http.(*ServeMux).ServeHTTP(0x14000194300, {0x1026be490, 0x14000170000}, 0x14000156100)
14 /opt/homebrew/Cellar/go/1.17.5/libexec/src/net/http/server.go:2425 +0x18c
15main.(*LogMiddleware).ServeHTTP(0x1400018e180, {0x1026be490, 0x14000170000}, 0x14000156100)
16 /Users/ihsanarif/Documents/ihsan/tutorial/learn-golang-web/log_middleware.go:14 +0x9c
17net/http.serverHandler.ServeHTTP({0x140001c8000}, {0x1026be490, 0x14000170000}, 0x14000156100)
18 /opt/homebrew/Cellar/go/1.17.5/libexec/src/net/http/server.go:2879 +0x444
19net/http.(*conn).serve(0x140001cc000, {0x1026bf6a0, 0x1400018a960})
20 /opt/homebrew/Cellar/go/1.17.5/libexec/src/net/http/server.go:1930 +0xb6c
21created by net/http.(*Server).Serve
22 /opt/homebrew/Cellar/go/1.17.5/libexec/src/net/http/server.go:3034 +0x4b8How will you be able to handle panic handlers and error handlers using Middleware? Below we will try to create an error handler middleware. First, we create the file error_middleware.go then fill in the file with the following.
1func (middleware *ErrorMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
2 defer func() {
3 err := recover()
4 fmt.Println("recover :", err)
5 if err != nil {
6 w.WriteHeader(http.StatusInternalServerError)
7 fmt.Fprintf(w, "Error: %v", err)
8 }
9 }()
10 middleware.Handler.ServeHTTP(w, r)
11}Then update the middleware that we added to the main.go file to look like the one below.
1logMiddleware := new(LogMiddleware)
2logMiddleware.Handler = mux
3
4errMiddleware := &ErrorMiddleware{
5 Handler: logMiddleware,
6}
7
8server := http.Server{
9 Addr: "localhost:8080",
10 Handler: errMiddleware,
11}Finally, we build and rerun the program then try to access the browser page with the URL
1http://localhost:8080/panicThen it will appear on the accessed page as below

And in our program there will be no ‘panic’ that causes our program to stop.
1➜ learn-golang-web git:(main) ✗ go build && ./learn-golang-web
2Before Execute Handler
3recover : upps
4Before Execute Handler
5After Execute Handler
6recover : <nil>