用 net/http 启最简服务器只需两步:注册路由(http.handlefunc)和监听(http.listenandserve(":8080", nil)),注意地址用":8080"而非"localhost:8080"以支持外部访问。

怎么用 net/http 启一个最简 HTTP 服务器
不需要第三方库,Go 标准库的 net/http 足够跑起生产可用的轻量服务。核心就两步:注册路由 + 调用 http.ListenAndServe。
常见错误是监听地址写成 "localhost:8080" —— 这会导致外部无法访问,必须用 ":8080"(空主机名表示监听所有网卡)。
-
http.HandleFunc注册路径处理器,第一个参数是带前导/的路径字符串 - 处理器函数签名必须是
func(http.ResponseWriter, *http.Request) -
http.ListenAndServe第二个参数传nil表示使用默认的http.DefaultServeMux - 端口被占用时会报错
listen tcp :8080: bind: address already in use,需先lsof -i :8080或kill
func main() {
http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
w.Write([]byte("pong"))
})
log.Fatal(http.ListenAndServe(":8080", nil))
}怎么发一个带超时和 JSON 的 HTTP 请求
直接用 http.Get 或 http.Post 很方便,但没超时控制、没法设 Header、不支持重定向定制——这些都得用 http.Client 实例。
关键点在于:超时不是设在请求上,而是设在 http.Client 的 Timeout 字段;JSON 发送要手动序列化并设 Content-Type。
立即学习“go语言免费学习笔记(深入)”;
- 别用
time.AfterFunc或context.WithTimeout包裹http.Get,那只会中断你的代码逻辑,不会取消底层连接 -
http.Client是可复用的,建议全局复用一个实例,避免连接池泄漏 - POST JSON 时,
bytes.NewReader(data)比strings.NewReader(string(data))更安全(避免 UTF-8 编码问题) - 如果服务端返回非 2xx 状态码,
resp.Body依然可读,但resp.StatusCode需主动检查
client := &http.Client{Timeout: 5 * time.Second}
data, _ := json.Marshal(map[string]string{"msg": "hello"})
resp, err := client.Post("http://localhost:8080/api", "application/json", bytes.NewReader(data))
if err != nil { return }
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)如何处理表单提交、URL 查询参数和请求体共存
Go 的 *http.Request 把不同来源的数据分开放,容易混淆:r.URL.Query() 是 URL 中的 ?key=val,r.FormValue 会自动合并 GET 查询参数和 POST 表单(application/x-www-form-urlencoded),但对 JSON 或 raw body 不生效。
典型坑是:前端用 fetch 发 POST /login?from=mobile,后端只调 r.FormValue("from") 就能拿到,但若前端发的是 JSON,这个值就为空——因为没触发自动解析表单。
- 调
r.ParseForm()是前提,否则r.Form和r.PostForm都是空的(GET 请求也建议调,确保一致性) -
r.FormValue("key")等价于r.FormValue("key"),但优先取 POST 数据,再 fallback 到 URL 查询参数 - 读原始 body 前必须用
io.ReadAll(r.Body),且只能读一次;之后再调r.FormValue会失败(body 已关闭) - 上传文件要用
r.ParseMultipartForm,且需提前设置r.MaxMemory,否则大文件直接 OOM
为什么本地测试时经常遇到连接拒绝或 CORS 报错
连接拒绝(dial tcp [::1]:8080: connect: connection refused)基本等于服务器根本没起来,或者端口不对;CORS 报错(Access to fetch at ... has been blocked by CORS policy)则是浏览器拦截,和 Go 服务本身无关,但你得在服务端加响应头才能让前端跨域请求成功。
- 启动服务器后立刻 curl 测试:
curl -v http://localhost:8080/ping,确认服务存活再测前端 - CORS 头必须由服务端返回:
w.Header().Set("Access-Control-Allow-Origin", "*"),仅开发用;生产环境应限制具体域名 - 预检请求(OPTIONS)不会进你写的
HandleFunc,除非显式注册:http.HandleFunc("OPTIONS /api", corsHandler) - Chrome 会缓存预检结果,改了 CORS 头后要清空开发者工具里的「Network」→ 右键 → 「Clear browser cache」
HTTP 服务写起来快,但调试时最容易卡在“以为服务有问题”,其实只是浏览器策略、本地防火墙、或启动顺序错了。把 log.Printf("req: %+v", r) 打在 handler 开头,比猜强得多。











