4

I'm fairly new to Go and trying to create a middleware implementation in a gateway library that I'm building on top of mux. So far, I've come up up with this:

type (
    MyContext struct {
        // ...
    }

    APIGateway struct {
        mux *mux.Router
        ctx *MyContext
    }
)

type MiddlewareFunc func(*MyContext) func(http.Handler) http.Handler

func (a APIGateway) Use(mw MiddlewareFunc) {
    a.mux.Use(mw(ctx))
}

This works, but then I have to deal with a lot of boilerplate code. The simplest middleware implemented on this pattern has to have a minimum of two return statements and lot of incline function declaration.

func someMiddleware(ctx *MyContext) func(http.Handler) http.Handler {
    return func(handler http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            // Application Logic here
            handler.ServeHTTP(w, r)
        })
    }
}

This quickly becomes a bigger problem when the middlware has to take some extra parameters since now it has to deal with three return statements:

func LogginMiddleware(c LoggerConfig) MiddlewareFunc {
    return func(ctx *MyContext) func(http.Handler) http.Handler {
        return func(handler http.Handler) http.Handler {
            return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
                // ...
                handler.ServeHTTP(w, r)
                // ...
            })
        }
    }
}

Is there a way I can possibly reduce the involvement of boilerplate code?

Jay
  • 1,089
  • 12
  • 29

1 Answers1

4

You have this problem because the middleware functions are implemented as closures. Note that middleware function is defined as:

type MiddlewareFunc func(http.Handler) http.Handler

where http.Handler is an interface. So you can write something like this for each middleware:

type MyMiddleware struct {
   // Fields your middleware func needs
   Next http.Handler
}

// This is the middleware func
func MW(next http.Handler) http.Handler {
   m:=MyMiddleware{Next:next}
   // Initialize other fields
   return m
}

// Implement http.Handler.
func (m MyMiddleware) ServeHTTP(wr http.ResponseWriter, req *http.Request) {
   // Do  middleware stuff
   m.Next.ServeHTTP(w,req)
}
Burak Serdar
  • 46,455
  • 3
  • 40
  • 59
  • Thanks. While this was not exactly what I was looking for, you certainly sent me in the right direction :) – Jay Apr 27 '20 at 18:16