用 net/http 启动最简服务只需三行代码:导入 net/http 包,调用 http.ListenAndServe(":8080", nil) 即可监听端口,但未注册路由时所有请求均返回 404。

用 net/http 启动最简服务
Go 自带的 net/http 包不需要额外依赖,三行就能跑起来。核心是调用 http.ListenAndServe,它会阻塞运行,监听指定地址并处理请求。
package main
import "net/http"
func main() {
http.ListenAndServe(":8080", nil)
}
这段代码启动一个监听 :8080 的服务器,但目前没有注册任何路由,所有请求都会返回 404 page not found。它只是“能跑”,不是“能用”。
注册 http.HandleFunc 处理路径
要让服务器响应具体 URL,得用 http.HandleFunc 注册处理器函数。它接收两个参数:路径前缀(如 "/")和一个符合 func(http.ResponseWriter, *http.Request) 签名的函数。
- 路径匹配是前缀匹配,
"/api"会匹配/api、/api/users,但不会匹配/apix - 多个注册时,更长的路径前缀优先级更高(实际按注册顺序覆盖,建议避免重叠)
- 空字符串
""等价于"/",表示根路径
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Hello, World!")
})
http.ListenAndServe(":8080", nil)
}
用 http.ServeMux 显式管理路由
直接用 nil 作为 http.ListenAndServe 的第二个参数,底层会使用默认的 http.DefaultServeMux。但显式创建 http.ServeMux 更可控,也方便测试或替换。
立即学习“go语言免费学习笔记(深入)”;
- 避免意外污染全局
DefaultServeMux(尤其在库中注册 handler 时) - 便于单元测试:可传入构造好的
*http.ServeMux给httptest.NewServer - 支持更清晰的路由组织,比如按模块分组注册
package main
import (
"fmt"
"net/http"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "ok")
})
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Home page")
})
http.ListenAndServe(":8080", mux)
}
常见错误:端口被占用或权限不足
运行时报错 listen tcp :8080: bind: address already in use 或 permission denied 是高频问题。
-
address already in use:检查是否已有进程在监听该端口,可用lsof -i :8080(macOS/Linux)或netstat -ano | findstr :8080(Windows)定位并 kill -
permission denied:尝试绑定:80或:443等特权端口时,Linux/macOS 需要 root 权限;开发阶段建议改用:8080、:3000等非特权端口 - 忘记
return导致后续逻辑执行:在 handler 中写完响应后不return,可能意外继续执行下一行(比如又写一次 body),引发http: multiple response.WriteHeader calls
真正上线时,别只靠 ListenAndServe —— 它不支持超时控制、优雅关闭、TLS 自动配置等,这些得靠封装 http.Server 实例来实现。

