DEV Community

Cover image for To the point - http middleware in go with net/http Part - 1
HM
HM

Posted on

6 5

To the point - http middleware in go with net/http Part - 1

What the hell middleware!

If you are programmer you have probably heard one of the two terms - "middleware" or "decorator"
They both achieve the same thing!

A pattern that allows adding new behaviors to objects/functions dynamically by placing them inside special wrapper objects/functions. This allows wrapping countless number of times since both input and output follow the same interface.

Requirement:

Create two middlewares:

  1. logMw: for logging req and response
  2. loggedInMw: for verifying that a user is logged in

Create one handler:

  1. "/me" shows basic user information if not logged in. But shows more info if user is logged in

Thinking:

I should be able to use the middleware like this:

mux.HandleFunc("/", logMw(loggedInMw(userInfo)))
Enter fullscreen mode Exit fullscreen mode

where userInfo is my http.HandlerFunc
and lowMw and loggedInMw are the middlewares

More thinking:

A middleware then is a function that accepts and returns a HandlerFunc, thus enabling chaining

func(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        //middleware logic
        .... // can operate on w and r

        //call next in the end
        next(w, r)
    }
}
Enter fullscreen mode Exit fullscreen mode

Code it!

From now on see my comments inline.

Note: The pattern to add a header called "loggedIn" is not the right thing to do . We will look at how to make it right, but for this example bear with me.

package main

import (
    "fmt"
    "log"
    "math/rand"
    "net/http"
    "time"
)

var mux *http.ServeMux = http.NewServeMux()

func main() {
    mux.HandleFunc("/", lowMw(loggedInMw(userInfo)))
    fmt.Println("server running on 8080")
    log.Fatal(http.ListenAndServe(":8080", mux))

}

// check header "loggedIn"
func userInfo(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte(w.Header().Get("loggedIn")))
}

// log the req and response after the final handler has been called
func lowMw(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        // start middleware logic
        t := time.Now()
        next(w, r)
        fmt.Printf(`[%v] {"r.Method": "%s", "r.URL.Path": "%s", "status": "%v", "timeTakenµs":"%v", "loggedIn": "%s"}
`, t.UTC(), r.Method, r.URL.Path, 200, time.Since(t).Nanoseconds(), w.Header().Get("loggedIn"))
    }
}

// check if user is logged or not and set a header called "loggedIn" for next handlerFunc to read from
func loggedInMw(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        // start middleware logic
        //
        // dummy process of setting loggedIn = true
        rand.Seed(time.Now().UnixNano())
        temp := rand.Intn(2)
        var loggedIn string = "no"
        if temp == 0 {
            loggedIn = "yes"
        } 
        // end of dummy process
        //
        w.Header().Set("loggedIn", loggedIn)
        // end of middleware logic
        next(w, r)
    }
}

Enter fullscreen mode Exit fullscreen mode

Output

Checkout the "loggedIn" in the log below! We did!

[2020-01-14 03:50:39.7792098 +0000 UTC] {"r.Method": "GET", "r.URL.Path": "/", "status": "200", "timeTakenµs":"0", "loggedIn": "yes"}
[2020-01-14 03:50:39.9446553 +0000 UTC] {"r.Method": "GET", "r.URL.Path": "/", "status": "200", "timeTakenµs":"0", "loggedIn": "no"}
[2020-01-14 03:50:40.1101016 +0000 UTC] {"r.Method": "GET", "r.URL.Path": "/", "status": "200", "timeTakenµs":"0", "loggedIn": "yes"}
[2020-01-14 03:50:40.2615089 +0000 UTC] {"r.Method": "GET", "r.URL.Path": "/", "status": "200", "timeTakenµs":"0", "loggedIn": "yes"}
[2020-01-14 03:50:40.4339753 +0000 UTC] {"r.Method": "GET", "r.URL.Path": "/", "status": "200", "timeTakenµs":"0", "loggedIn": "yes"}
[2020-01-14 03:50:40.5863852 +0000 UTC] {"r.Method": "GET", "r.URL.Path": "/", "status": "200", "timeTakenµs":"0", "loggedIn": "yes"}
Enter fullscreen mode Exit fullscreen mode

You may like to read the series:

# To the point - http middleware in go with net/http Part - 1
# To the point - http middleware in go with net/http Part - 2
Enter fullscreen mode Exit fullscreen mode

AWS GenAI LIVE image

Real challenges. Real solutions. Real talk.

From technical discussions to philosophical debates, AWS and AWS Partners examine the impact and evolution of gen AI.

Learn more

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay