← 返回目录

第五章:网络编程

HTTP 服务器、REST API 与 JSON

1 net/http 基础

Go 标准库 net/http 提供了功能完备的 HTTP 服务器和客户端,无需任何第三方框架即可构建生产级 Web 服务。

最小 HTTP 服务器

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, Go!")
    })

    http.HandleFunc("/api/hello", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        fmt.Fprintf(w, `{"message": "Hello, World!"}`)
    })

    fmt.Println("Server running on :8080")
    http.ListenAndServe(":8080", nil)
}

请求方法判断与路由

package main

import (
    "fmt"
    "net/http"
)

func userHandler(w http.ResponseWriter, r *http.Request) {
    switch r.Method {
    case http.MethodGet:
        fmt.Fprintf(w, "获取用户列表")
    case http.MethodPost:
        fmt.Fprintf(w, "创建用户")
    default:
        http.Error(w, "方法不允许", http.StatusMethodNotAllowed)
    }
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/users", userHandler)
    mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        fmt.Fprintf(w, "OK")
    })

    server := &http.Server{
        Addr:    ":8080",
        Handler: mux,
    }
    fmt.Println("Server running on :8080")
    server.ListenAndServe()
}

🔄 Go net/http vs Node.js Express vs Python Flask

// Go:标准库即可,零依赖
http.HandleFunc("/api/users", handler)
http.ListenAndServe(":8080", nil)
# Node.js Express:需要安装框架
# const express = require('express');
# const app = express();
# app.get('/api/users', handler);
# app.listen(8080);

# Python Flask:需要安装框架
# from flask import Flask
# app = Flask(__name__)
# @app.route('/api/users')
# def handler(): ...
# app.run(port=8080)

Go 优势:标准库自带高性能 HTTP 服务器,无需框架依赖,编译为单一二进制文件部署极其简单。

2 JSON 处理

Go 通过 encoding/json 包提供 JSON 编解码支持,使用 struct tag 控制字段映射。

结构体与 JSON 映射

type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email,omitempty"`
    Age   int    `json:"age,omitempty"`
}

type ApiResponse struct {
    Code    int         `json:"code"`
    Message string      `json:"message"`
    Data    interface{} `json:"data,omitempty"`
}

Marshal / Unmarshal

package main

import (
    "encoding/json"
    "fmt"
)

type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email,omitempty"`
}

func main() {
    // 结构体 → JSON 字符串(Marshal)
    user := User{ID: 1, Name: "张三", Email: "zhangsan@example.com"}
    data, err := json.Marshal(user)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(data))
    // {"id":1,"name":"张三","email":"zhangsan@example.com"}

    // 美化输出
    pretty, _ := json.MarshalIndent(user, "", "  ")
    fmt.Println(string(pretty))

    // JSON 字符串 → 结构体(Unmarshal)
    jsonStr := `{"id":2,"name":"李四","email":"lisi@example.com"}`
    var u User
    err = json.Unmarshal([]byte(jsonStr), &u)
    if err != nil {
        panic(err)
    }
    fmt.Printf("ID: %d, Name: %s\n", u.ID, u.Name)

    // 解析到 map(不确定结构时)
    var result map[string]interface{}
    json.Unmarshal([]byte(jsonStr), &result)
    fmt.Println(result["name"]) // 李四
}

流式编解码:NewEncoder / NewDecoder

// 在 HTTP handler 中直接编码到 ResponseWriter
func getUser(w http.ResponseWriter, r *http.Request) {
    user := User{ID: 1, Name: "张三", Email: "zhangsan@example.com"}
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(user)
}

// 从请求体直接解码
func createUser(w http.ResponseWriter, r *http.Request) {
    var user User
    if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
        http.Error(w, "无效的 JSON", http.StatusBadRequest)
        return
    }
    defer r.Body.Close()
    fmt.Fprintf(w, "收到用户: %s", user.Name)
}

struct tag 常用选项:`json:"field_name"` 重命名字段;`json:"field,omitempty"` 零值时省略;`json:"-"` 忽略字段。

