Day 085 — net/http Server & ServeMux¶
Month 4 · Week 1 · ⬅ Day 084 · Day 086 ➡ · Journal index
🎯 Learning Objective¶
Stand up an HTTP server with the standard library and route requests with the
Go 1.22 enhanced http.ServeMux — methods, path wildcards, and precedence.
📚 Topics¶
http.Server,ListenAndServe,http.NewServeMux,HandleFunc- Method+pattern routes,
{id}/{path...}/{$},r.PathValue
📖 Reading / Sources¶
📝 Notes¶
- An
http.Handleris anything withServeHTTP(w, r). A*ServeMuxis itself a Handler that dispatches to other handlers → [[http-handler]]. - Go 1.22 patterns add method + wildcards:
"GET /users/{id}". The method is optional; omit it to match any method. Read wildcards withr.PathValue("id"), never by slicingr.URL.Path. {path...}is a trailing wildcard that greedily captures the remainder.{$}anchors an exact match (/things/{$}matches only/things/, not a subtree). A pattern ending in/without{$}matches the whole subtree.- Precedence: the most specific pattern wins regardless of registration order; truly conflicting patterns panic at registration.
- The new mux returns 405 Method Not Allowed (with an
Allowheader) when a path matches but the method doesn't — the old mux silently 404'd. - Prefer constructing
&http.Server{Addr, Handler, …Timeout}over thehttp.ListenAndServeshortcut so you can set timeouts later → [[http-timeouts]]. - Set response headers before the first
Write/WriteHeader; they freeze once the status line is sent. A bareWriteimpliesWriteHeader(200).
💻 Code Examples¶
mux := http.NewServeMux()
mux.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "user %s\n", r.PathValue("id")) // read the wildcard
})
mux.HandleFunc("DELETE /users/{id}", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNoContent) // 204, no body
})
srv := &http.Server{Addr: ":8085", Handler: mux}
log.Fatal(srv.ListenAndServe())
Full code:
examples/month-04/servemux/main.go· Run:go run ./examples/month-04/servemux
🏋️ Exercises / Practice¶
| Exercise | Status | Link |
|---|---|---|
Method routing + {id} PathValue, auto 404/405 |
✅ | exercises/month-04/week-1/pathrouter |
🐛 Mistakes Made¶
- Tried to read the id with
strings.Split(r.URL.Path, "/")—r.PathValue("id")is the right tool now and survives pattern changes. - Forgot the leading method:
"/users/{id}"matched every method, so my DELETE ran the GET handler. Added explicit"GET …"/"DELETE …".
❓ Open Questions¶
- When do two registered patterns "conflict" enough to panic vs. just rank by specificity? (Revisit the precedence rules in the docs.)
🧠 Active Recall (answer without looking)¶
- Q: How do you read the
{id}wildcard from"GET /users/{id}"?
A
`r.PathValue("id")` — it returns the matched segment as a string (empty if absent).- Q: What status does the Go 1.22 mux return when the path matches but the method doesn't?
A
`405 Method Not Allowed`, with an `Allow` header listing the methods that *are* registered for that path.🪶 Feynman Reflection¶
A ServeMux is a switchboard: each incoming request is a call, and the mux
connects it to the handler whose pattern best fits the method+path. Since Go 1.22
the switchboard understands verbs and named slots in the path, so I rarely need a
third-party router for simple services.
🕳️ Knowledge Gaps¶
- Subtree (
/static/) vs exact ({$}) matching edge cases. - How
ServeMuxinteracts with host-prefixed patterns (example.com/...).
✅ Summary¶
I can boot an http.Server, register method-aware routes on the stdlib mux, read
path wildcards, and rely on automatic 404/405 handling.
⏭️ Next Steps / Prep for Tomorrow¶
- Day 086: the
http.Handlerinterface and theHandlerFuncadapter in depth.
| Time spent | Difficulty | Confidence |
|---|---|---|
| 90 min | 🟦🟦⬜⬜⬜ | 🟦🟦🟦⬜⬜ |
Suggested commit: feat(examples): net/http server and ServeMux routing (day 085)