-1

I have this utility:

type Handler struct{}

func (h Handler) Mount(router *mux.Router, v PeopleInjection) {
    router.HandleFunc("/api/v1/people", h.makeGetMany(v)).Methods("GET")
}

the above calls this:

func (h Handler) makeGetMany(v PeopleInjection) http.HandlerFunc {

    type RespBody struct {}

    type ReqBody struct {
        Handle string
    }

    return tc.ExtractType(
        tc.TypeList{ReqBody{},RespBody{}},
        func(w http.ResponseWriter, r *http.Request) {
         // ...
    })
}

and then tc.ExtractType is like so:

func ExtractType(s TypeList, h http.HandlerFunc) http.HandlerFunc {

    return func(w http.ResponseWriter, r *http.Request) {

        h.ServeHTTP(w, r)  // <<< h is just a func right? so where does ServeHTTP come from?
    }
}

my question is - where does the serveHTTP method/func come from??

isn't the h parameter just a func with this signature:

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

so then how does that func have the ServeHTTP func attached to it?

In other words, why I am I calling

h.ServeHTTP(w,r)

instead of

h(w,r)

?

  • my only guess is that the core http or gorilla/mux lib magically adds that utility method after `http.HandlerFunc` gets returned to `router.HandleFunc` –  Dec 02 '18 at 05:19
  • I was not a fan of this adding function methods to a function pointer trick when I discovered it several years ago. Yes it is clever and all but I think it's confusing. – Zan Lynx Dec 02 '18 at 06:53

1 Answers1

4

The http.HandlerFunc is a type represent func(ResponseWriter, *Request).

The difference between http.HandlerFunc and func(ResponseWriter, *Request) is: http.HandlerFunc type has method called ServeHTTP().

From the source code:

// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

The http.HandlerFunc() can be used to wrap handler function.

func Something(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // do something

        next.ServeHTTP(w, r)
    })
}

The wrapped handler will have http.HandlerFunc() type, meaning we'll be able to access it's .ServeHTTP() method.


There is also another type, an interface called http.Handler. It has .ServeHTTP() method signature, and it must be implemented on the struct where the interface is being embedded.

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

As you can see on Something() function above, a return value in http.Handler type is required, but we returned a handler wrapped in http.HandlerFunc(). It's fine, because the http.HandlerFunc has method .ServeHTTP() who fulfil the requirement of http.Handler interface.


In other words, why I am I calling h.ServeHTTP(w,r) instead of h(w,r)?

Because to continue serve the incoming request, you need to call .ServeHTTP().

novalagung
  • 10,905
  • 4
  • 58
  • 82