Golang net/http标准库怎么用_Golang HTTP开发入门

11次阅读

最简http服务用http.ListenAndServe(“:8080”, nil),默认使用http.DefaultServeMux;需自定义路由时应创建http.ServeMux实例并显式传入server.Handler,注意其仅支持前缀匹配且同路径后注册覆盖前注册。

Golang net/http标准库怎么用_Golang HTTP开发入门

gonet/http 标准库足够轻量、稳定,直接用它写生产级 HTTP 服务完全可行,不需要立刻上框架。

怎么快速启动一个 HTTP 服务

最简方式是调用 http.ListenAndServe,传入监听地址和处理器。默认使用 http.DefaultServeMux,也就是全局的多路复用器。

常见写法:

package main  import (     "fmt"     "net/http" )  func main() {     http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {         fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])     })     http.ListenAndServe(":8080", nil) // nil 表示用默认多路复用器 }

注意:http.ListenAndServe 默认启用 HTTP/1.1,不支持 https(需用 http.ListenAndServeTLS);端口被占用时会直接 panic,建议加错误处理。

立即学习go语言免费学习笔记(深入)”;

  • 监听地址写 ":8080" 表示绑定所有网卡,写 "127.0.0.1:8080" 更安全(仅本地访问)
  • 如果处理器逻辑稍复杂,别把业务逻辑全塞进匿名函数里,应拆成独立函数,便于测试和复用
  • nil 作为第二个参数虽方便,但隐藏了路由控制权——后续想换多路复用器或加中间件时,得改这里

如何自定义路由和处理器

直接操作 http.ServeMux 实例,能明确控制路由注册行为,也利于单元测试(比如传入 mock 的 *http.ServeMux)。

示例:

func main() {     mux := http.NewServeMux()     mux.HandleFunc("/api/users", usersHandler)     mux.HandleFunc("/health", healthHandler)      server := &http.Server{         Addr:    ":8080",         Handler: mux,     }     server.ListenAndServe() }

关键点:

  • http.ServeMux 只支持前缀匹配(/api 会匹配 /api/users/api/foo/bar),不支持通配符或正则——需要路径参数或 REST 风格路由时,得自己解析 r.URL.Path 或换第三方路由器(如 gorilla/muxchi
  • 处理器函数签名必须是 func(http.ResponseWriter, *http.Request),否则编译报错:cannot use ... (type func()) as type func(http.ResponseWriter, *http.Request) in argument to mux.HandleFunc
  • 多个 HandleFunc 注册相同路径,后注册的会覆盖前一个,无警告

怎么读取请求参数和解析 jsON

GET 查询参数用 r.URL.Query().Get("key"),POST 表单用 r.FormValue("key")(自动调用 ParseForm),json 请求体需手动解码。

典型 JSON 处理片段:

func apiPostHandler(w http.ResponseWriter, r *http.Request) {     if r.Method != "POST" {         http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)         return     }      var data struct {         Name string `json:"name"`         Age  int    `json:"age"`     }     if err := json.NewDecoder(r.Body).Decode(&data); err != nil {         http.Error(w, "Invalid JSON", http.StatusbadRequest)         return     }      w.Header().Set("Content-Type", "application/json")     json.NewEncoder(w).Encode(map[string]string{"status": "ok"}) }

容易踩的坑:

  • r.Bodyio.ReadCloser,只可读一次;若中间件已读过(如日志中间件调用了 r.ParseForm()io.ReadAll(r.Body)),后续再读就会得到空内容
  • 没设 Content-Type: application/json 头时,json.Decode 仍可能成功(只要内容合法),但前端发错类型时服务端难以区分意图
  • http.Error 会立即写响应并返回,但不会终止 handler 函数执行——后面代码仍会运行,可能导致重复写响应(触发 http: multiple response.WriteHeader calls 错误)

怎么安全关闭 HTTP 服务

http.Server.Shutdown 是唯一推荐的优雅关机方式,它会等待活跃连接完成处理后再退出。直接杀进程或用 server.Close() 会导致正在处理的请求被中断。

示例:

server := &http.Server{Addr: ":8080", Handler: mux} go func() {     if err := server.ListenAndServe(); err != http.ErrServerClosed {         log.Fatal(err)     } }()  // 收到 SIGINT/SIGTERM 后触发关机 quit := make(chan os.Signal, 1) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit  ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() server.Shutdown(ctx)

注意点:

  • Shutdown 不会关闭监听 socket 直到所有连接完成,超时时间要结合业务响应时长设(例如有长轮询或流式接口,就得设更长)
  • 若 handler 中有阻塞操作(如未设超时的数据库查询、外部 HTTP 调用),需在对应上下文中传入取消信号,否则 Shutdown 会等满超时时间
  • 标准库不提供内置的请求超时控制,需在 http.Server 中配置 ReadTimeoutWriteTimeout 或用 context 手动控制 handler 内部逻辑

真正难的不是写出能跑的 HTTP 服务,而是让每个 handler 在高并发下不泄露 goroutine、不阻塞线程、不忽略错误返回、不滥用全局状态——这些细节不会出现在入门示例里,但线上出问题时,往往就卡在这几步。

text=ZqhQzanResources