3 HTTP 客户端

简单 GET 请求

package main

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

func main() {
    resp, err := http.Get("https://api.example.com/users")
    if err != nil {
        fmt.Println("请求失败:", err)
        return
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        fmt.Println("读取失败:", err)
        return
    }

    fmt.Println("状态码:", resp.StatusCode)
    fmt.Println("响应:", string(body))
}

POST 请求发送 JSON

package main

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

func main() {
    user := map[string]interface{}{
        "name":  "王五",
        "email": "wangwu@example.com",
    }
    jsonData, _ := json.Marshal(user)

    resp, err := http.Post(
        "https://api.example.com/users",
        "application/json",
        bytes.NewBuffer(jsonData),
    )
    if err != nil {
        fmt.Println("请求失败:", err)
        return
    }
    defer resp.Body.Close()

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

自定义 Client 与超时

package main

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

func main() {
    client := &http.Client{
        Timeout: 10 * time.Second,
    }

    req, err := http.NewRequest("GET", "https://api.example.com/users", nil)
    if err != nil {
        panic(err)
    }

    req.Header.Set("Authorization", "Bearer your-token-here")
    req.Header.Set("Accept", "application/json")

    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("请求失败:", err)
        return
    }
    defer resp.Body.Close()

    body, _ := io.ReadAll(resp.Body)
    fmt.Printf("状态码: %d\n响应: %s\n", resp.StatusCode, body)
}

重要:始终记得 defer resp.Body.Close(),否则会导致连接泄漏。生产环境建议使用自定义 Client 而非默认的 http.DefaultClient(它没有超时限制)。

4 REST API 实践

以下是一个完整的用户 CRUD API 示例,展示路由分发、JSON 请求/响应处理的最佳实践。

package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "strconv"
    "strings"
    "sync"
)

type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

var (
    users  = make(map[int]User)
    nextID = 1
    mu     sync.Mutex
)

func jsonResponse(w http.ResponseWriter, data interface{}, status int) {
    w.Header().Set("Content-Type", "application/json; charset=utf-8")
    w.WriteHeader(status)
    json.NewEncoder(w).Encode(data)
}

func jsonError(w http.ResponseWriter, msg string, status int) {
    jsonResponse(w, map[string]string{"error": msg}, status)
}

func usersHandler(w http.ResponseWriter, r *http.Request) {
    switch r.Method {
    case http.MethodGet:
        mu.Lock()
        list := make([]User, 0, len(users))
        for _, u := range users {
            list = append(list, u)
        }
        mu.Unlock()
        jsonResponse(w, list, http.StatusOK)

    case http.MethodPost:
        var u User
        if err := json.NewDecoder(r.Body).Decode(&u); err != nil {
            jsonError(w, "无效的 JSON", http.StatusBadRequest)
            return
        }
        if u.Name == "" || u.Email == "" {
            jsonError(w, "name 和 email 为必填字段", http.StatusUnprocessableEntity)
            return
        }
        mu.Lock()
        u.ID = nextID
        nextID++
        users[u.ID] = u
        mu.Unlock()
        jsonResponse(w, u, http.StatusCreated)

    default:
        jsonError(w, "方法不允许", http.StatusMethodNotAllowed)
    }
}

func userHandler(w http.ResponseWriter, r *http.Request) {
    idStr := strings.TrimPrefix(r.URL.Path, "/api/users/")
    id, err := strconv.Atoi(idStr)
    if err != nil {
        jsonError(w, "无效的用户 ID", http.StatusBadRequest)
        return
    }

    switch r.Method {
    case http.MethodGet:
        mu.Lock()
        u, ok := users[id]
        mu.Unlock()
        if !ok {
            jsonError(w, "用户不存在", http.StatusNotFound)
            return
        }
        jsonResponse(w, u, http.StatusOK)

    case http.MethodPut:
        var input User
        if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
            jsonError(w, "无效的 JSON", http.StatusBadRequest)
            return
        }
        mu.Lock()
        if _, ok := users[id]; !ok {
            mu.Unlock()
            jsonError(w, "用户不存在", http.StatusNotFound)
            return
        }
        input.ID = id
        users[id] = input
        mu.Unlock()
        jsonResponse(w, input, http.StatusOK)

    case http.MethodDelete:
        mu.Lock()
        if _, ok := users[id]; !ok {
            mu.Unlock()
            jsonError(w, "用户不存在", http.StatusNotFound)
            return
        }
        delete(users, id)
        mu.Unlock()
        jsonResponse(w, map[string]string{"message": "删除成功"}, http.StatusOK)

    default:
        jsonError(w, "方法不允许", http.StatusMethodNotAllowed)
    }
}

