5.1. HTTP 编程

5.1.1. net/http 包

Go 标准库的 net/http 包功能强大,可用于生产环境。

5.1.2. HTTP 服务端

5.1.2.1. 基本用法

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, World!")
    })
    
    http.ListenAndServe(":8080", nil)
}

5.1.2.2. 使用自定义 ServeMux

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

5.1.2.3. ⚠️ 常见陷阱

5.1.2.3.1. 陷阱 1:忽略 http.Server 配置

// ❌ 无超时配置,可能被慢客户端攻击
http.ListenAndServe(":8080", nil)

// ✅ 配置超时
server := &http.Server{
    Addr:         ":8080",
    Handler:      mux,
    ReadTimeout:  5 * time.Second,
    WriteTimeout: 10 * time.Second,
    IdleTimeout:  120 * time.Second,
}
server.ListenAndServe()

5.1.2.3.2. 陷阱 2:未检查 ResponseWriter.Write 错误

// ❌ 忽略错误
func handler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("data"))  // 忽略了返回的错误
}

// ✅ 检查错误
func handler(w http.ResponseWriter, r *http.Request) {
    _, err := w.Write([]byte("data"))
    if err != nil {
        log.Printf("write error: %v", err)
        return
    }
}

5.1.3. HTTP 客户端

5.1.3.1. 基本用法

resp, err := http.Get("https://api.example.com/data")
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)

5.1.3.2. ⚠️ 客户端陷阱

5.1.3.2.1. 陷阱 1:使用默认 Client

// ❌ 默认 Client 无超时
resp, _ := http.Get(url)

// ✅ 配置超时
client := &http.Client{
    Timeout: 10 * time.Second,
}
resp, _ := client.Get(url)

5.1.3.2.2. 陷阱 2:忘记关闭 Response Body

// ❌ 泄漏连接
resp, _ := http.Get(url)
// 忘记 resp.Body.Close()

// ✅ 使用 defer 关闭
resp, err := http.Get(url)
if err != nil {
    return err
}
defer resp.Body.Close()

5.1.3.2.3. 陷阱 3:不读取 Response Body

// ❌ 连接无法复用
resp, _ := http.Get(url)
defer resp.Body.Close()
// 未读取 body,连接无法复用

// ✅ 读取并丢弃 body
resp, _ := http.Get(url)
defer resp.Body.Close()
io.Copy(io.Discard, resp.Body)  // 确保读完

5.1.3.3. 自定义 Transport

transport := &http.Transport{
    MaxIdleConns:        100,
    MaxIdleConnsPerHost: 10,
    IdleConnTimeout:     90 * time.Second,
    TLSHandshakeTimeout: 10 * time.Second,
}

client := &http.Client{
    Transport: transport,
    Timeout:   30 * time.Second,
}

5.1.4. 中间件模式

type Middleware func(http.Handler) http.Handler

// 日志中间件
func LoggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        next.ServeHTTP(w, r)
        log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
    })
}

// 认证中间件
func AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("Authorization")
        if !validateToken(token) {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }
        next.ServeHTTP(w, r)
    })
}

// 链式使用
func main() {
    handler := LoggingMiddleware(AuthMiddleware(http.HandlerFunc(myHandler)))
    http.ListenAndServe(":8080", handler)
}

5.1.5. 优雅关闭

func main() {
    server := &http.Server{Addr: ":8080", Handler: mux}
    
    // 启动服务器
    go func() {
        if err := server.ListenAndServe(); err != http.ErrServerClosed {
            log.Fatal(err)
        }
    }()
    
    // 等待中断信号
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit
    
    // 优雅关闭
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()
    
    if err := server.Shutdown(ctx); err != nil {
        log.Fatal(err)
    }
    
    log.Println("Server stopped")
}

5.1.6. HTTP/2 支持

// Go 默认支持 HTTP/2(HTTPS 时)
server := &http.Server{
    Addr:    ":443",
    Handler: mux,
}
server.ListenAndServeTLS("cert.pem", "key.pem")

5.1.7. 参考资源