

新闻资讯
技术学院最常见原因是没启动服务器或 ListenAndServe 后程序退出;注册路由仅存入 DefaultServeMux,需调用 ListenAndServe 才生效,且须确保其为 main goroutine 最后调用。
http.HandleFunc 注册的路由不生效?最常见原因是没启动服务器,或 http.ListenAndServe 调用后程序直接退出。注册路由只是往默认的 http.DefaultServeMux 里塞函数,真正监听和分发要靠 ListenAndServe 启动。
http.ListenAndServe(":8080", nil),第二个参数为 nil 表示使用默认多路复用器http.ServeMux,必须显式传入 ListenAndServe 第二个参数,不能传 nil
listen tcp :8080: bind: address already in use
ListenAndServe 后不会自动阻塞——如果它不是 main goroutine 的最后调用,后续代码执行完进程就退出了req.URL.Query() 拿查询参数(?name=alice&age=30),req.ParseForm() 才能读 POST 的 application/x-www-form-urlencoded 或 multipart/form-data 数据。两者互不影响,但顺序有讲究。
req.URL.Query().Get("key")
req.ParseForm()(否则 req.Form 是空的),再用 req.FormValue("key")
ParseForm() 会自动识别 Content-Type 并解析;若请求体是 JSON,则不应调用它,而该用 json.Decoder
?tag=a&tag=b)用 req.URL.Query()["tag"] 获取切片,Get 只返回第一个别手动拼接字符串或用 fmt.Fprintf 输出 JSON——容易出格式错误、缺少 Content-Type、不处理编码问题。
w.Header().Set("Content-Type", "application/json; charset=utf-8"),再用 json.NewEncoder(w).Encode(v)
json.NewDecoder(req.Body).Decode(&v),别用 io.ReadAll + json.Unmarshal,除非你明确需要原始字节
req.Body 只能读一次;如果之前调过 ParseForm() 或 ParseMultipartForm(),Body 可能已被消费,需提前保存或重置{"error": "invalid json"}),并设 http.StatusBadRequest
func handleUser(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
if r.Method != "POST" {
http.Error(w, `{"error":"method not allowed"}`, http.StatusMethodNotAllowed)
return
}
var u struct{ Name string `json:"name"` }
if err := json.NewDecoder(r.Body).Decode(&u); err != nil {
http.Error(w, `{"error":"invalid json"}`, http.StatusBadRequest)
return
}
json.NewEncoder(w).Encode(map[string]string{"msg": "ok", "name": u.Name})
}
Go 的 HTTP handler 函数每次请求都在独立 goroutine 中执行,但闭包捕获的外部变量(比如全局 map、计数器)是共享的,没有自动同步机制。
sync.Mutex 或 sync.RWMutex 保护http.Handle 注册带状态的 handler 实例,而非 HandleFunc
log.Printf 是线程安全的,但自定义 logger 若含缓存或写文件逻辑,仍需确认并发安全性req.Body 的一次性读取、ParseForm 的隐式副作用、以及没加锁的共享变量,最容易在线上突然暴露。