How does httprouterhttp.HandlerFunc() works?

Issue

I am learning Go and at the moment trying to understand what and how actually julienschmidt’s httprouterhttp router works. Especially how the router.HandlerFunc() works.

Go’s http package:

  • http.Handler: Handler is an Interface. Any type which has the same method signature i.e., ServeHTTP(w,r) implements a Handler.
  • http.Handle: http.Handle is a function which has two parameters. 1) String to match request path, 2)Handler, to call its ServeHTTP() method.
  • http.HandlerFunc: A custom function type with function signature (ResponseWriter, *Request) and its own ServeHTTP(w,r) method, which satisfies as a http.Handler interface.

Now if I need to use a function eg. Bar(ResponseWriter, *Request) which satisfies to be a HandlerFunc, inside http.Handle, I type caste/covert the function to http.HandlerFunc and then use it inside the http.Handle. Like so:

func Bar(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("My bar func response"))
}
...
mf := http.HandlerFunc(Bar)
http.Handle("/path", mf)
  • Looking at the http.HandleFunc source code, this is how http.HandleFunc() works. It expects a function which as the signature of (w ResponseWriter, r *Request) and type caste the function.

Like so:

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    if handler == nil {
        panic("http: nil handler")
    }
    mux.Handle(pattern, HandlerFunc(handler))
}

julienschmidt’s httprouterhttp

Looking at the source code of router.HandlerFunc()

func (r *Router) HandlerFunc(method, path string, handler http.HandlerFunc) {
    r.Handler(method, path, handler)
}
  • Instead of expecting a function with appropriate signature (ResponseWriter, *Request) like the http.HandleFunc(), it expects a http.HandlerFunc.
  • Also without type casting/converting the function to a HandlerFunc type, it just pass the function to another function router.Handler(method, path string, handler http.Handler), which again expects a Handler.

So I can execute this code without any problem:

router.HandlerFunc(http.MethodPost, "/path", Bar) // Bar is not type casted/converted to a HandlerFunc

I can understand type casting the Bar to a HandlerFunc and then pass it to router.HandlerFunc().

Could you please clear some of my doubts:

  1. How does without type casting the Bar() to a HandlerFunc satisfies as a HandlerFunc type to the router.HandlerFunc()?
  2. If the router.Handler() expects a http.Handler type, how come Bar() without type casting to the http.HandlerFunc satisfies a a Handler to the router.Handler()?
  3. What am I missing?

Solution

  1. "How does without type casting the Bar() to a HandlerFunc satisfies as a HandlerFunc type to the router.HandlerFunc()?"

The expression

router.HandlerFunc(http.MethodPost, "/path", Bar)

complies with the rules of Assignability:

V and T have identical underlying types and at least one of V or T is not a named type.

The Bar function’s type and the http.HandlerFunc type have identical underlying types, and Bar‘s type is not named. Because of that you can pass (assign) Bar as the handler argument to httprouter.Router.HandlerFunc without an explicit conversion.


  1. "If the router.Handler() expects a http.Handler type, how come Bar() without type casting to the http.HandlerFunc satisfies a a Handler to the router.Handler()?"

In the following method definition

func (r *Router) HandlerFunc(method, path string, handler http.HandlerFunc) {
    r.Handler(method, path, handler)
}

there is no Bar, there’s only the handler argument whose type is http.HandlerFunc and that type does implement the http.Handler interface. So the statement r.Handler(method, path, handler) is perfectly legitimate.


  1. "What am I missing?"

See above.

Answered By – mkopriva

Answer Checked By – Willingham (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.