*image credit to Renee French, the source is the one of the official Go blog posts
TL; DR
- "Handler"s are types, whereas "Handle"s are functions that register them.
-
HandlerFunc
can be understood as a "wrapper" that equips a plain function with the interfaceHandler
. - All you need to understand is the type
Handler
; everything else follows from it.
Introduction: A family of confusing naming - Handle(r) family
As a newbie who recently having started digging into Go, I find it fascinating in that I can basically live without relying on third party packages. And one of the best things I think you can do with only using the standard packages, is to run a server using net/http.
However, once I began looking into tutorials and other learning materials(like gowebexamples.com) for using this package, the family of “Handle(r)” was a little bit confusing to understand in its naming. I believe I am not the only one:
- Handler and HandlerFunc
- Handle and HandleFunc
So in this short article, I would like to explain how I understand their differences and my own tip to distinguish them in a simple way.
Distinguishing the family members from naming
Let’s talk about Handler
and Handle
first. If you read the documentation of the net/http package, Handler
is an interface that implements ServeHTTP
function, whereas Handle
is either a function or a method belonging to the type *ServeMux
. Namely, Handle
function/method registers a given Handler
to its tied (Default)ServeMux
.
You can find almost the identical relationship between HandlerFunc
and HandleFunc
. So here is my first personal tip to understand the “Handle(r)” family:
1)"Handler"s are types, whereas "Handle"s are functions that register them.
But understanding the difference between Handler
and HandlerFunc
is not that simple. See this explanation of HandlerFunc
in the documentation:
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.
So HandleFunc
is an "adapter". The term "adapter" would be probably from the adapter design pattern, which simply means that an adapter changes the interface without touching the internal logic of the adapted target. Thus, the explanation here can be understood as
2)HandlerFunc
can be understood as a "wrapper" that equips a plain function with the interface Handler
.
and this second tip becomes more obvious once we read this line from the source code of the package:
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
So when we convert a function of type func(w http.ResponseWriter, r *http.Request)
into a HandlerFunc
, we must have in mind that the function will behave exactly the same as ServeHTTP(w, r)
.
The type Handler
is at the heart of the family
Now from the tips 1) and 2), we can infer the following takeaway which I would like to emphasize as a separate tip:
3)All you need to understand is the type Handler
; everything else follows from it.
But what does it mean by "understanding Handler
"? Although the low-level implementation can be found(for example, the method Serve
), I would like to mention that it is an interface with only one method. Since we only have to implement one single method ServeHTTP
, we can construct a handler with more stateful layers.
For example:
type WrappedHandler struct {
// some internal variables representing states
}
func (wh *WrappedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// …
}
type WrapperHandler struct {
// additional variables
wrapped *WrappedHandler
}
func (wh *WrapperHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// do some layered logics
// run the wrapped handler’s logic
wh.wrapped.ServeHTTP(w, r)
}
Moreover, using HandlerFunc
, we can make a middleware in a simple way following this pattern(I learned it from the book Learning Go, 2nd ed. by Jon Bodner).
func middleware(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// do some middleware stuffs
// call the original handler’s ServeHTTP
handler.ServeHTTP(w, r)
})
}
Conclusion
The three tips mentioned here are totally personal. However, I find it fairly useful whenever I get confused with reading the names of the members of “Handle(r)” family, clearly distinguishing what each type/function does. Moreover, from understanding the roles of them, I can be more familiar with the structure of how net/http package works under the hood.
Top comments (0)