user3125693
user3125693

Reputation: 950

Setting callback with Golang Gorilla package

Is there an easy way to set a callback function that is called on every HTTP request made to a gorilla/mux HTTP webserver? They don't seem to have a notion of setting a callback function in their docs.

I wanted to check one of the headers for some information on every call to my server.

Upvotes: 0

Views: 1140

Answers (2)

squiguy
squiguy

Reputation: 33370

If you want to have a handler that can receive all requests in the router you can use alice to chain your handlers together.

This is a simple way to act on all incoming requests.

Here is an example:

package main

import (
    "net/http"

    "github.com/gorilla/mux"
    "github.com/justinas/alice"
)

func MyMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if len(r.URL.Path) > 1 {
            http.Error(w, "please only call index because this is a lame example", http.StatusNotFound)
            return
        }
        next.ServeHTTP(w, r)
    })
}

func Handler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello"))
}

func main() {
    r := mux.NewRouter()

    r.HandleFunc("/", Handler)
    r.HandleFunc("/fail", Handler)

    chain := alice.New(MyMiddleware).Then(r)

    http.ListenAndServe(":8080", chain)
}

Upvotes: 2

John Weldon
John Weldon

Reputation: 40759

gorilla/mux itself is just a router and dispatcher, which, although technically could do what you're asking, is not really what it's designed for.

The "idiomatic" approach is to wrap your handler with middleware that decorates your handler with any additional functions, like one to check headers for some information on every call.

Expanding on the gorilla/mux full example, you could do something like:

package main

import (
        "log"
        "net/http"

        "github.com/gorilla/mux"
)

func HeaderCheckWrapper(requiredHeader string, original func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
        return func(w http.ResponseWriter, r *http.Request) {
                if foo, ok := r.Header[requiredHeader]; ok {
                        // do whatever with the header value, and then call the original:
                        log.Printf("Found header %q, with value %q", requiredHeader, foo)
                        original(w, r)
                        return
                }
                // otherwise reject the request
                w.WriteHeader(http.StatusPreconditionFailed)
                w.Write([]byte("missing expected header " + requiredHeader))
        }
}

func YourHandler(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Gorilla!\n"))
}

func main() {
        r := mux.NewRouter()
        // Routes consist of a path and a handler function.
        r.HandleFunc("/", HeaderCheckWrapper("X-Foo-Header", YourHandler))

        // Bind to a port and pass our router in
        log.Fatal(http.ListenAndServe(":8000", r))
}

Upvotes: 2

Related Questions