Skip to content
Santekno.com | Level Up Your Engineering Skills
EN
📖 0%
11 Sep 2023 · 3 min read ·Article 49 / 119
Go

19 How to Understanding HTTP Middleware in Golang

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

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.

MERMAID
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.

go
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.

go
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.

bash
1➜  learn-golang-web git:(main) ✗ go build && ./learn-golang-web
2Before Execute Handler
3After Execute Handler
4Before Execute Handler
5After Execute Handler

Error 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

go
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.

bash
 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 +0x4b8

How 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.

go
 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.

go
 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

bash
1http://localhost:8080/panic

Then it will appear on the accessed page as below

tutorial golang web panic error middleware

And in our program there will be no ‘panic’ that causes our program to stop.

bash
1➜  learn-golang-web git:(main) ✗ go build && ./learn-golang-web
2Before Execute Handler
3recover : upps
4Before Execute Handler
5After Execute Handler
6recover : <nil>

Related Articles

💬 Comments