欢迎光临
我们一直在努力

Go net/http 标准库基础

Go net/http 标准库基础

一、知识点总结

1.1 net/http 包的设计理念

Go 的 net/http 包是标准库中最具代表性的设计之一,它遵循接口驱动的设计哲学。整个包围绕两个核心接口展开:

  • Handler 接口:处理 HTTP 请求的核心抽象
  • ResponseWriter + *Request:请求-响应的载体

这种设计的精妙之处在于解耦。你可以用任何自定义类型实现 Handler 接口,然后注册到服务端,不需要继承任何类或框架提供的基类。这是 Go “接口即契约” 思想的典型体现。

1.2 Handler 接口

type Handler interface {
ServeHTTP(w ResponseWriter, r *Request)
}

任何实现了 ServeHTTP 方法的类型都可以作为 HTTP 处理器。标准库提供了 HandlerFunc 类型作为函数适配器,让普通函数也能当作 Handler 使用:

type HandlerFunc func(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 是一个可配置的服务端结构体:

type Server struct {
Addr string // 监听地址,如 ":8080"
Handler Handler // 若nil则使用 DefaultServeMux
ReadTimeout time.Duration // 读取请求超时
WriteTimeout time.Duration // 写入响应超时
IdleTimeout time.Duration // Keep-Alive 连接空闲超时
MaxHeaderBytes int // 请求头最大字节数
}

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 服务

    package main

    import (
    "fmt"
    "log"
    "net/http"
    )

    func main() {
    // 使用 HandleFunc 注册路由,底层自动包装为 HandlerFunc
    http.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 表示使用 DefaultServeMux
    if err := http.ListenAndServe(":8080", nil); err != nil {
    log.Fatal(err)
    }
    }

    示例 2:自定义 Handler 结构体

    package main

    import (
    "fmt"
    "log"
    "net/http"
    "sync/atomic"
    )

    // CounterHandler 是一个自定义 Handler,统计访问次数
    type CounterHandler struct {
    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)
    }

    func main() {
    mux := http.NewServeMux()
    counter := &CounterHandler{}

    // 注册自定义 Handler
    mux.Handle("/counter", counter)
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "访问 /counter 查看计数")
    })

    log.Println("Server on :8080")
    if err := http.ListenAndServe(":8080", mux); err != nil {
    log.Fatal(err)
    }
    }

    示例 3:带超时的生产级 Server 配置

    package main

    import (
    "context"
    "fmt"
    "log"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"
    )

    func main() {
    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 中启动服务
    go func() {
    log.Println("Server starting on :8080")
    if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
    log.Fatalf("Server error: %v", err)
    }
    }()

    // 监听系统信号,实现优雅关闭
    sigChan := make(chan os.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)
    defer cancel()
    if err := server.Shutdown(ctx); err != nil {
    log.Fatalf("Shutdown error: %v", err)
    }
    log.Println("Server gracefully stopped")
    }

    示例 4:ResponseWriter 使用要点演示

    package main

    import (
    "fmt"
    "log"
    "net/http"
    )

    func main() {
    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))
    }


    赞(0)
    未经允许不得转载:171主机测评 » Go net/http 标准库基础
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址