Day 086 — http.Handler & HandlerFunc¶
Month 4 · Week 1 · ⬅ Day 085 · Day 087 ➡ · Journal index
🎯 Learning Objective¶
Understand the one interface the whole net/http ecosystem is built on,
http.Handler, and the HandlerFunc adapter that turns plain functions into it.
📚 Topics¶
http.Handler,ServeHTTP,http.HandlerFunc, the function adapter patternResponseWritermechanics, handler values vs functions
📖 Reading / Sources¶
📝 Notes¶
- The entire server contract is one method:
type Handler interface { ServeHTTP(http.ResponseWriter, *http.Request) }. Everything — mux, middleware, file server — is just aHandler→ [[http-handler]]. HandlerFuncis the classic adapter: a named function type whose ownServeHTTPcalls the function.type HandlerFunc func(w, r)+func (f HandlerFunc) ServeHTTP(w, r) { f(w, r) }lets a plain func satisfy the interface → [[function-adapter]].mux.HandleFunc(pattern, fn)is sugar formux.Handle(pattern, http.HandlerFunc(fn)).ResponseWriteris an interface, not a buffer: writing to it streams bytes.Header()must be set beforeWriteHeader; aWritewithoutWriteHeadersends200first. You cannot change the status after the body starts.- A handler runs on its own goroutine per request; don't share mutable state without synchronisation → ties back to [[mutex]] / [[channels]].
- The request
*http.Requestcarries aContext()that is cancelled when the client disconnects — pass it to downstream calls (context is first-param) → [[context]]. - Returning a
Handlerfrom a function (a constructor) is how middleware and dependency injection work; you wrap oneHandlerto produce another.
💻 Code Examples¶
// HandlerFunc adapts a plain function to the http.Handler interface.
type greeter struct{ name string } // a struct handler, when you need state
func (g greeter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello from %s\n", g.name)
}
mux := http.NewServeMux()
mux.Handle("GET /svc", greeter{name: "svc-a"}) // value implements Handler
mux.HandleFunc("GET /ping", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("pong")) // implicit WriteHeader(200)
})
See it wired up in
examples/month-04/servemux/main.go· Run:go run ./examples/month-04/servemux
🏋️ Exercises / Practice¶
| Exercise | Status | Link |
|---|---|---|
| Wrap handlers (Chain + Recoverer return Handlers) | ✅ | exercises/month-04/week-1/middleware |
🐛 Mistakes Made¶
- Passed a method value with the wrong signature to
HandleFunc→ it must be exactlyfunc(http.ResponseWriter, *http.Request). - Called
w.WriteHeader(201)afterw.Write(...); the 201 was ignored (status already 200) and the server logged "superfluous WriteHeader call".
❓ Open Questions¶
- When is a struct handler better than a closure capturing dependencies? (Struct handlers shine when you need methods + shared fields; closures are lighter for one-offs.)
🧠 Active Recall (answer without looking)¶
- Q: What single method must a type implement to be an
http.Handler?
A
`ServeHTTP(w http.ResponseWriter, r *http.Request)`.- Q: What does
http.HandlerFunc(f)give you?
A
It converts a plain `func(w, r)` into a value whose `ServeHTTP` just calls `f`, so the function satisfies the `Handler` interface.🪶 Feynman Reflection¶
http.Handler is the universal plug: anything that can answer a request fits the
same socket. HandlerFunc is an adapter that lets a bare function pretend to be
a full object, which is why Go web code is mostly small functions plus wrappers.
🕳️ Knowledge Gaps¶
- The optional interfaces a
ResponseWritermay also implement (http.Flusher,http.Hijacker) and when they matter.
✅ Summary¶
I see that the whole net/http stack is one interface; functions become handlers
via HandlerFunc, and constructors that return handlers unlock middleware.
⏭️ Next Steps / Prep for Tomorrow¶
- Day 087: routing with chi — sub-routers, URL params, and route groups.
| Time spent | Difficulty | Confidence |
|---|---|---|
| 90 min | 🟦🟦⬜⬜⬜ | 🟦🟦🟦⬜⬜ |
Suggested commit: docs(journal): http.Handler and HandlerFunc (day 086)