强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Go 语言完全指南 / 20 - HTTP:net/http 包、客户端、服务器、中间件

20 - HTTP

20.1 HTTP 客户端

基本请求

package main

import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "time"
)

func main() {
    // GET 请求
    resp, err := http.Get("https://httpbin.org/get")
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    body, _ := io.ReadAll(resp.Body)
    fmt.Println("状态码:", resp.StatusCode)
    fmt.Println("响应:", string(body))

    // POST JSON
    data := map[string]string{"name": "Alice", "email": "[email protected]"}
    jsonData, _ := json.Marshal(data)

    resp2, err := http.Post(
        "https://httpbin.org/post",
        "application/json",
        bytes.NewReader(jsonData),
    )
    if err != nil {
        panic(err)
    }
    defer resp2.Body.Close()
    body2, _ := io.ReadAll(resp2.Body)
    fmt.Println(string(body2))

    // 带 header 的请求
    req, _ := http.NewRequest("GET", "https://httpbin.org/headers", nil)
    req.Header.Set("Authorization", "Bearer token123")
    req.Header.Set("X-Request-ID", "req-abc")
    client := &http.Client{Timeout: 10 * time.Second}
    resp3, _ := client.Do(req)
    defer resp3.Body.Close()
    body3, _ := io.ReadAll(resp3.Body)
    fmt.Println(string(body3))
}

自定义 Client

// 创建带超时的客户端
client := &http.Client{
    Timeout: 30 * time.Second,
    Transport: &http.Transport{
        MaxIdleConns:        100,
        MaxIdleConnsPerHost: 10,
        IdleConnTimeout:     90 * time.Second,
        TLSHandshakeTimeout: 10 * time.Second,
        DisableCompression:  false,
    },
}

// 带 context 的请求
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

req, _ := http.NewRequestWithContext(ctx, "GET", "https://example.com", nil)
resp, err := client.Do(req)

20.2 HTTP 服务器

基本服务器

package main

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

func main() {
    // 注册路由
    http.HandleFunc("/", homeHandler)
    http.HandleFunc("/hello", helloHandler)
    http.HandleFunc("/api/users", usersHandler)

    // 启动服务器
    log.Println("服务器启动在 :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "欢迎来到首页!")
}

func helloHandler(w http.ResponseWriter, r *http.Request) {
    name := r.URL.Query().Get("name")
    if name == "" {
        name = "World"
    }
    fmt.Fprintf(w, "Hello, %s!", name)
}

func usersHandler(w http.ResponseWriter, r *http.Request) {
    switch r.Method {
    case http.MethodGet:
        users := []map[string]any{
            {"id": 1, "name": "Alice"},
            {"id": 2, "name": "Bob"},
        }
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(users)
    case http.MethodPost:
        var user map[string]any
        json.NewDecoder(r.Body).Decode(&user)
        w.WriteHeader(http.StatusCreated)
        json.NewEncoder(w).Encode(user)
    default:
        w.WriteHeader(http.StatusMethodNotAllowed)
    }
}

ServeMux(Go 1.22+ 增强路由)

func main() {
    mux := http.NewServeMux()

    // Go 1.22+ 支持方法和路径参数
    mux.HandleFunc("GET /api/users", listUsers)
    mux.HandleFunc("POST /api/users", createUser)
    mux.HandleFunc("GET /api/users/{id}", getUser)
    mux.HandleFunc("DELETE /api/users/{id}", deleteUser)

    server := &http.Server{
        Addr:         ":8080",
        Handler:      mux,
        ReadTimeout:  10 * time.Second,
        WriteTimeout: 10 * time.Second,
        IdleTimeout:  60 * time.Second,
    }

    log.Fatal(server.ListenAndServe())
}

func getUser(w http.ResponseWriter, r *http.Request) {
    id := r.PathValue("id") // Go 1.22+
    fmt.Fprintf(w, "User ID: %s", id)
}

优雅关闭

func main() {
    srv := &http.Server{Addr: ":8080"}

    // 监听系统信号
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

    go func() {
        <-quit
        log.Println("正在关闭服务器...")
        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
        defer cancel()
        srv.Shutdown(ctx)
    }()

    log.Println("服务器启动在 :8080")
    if err := srv.ListenAndServe(); err != http.ErrServerClosed {
        log.Fatal(err)
    }
    log.Println("服务器已关闭")
}

20.3 中间件模式

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 %s %v", r.RemoteAddr, r.Method, r.URL.Path, time.Since(start))
    })
}

// CORS 中间件
func CORSMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Access-Control-Allow-Origin", "*")
        w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
        w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
        if r.Method == "OPTIONS" {
            w.WriteHeader(http.StatusOK)
            return
        }
        next.ServeHTTP(w, r)
    })
}

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

// Recovery 中间件
func RecoveryMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if err := recover(); err != nil {
                log.Printf("panic: %v", err)
                http.Error(w, "Internal Server Error", http.StatusInternalServerError)
            }
        }()
        next.ServeHTTP(w, r)
    })
}

// 中间件链
func Chain(handler http.Handler, middlewares ...Middleware) http.Handler {
    for i := len(middlewares) - 1; i >= 0; i-- {
        handler = middlewares[i](handler)
    }
    return handler
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", homeHandler)

    handler := Chain(mux,
        RecoveryMiddleware,
        LoggingMiddleware,
        CORSMiddleware,
    )

    http.ListenAndServe(":8080", handler)
}

20.4 响应写入工具

// JSON 响应
func respondJSON(w http.ResponseWriter, status int, data any) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(status)
    json.NewEncoder(w).Encode(data)
}

// 错误响应
func respondError(w http.ResponseWriter, status int, message string) {
    respondJSON(w, status, map[string]string{"error": message})
}

// 读取 JSON 请求体
func readJSON(r *http.Request, dst any) error {
    dec := json.NewDecoder(r.Body)
    dec.DisallowUnknownFields()
    return dec.Decode(dst)
}

// 使用示例
func createUserHandler(w http.ResponseWriter, r *http.Request) {
    var req struct {
        Name  string `json:"name"`
        Email string `json:"email"`
    }
    if err := readJSON(r, &req); err != nil {
        respondError(w, http.StatusBadRequest, "Invalid request body")
        return
    }
    // 创建用户...
    respondJSON(w, http.StatusCreated, map[string]any{
        "id":    1,
        "name":  req.Name,
        "email": req.Email,
    })
}

🏢 业务场景

  1. REST API:http.HandleFunc + JSON 序列化构建 API
  2. 微服务通信:HTTP 客户端调用下游服务
  3. 反向代理:httputil.ReverseProxy 实现反向代理
  4. 文件上传:multipart.Reader 处理文件上传
  5. 健康检查:HTTP handler 实现 /health 端点

📖 扩展阅读