Skip to content

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.Handler is anything with ServeHTTP(w, r). A *ServeMux is 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 with r.PathValue("id"), never by slicing r.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 Allow header) when a path matches but the method doesn't — the old mux silently 404'd.
  • Prefer constructing &http.Server{Addr, Handler, …Timeout} over the http.ListenAndServe shortcut 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 bare Write implies WriteHeader(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)

  1. 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).
  1. 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 ServeMux interacts 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.Handler interface and the HandlerFunc adapter in depth.

Time spent Difficulty Confidence
90 min 🟦🟦⬜⬜⬜ 🟦🟦🟦⬜⬜

Suggested commit: feat(examples): net/http server and ServeMux routing (day 085)