DEV Community

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

Posted on

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

Top comments (0)