Chaining middleware in Go using justinas/alice package

We can make a couple of improvements to the simple API we created that included a couple of middleware wrapping functions.
  • We're going to split the project into separate files.
  • We're going to use a 3rd party package to chain the middleware functions using a popular community package called alice.
  • Let's go
  • create a new project and run go mid init <name-of-your-project>
  • Run go get github.com/justinas/alice on the cli.
  • Create main.go, handlers.go & middlewares.go files.
  • add our handler
    In the handlers.go file we want to place the following code.
    package main
    
    import (
        "encoding/json"
        "fmt"
        "net/http"
    )
    
    type Lesson struct {
        Title   string
        Summary string
    }
    
    func handle(w http.ResponseWriter, r *http.Request) {
        if r.Method == http.MethodPost {
    
            // create a variable of our defined struct type Lesson
            var tempLesson Lesson
    
            decoder := json.NewDecoder(r.Body)
            err := decoder.Decode(&tempLesson)
            if err != nil {
                panic(err)
            }
    
            defer r.Body.Close()
    
            fmt.Printf("Title: %s. Summary: %s\n", tempLesson.Title, tempLesson.Summary)
            w.WriteHeader(http.StatusCreated)
            w.Write([]byte("201 - Created"))
    
        } else {
            w.WriteHeader(http.StatusMethodNotAllowed)
            w.Write([]byte("405 - Method Not Allowed"))
        }
    }
    Add the middleware functions
    In the middlewares.go file we can add the following code.
    package main
    
    import (
        "log"
        "net/http"
        "strconv"
        "time"
    )
    
    func filterContentType(handler http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            log.Println("Inside filterContentType middleware, before the request is handled")
    
            // filter requests by mime type
            if r.Header.Get("Content-Type") != "application/json" {
                w.WriteHeader(http.StatusUnsupportedMediaType)
                w.Write([]byte("415 - Unsupported Media Type. JSON type expected"))
                return
            }
    
            // handle the request
            handler.ServeHTTP(w, r)
    
            log.Println("Inside filterContentType middleware, after the request was handled")
        })
    }
    
    func setServerTimeCookie(handler http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            log.Println("Inside setServerTimeCookie middleware, before the request is handled")
    
            // handles the request
            handler.ServeHTTP(w, r)
    
            cookie := http.Cookie{Name: "Server Time(UTC)", Value: strconv.FormatInt(time.Now().Unix(), 10)}
            http.SetCookie(w, &cookie)
    
            log.Println("Inside setServerTimeCookie middleware, after the request is handled")
        })
    }
    add the main function
    in main.go we need to add the following
    package main
    
    // chaing middleware with alice.
    // here we will use the alice package to demo chainging middleware
    // around our original or main handler.
    
    import (
        "log"
        "net/http"
    
        "github.com/justinas/alice"
    )
    
    func main() {
    
        port := ":8080"
    
        originalHandler := http.HandlerFunc(handle)
        chain := alice.New(filterContentType, setServerTimeCookie).Then(originalHandler)
    
        http.Handle("/lesson", chain)
        log.Fatal(http.ListenAndServe(port, nil))
    }
    As you can see our refactor has added a 3rd party dependency but increased readbility. The fluent API of the alice package makes for clear and readable code when used.
    Splitting the project into multiple files here is trivial as we have a tiny codebase and it is purely for demo purposes, however it is reasonably good, common practice to split your files in Go projects. This lowers the cognitive load from each file on the reader.

    46

    This website collects cookies to deliver better user experience

    Chaining middleware in Go using justinas/alice package