How to Write Simple Middleware Using Go net/http Standard Library


Sometimes as web developer, we need to add more functionality to our http handlers, like pre processing or post processing (kind of wrapper) for some or all handlers with additional code that we call as middleware.

In real world, the example of situation when we will need to have simple middleware, maybe if we want to add profiler/timer to measure how long the http request is processed in the handler.

Here I will give example how we can have  simple middleware only utilize go standard libraries, especially http.Handler.

Let see the code below:


package main

import (
"fmt"
"net/http"
"time"
)

func profilerMiddleware(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
begin := time.Now().UnixNano()
next.ServeHTTP(w, r)
end := time.Now().UnixNano()
ns := end - begin
fmt.Printf("Request is processed in %d nanoseconds\n", ns)
}
return http.HandlerFunc(fn)
}

func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello World")
}

func main() {
helloHandler := http.HandlerFunc(hello)
http.Handle("/", profilerMiddleware(helloHandler))
http.ListenAndServe(":8080", nil)
}

The code above has simple middleware that count how long request is processed. And the middleware is represented as simple function named profilerMiddleware. This function returns http.Handler and accept http.Handler as argument. Inside this handler we put the function that can be converted to Handler using http.HandlerFunc in variable fn.

Then in the middle of this function we call the ServeHttp from http.Handler that is passed as argument (the next handler) so next handler can be processed.

Before and after we call next.serveHttp we can add code to do pre(before) and post(after) processing code, see code below:

//pre processing code
next.ServeHTTP(w,r)
//post processing code

So the principle is, we make profilerMiddleware as the handler which does pre process and post process and pass the next handler as argument to the handler which acts as middleware.

Finally we call the final Handler which is helloHandler in the code above as argument to profilerMiddleware. On the code above helloHandler is http.Handler that we get from passing the function to http.HandlerFunc.

Note: Remember that we need to convert the function hello in the code as http.Handler first before we can pass it into http.Handle. 

See the code below:

helloHandler := http.HandlerFunc(hello)
http.Handle("/", profilerMiddleware(helloHandler))

The output of code above will show to console log how long the request is processed in helloHandler in nanoseconds.

So we can call profilerMiddleware to each handler that we want to measure the processing time.

What i wrote here only covers basic middleware concept in go using http.Handler so we may have need better code or use 3rd party library to make it better or more efficient.