net/http 可构建轻量 REST 服务:需手动精确匹配路径、解析 URL 参数、一次性读取 Body、导出结构体字段并加 json tag、校验 Content-Type、用 http.Error 或封装函数返回错误、显式处理 OPTIONS 预检并设置 CORS 头。
net/http 启动最简 REST 服务,别急着上框架Go 原生 net/http 完全够用,尤其在中小规模 API 场景。过早引入 gin 或 echo 反而掩盖路由匹配、中间件执行顺序等关键细节。
http.HandleFunc 注册路径时,前缀匹配是隐式的:注册 /api/users 会同时响应 /api/users/123 —— 需手动检查 r.URL.Path 是否精确匹配http.ServeMux 自定义路由时,注意它不支持路径参数(如 /users/{id}),必须自己解析 r.URL.Path 并提取 id
if err := http.ListenAndServe(":8080", nil); err != nil && err != http.ErrServerClosed {
log.Fatal
(err)
}io.EOF 和空结构体常见错误是直接对 http.Request.Body 多次调用 json.Unmarshal —— Body 是单次读取流,第二次读就会返回 io.EOF。
io.ReadAll(r.Body) 一次性读完,再传给 json.Unmarshal;不要试图复用 r.Body
json tag,否则反序列化后字段为空:type User struct { Name string `json:"name"` }
r.Header.Get("Content-Type") 检查是否为 application/json,非 JSON 请求应返回 415 Unsupported Media Type
http.Error 统一返回错误,别拼接字符串手写 w.WriteHeader(400); w.Write([]byte(`{"error":"xxx"}`)) 容易遗漏状态码、Content-Type 或格式不一致。
http.Error(w, "bad request", http.StatusBadRequest),它自动设状态码和 text/plain 类型func writeJSONError(w http.ResponseWriter, msg string, code int) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
json.NewEncoder(w).Encode(map[string]string{"error": msg})
}http.Error 不会终止 handler 执行,后续代码仍会运行,必要时加 return
OPTIONS 预检请求必须显式响应浏览器发跨域请求前会先发 OPTIONS,如果没处理,前端卡在 pending 状态,控制台只显示 “CORS error”。
if r.Method == "OPTIONS" { w.WriteHeader(http.StatusOK); return }
Access-Control-Allow-Origin、Access-Control-Allow-Methods、Access-Control-Allow-Headers
Access-Control-Allow-Credentials: true,且 Allow-Origin 不能为 *