Go net/http 标准库基础
一、知识点总结
1.1 net/http 包的设计理念
Go 的net/http包是标准库中最具代表性的设计之一,它遵循接口驱动的设计哲学。整个包围绕两个核心接口展开:
Handler接口:处理 HTTP 请求的核心抽象ResponseWriter+*Request:请求-响应的载体
这种设计的精妙之处在于解耦。你可以用任何自定义类型实现Handler接口,然后注册到服务端,不需要继承任何类或框架提供的基类。这是 Go “接口即契约” 思想的典型体现。
1.2 Handler 接口
typeHandlerinterface{ServeHTTP(w ResponseWriter,r*Request)}任何实现了ServeHTTP方法的类型都可以作为 HTTP 处理器。标准库提供了HandlerFunc类型作为函数适配器,让普通函数也能当作 Handler 使用:
typeHandlerFuncfunc(ResponseWriter,*Request)func(f HandlerFunc)ServeHTTP(w ResponseWriter,r*Request){f(w,r)}这个技巧非常经典——它让函数和实现了接口的结构体可以互换使用,既保留了接口的扩展性,又提供了函数的便利性。
1.3 ServeMux —— 路由多路复用器
ServeMux是标准库内置的路由分发器,负责将 URL 匹配到对应的 Handler:
- 注册路由:
mux.Handle("/path", handler)或mux.HandleFunc("/path", fn) - 匹配规则:最长前缀匹配,例如
/api/users比/api/更优先 - 路径规范:会自动处理冗余斜杠(
//→/) - 区分大小写:URL 路径匹配是大小写敏感的
1.4 Server 结构体与 ListenAndServe
http.Server是一个可配置的服务端结构体:
typeServerstruct{Addrstring// 监听地址,如 ":8080"Handler Handler// 若nil则使用 DefaultServeMuxReadTimeout time.Duration// 读取请求超时WriteTimeout time.Duration// 写入响应超时IdleTimeout time.Duration// Keep-Alive 连接空闲超时MaxHeaderBytesint// 请求头最大字节数}http.ListenAndServe是快捷函数,内部创建一个默认的 Server。生产环境建议显式配置Server,以便控制超时行为——没有超时的服务是生产事故的温床。
1.5 ResponseWriter 与 Request
ResponseWriter用于构造响应:
WriteHeader(statusCode)—— 写入状态码(只能调用一次,且必须在 Write 之前)Write([]byte)—— 写入响应体(若未调用 WriteHeader,自动写入 200 OK)Header()—— 获取响应头http.Header(必须在 WriteHeader 前设置)
Request封装了请求的所有信息:
r.Method—— HTTP 方法(GET/POST/PUT/DELETE 等)r.URL—— 解析后的 URL 对象(含 Path、RawQuery、Scheme 等)r.Header—— 请求头r.Body—— 请求体(io.ReadCloser,必须关闭)r.FormValue(key)—— 获取表单值(自动解析 query string 和 POST body)
1.6 重要注意事项
- WriteHeader 只能调用一次:重复调用会触发 panic 或无效
- Header 必须在 WriteHeader 前设置:否则不生效
- 请求体必须关闭:
defer r.Body.Close(),否则连接池会泄漏 - 没有路由参数:标准库 ServeMux 不支持
/users/:id这种路由参数,需要手动从r.URL.Path解析或使用第三方框架 - 默认无超时:
ListenAndServe没有配置任何超时,生产环境必须显式设置
二、练习代码
示例 1:最基础的 HTTP 服务
packagemainimport("fmt""log""net/http")funcmain(){// 使用 HandleFunc 注册路由,底层自动包装为 HandlerFunchttp.HandleFunc("/",func(w http.ResponseWriter,r*http.Request){fmt.Fprintf(w,"Hello, Go HTTP! Method=%s, Path=%s\n",r.Method,r.URL.Path)})http.HandleFunc("/health",func(w http.ResponseWriter,r*http.Request){w.WriteHeader(http.StatusOK)fmt.Fprintln(w,`{"status":"ok"}`)})log.Println("Server starting on :8080")// 等价于 http.ListenAndServe(":8080", nil)// nil 表示使用 DefaultServeMuxiferr:=http.ListenAndServe(":8080",nil);err!=nil{log.Fatal(err)}}示例 2:自定义 Handler 结构体
packagemainimport("fmt""log""net/http""sync/atomic")// CounterHandler 是一个自定义 Handler,统计访问次数typeCounterHandlerstruct{count atomic.Int64}func(h*CounterHandler)ServeHTTP(w http.ResponseWriter,r*http.Request){// 原子递增,线程安全c:=h.count.Add(1)w.Header().Set("Content-Type","text/plain; charset=utf-8")fmt.Fprintf(w,"你是第 %d 位访客\n",c)}funcmain(){mux:=http.NewServeMux()counter:=&CounterHandler{}// 注册自定义 Handlermux.Handle("/counter",counter)mux.HandleFunc("/",func(w http.ResponseWriter,r*http.Request){fmt.Fprintln(w,"访问 /counter 查看计数")})log.Println("Server on :8080")iferr:=http.ListenAndServe(":8080",mux);err!=nil{log.Fatal(err)}}示例 3:带超时的生产级 Server 配置
packagemainimport("context""fmt""log""net/http""os""os/signal""syscall""time")funcmain(){mux:=http.NewServeMux()mux.HandleFunc("/",func(w http.ResponseWriter,r*http.Request){fmt.Fprintln(w,"Hello with timeout protection!")})mux.HandleFunc("/slow",func(w http.ResponseWriter,r*http.Request){// 模拟慢请求:5秒延迟time.Sleep(5*time.Second)fmt.Fprintln(w,"slow response")})// 生产环境推荐:显式配置 Server,设置超时server:=&http.Server{Addr:":8080",Handler:mux,ReadTimeout:3*time.Second,// 读取完整请求的最大时间WriteTimeout:5*time.Second,// 写入响应的最大时间IdleTimeout:60*time.Second,// Keep-Alive 空闲超时}// 优雅关闭:在独立 goroutine 中启动服务gofunc(){log.Println("Server starting on :8080")iferr:=server.ListenAndServe();err!=nil&&err!=http.ErrServerClosed{log.Fatalf("Server error: %v",err)}}()// 监听系统信号,实现优雅关闭sigChan:=make(chanos.Signal,1)signal.Notify(sigChan,syscall.SIGINT,syscall.SIGTERM)<-sigChan log.Println("Shutting down server...")// 给正在处理的请求 5 秒缓冲时间ctx,cancel:=context.WithTimeout(context.Background(),5*time.Second)defercancel()iferr:=server.Shutdown(ctx);err!=nil{log.Fatalf("Shutdown error: %v",err)}log.Println("Server gracefully stopped")}示例 4:ResponseWriter 使用要点演示
packagemainimport("fmt""log""net/http")funcmain(){mux:=http.NewServeMux()// 正确设置 Header 的顺序演示mux.HandleFunc("/headers",func(w http.ResponseWriter,r*http.Request){// 1. 先设置响应头w.Header().Set("Content-Type","application/json")w.Header().Set("X-Custom-Header","myvalue")// 2. 再调用 WriteHeader(可选,Write 会自动调)w.WriteHeader(http.StatusCreated)// 201// 3. 最后写入响应体fmt.Fprintln(w,`{"message":"created"}`)// 下面这行无效:WriteHeader 已调用,再设置 Header 不生效w.Header().Set("X-Late-Header","too-late")})// 演示请求信息读取mux.HandleFunc("/info",func(w http.ResponseWriter,r*http.Request){w.Header().Set("Content-Type","text/plain")fmt.Fprintf(w,"Method: %s\n",r.Method)fmt.Fprintf(w,"URL Path: %s\n",r.URL.Path)fmt.Fprintf(w,"Query: %s\n",r.URL.RawQuery)fmt.Fprintf(w,"User-Agent: %s\n",r.UserAgent())fmt.Fprintf(w,"Host: %s\n",r.Host)fmt.Fprintf(w,"RemoteAddr: %s\n",r.RemoteAddr)})log.Println("Server on :8080")log.Fatal(http.ListenAndServe(":8080",mux))}