func main() {
    http.HandleFunc("/api/users", usersHandler)
    http.HandleFunc("/api/users/", userHandler)

    fmt.Println("REST API running on :8080")
    http.ListenAndServe(":8080", nil)
}

测试命令:

# 创建用户
curl -X POST http://localhost:8080/api/users \
  -H "Content-Type: application/json" \
  -d '{"name":"张三","email":"zhangsan@example.com"}'

# 获取用户列表
curl http://localhost:8080/api/users

# 获取单个用户
curl http://localhost:8080/api/users/1

# 更新用户
curl -X PUT http://localhost:8080/api/users/1 \
  -H "Content-Type: application/json" \
  -d '{"name":"张三丰","email":"zsf@example.com"}'

# 删除用户
curl -X DELETE http://localhost:8080/api/users/1

5 中间件

Go 的中间件是一个接收 http.Handler 并返回 http.Handler 的函数,通过链式组合实现请求预处理。

中间件基本模式

type Middleware func(http.Handler) http.Handler

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

日志中间件

package main

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

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

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 == http.MethodOptions {
            w.WriteHeader(http.StatusNoContent)
            return
        }
        next.ServeHTTP(w, r)
    })
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/api/hello", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, `{"message": "Hello!"}`)
    })

    handler := loggingMiddleware(corsMiddleware(mux))

    fmt.Println("Server running on :8080")
    http.ListenAndServe(":8080", handler)
}

认证中间件

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, `{"error":"缺少认证令牌"}`, http.StatusUnauthorized)
            return
        }

        if !strings.HasPrefix(token, "Bearer ") {
            http.Error(w, `{"error":"无效的令牌格式"}`, http.StatusUnauthorized)
            return
        }

        tokenStr := strings.TrimPrefix(token, "Bearer ")
        if !validateToken(tokenStr) {
            http.Error(w, `{"error":"令牌无效或已过期"}`, http.StatusUnauthorized)
            return
        }

        next.ServeHTTP(w, r)
    })
}

func validateToken(token string) bool {
    return token == "valid-secret-token"
}

// 使用:只保护需要认证的路由
// protectedMux := http.NewServeMux()
// protectedMux.HandleFunc("/api/admin", adminHandler)
// http.Handle("/api/admin", authMiddleware(protectedMux))

中间件链顺序很重要:外层中间件先执行。典型顺序为:恢复(Recovery) → 日志 → CORS → 认证 → 业务 Handler。

6 本章要点

🌐 HTTP 服务器

  • http.HandleFunc 注册路由
  • http.ListenAndServe 启动服务
  • http.NewServeMux 自定义路由器

📦 JSON 处理

  • json.Marshal / json.Unmarshal
  • • struct tag 控制字段映射
  • json.NewEncoder / json.NewDecoder

🔗 HTTP 客户端

  • http.Get / http.Post 快速请求
  • http.NewRequest 自定义请求
  • • 自定义 http.Client 设置超时

🔌 REST API

  • • 按 Method 分发处理逻辑
  • • 统一 JSON 响应格式
  • • CRUD 路由约定

🧩 中间件

  • • 函数签名:func(http.Handler) http.Handler
  • • 日志、CORS、认证中间件
  • • 链式组合,顺序敏感

💡 最佳实践

  • • 始终 defer resp.Body.Close()
  • • 生产环境设置请求超时
  • • 使用 http.Error 返回错误