Lucas
Lucas

Reputation: 748

How to use gorilla middleware handlers for all requests?

I want to use the handlers specified here for logging everything.

This is what I have:

r := mux.NewRouter()
s := r.PathPrefix("/api/v1").Subrouter()

s.HandleFunc("/abc", handler.GetAbc).Methods("GET")
s.HandleFunc("/xyz", handler.GetXyz).Methods("GET")

I want to use the logging middleware but I don't want to repeat it in every single line, as they show in github:

r.Handle("/admin", handlers.LoggingHandler(os.Stdout, http.HandlerFunc(ShowAdminDashboard)))
r.HandleFunc("/", ShowIndex)

Is there a way to just pass the general logging middleware to r, and everything that passes the r router will pass by the middleware first?

Upvotes: 4

Views: 9537

Answers (3)

Prav
Prav

Reputation: 2894

This is the approach I took and this worked best for me.


type Route struct {
    Name        string
    Method      string
    Pattern     string
    Secure      bool
    HandlerFunc http.HandlerFunc
}

type Routes []Route

var routes = Routes{
    Route{
        Name:        "Docs",
        Method:      "GET",
        Pattern:     "/v2/docs",
        HandlerFunc: Docs,
    },

    Route{
        Name:        "GetUserByName",
        Method:      "GET",
        Pattern:     "/v2/user/{username}",
        HandlerFunc: user.GetUserByName,
        Secure:      true,
    },
}

func NewRouter() *mux.Router {
    router := mux.NewRouter().StrictSlash(true)
    router.NotFoundHandler = http.HandlerFunc(notFound)
    router.MethodNotAllowedHandler = http.HandlerFunc(notAllowed)
    for _, route := range routes {
        var handler http.Handler
        if route.Secure {
            handler = AuthMiddleware(route.HandlerFunc)
        } else {
            handler = route.HandlerFunc
        }

        handler = Logger(os.Stderr, handler)

        router.
            Methods(route.Method).
            Path(route.Pattern).
            Name(route.Name).
            Handler(handler)
    }

    return router
}

func ApplicationRecovery(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if err := recover(); err != nil {
                fmt.Fprintln(os.Stderr, "Recovered from application error occurred")
                _, _ = fmt.Fprintln(os.Stderr, err)
                w.WriteHeader(http.StatusInternalServerError)
                }))
            }
        }()
        next.ServeHTTP(w, r)
    })
}

func Middleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Add("Content-Type", "application/json")
        next.ServeHTTP(w, r)
    })
}

func AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        //TODO: Add authentication
        log.Println("Authentication required")
        next.ServeHTTP(w, r)
    })
}

func Logger(inner http.Handler, name string) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()

        log.Printf(
            "%s %s %s %s",
            r.Method,
            r.RequestURI,
            name,
            time.Since(start),
        )

        inner.ServeHTTP(w, r)
    })
}

func notFound(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusNotFound)
}

func notAllowed(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusMethodNotAllowed)
}

func main() {
    srv := http.Server{
        Addr:         "0.0.0.0:8080",
        Handler:      ApplicationRecovery(Middleware(NewRouter())),
        ReadTimeout:  15 * time.Second,
        WriteTimeout: 15 * time.Second,
    }

    log.Fatal(srv.ListenAndServe())
}

This way I'm covering my bases:

  • panic during the request execution
  • a common middleware that set the response headers for all request
  • a logging middleware for all request logging
  • an authentication middleware for secure resources

Console logs of the handler in action

2020/06/23 22:28:48 Server started
2020/06/23 22:28:51 Authentication required
2020/06/23 22:28:51 Begin x-api-key validation
2020/06/23 22:28:51 x-api-key matched user: 1
2020/06/23 22:28:51 User 1 successfully accessed secure resourecs
::1 - - [23/Jun/2020:22:28:51 +0100] "DELETE /v2/user/john?permanent=true HTTP/1.1" 403 85

Upvotes: 5

Lucas
Lucas

Reputation: 748

I wrapped the LoggingHandler with a middleware function

func loggingMiddleware(next http.Handler) http.Handler {
    return handlers.LoggingHandler(os.Stdout, next)
}

r.Use(loggingMiddleware)

Upvotes: 5

Burak Serdar
Burak Serdar

Reputation: 51657

Use a middleware:

func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Do stuff here
        log.Println(r.RequestURI)
        // Call the next handler, which can be another middleware in the chain, or the final handler.
        next.ServeHTTP(w, r)
    })
}


r.Use(loggingMiddleware)

Here's the doc: https://github.com/gorilla/mux#middleware

Upvotes: 10

Related Questions