Go HTTP

Go 语言在标准库 net/http 包中提供了完整的 HTTP 客户端和服务器实现。Go 的 HTTP 包设计简洁高效,非常适合构建 Web 服务、API 服务器和微服务。

下面是一个示例:

package main

import (
    "encoding/json"
    "fmt"
    "html/template"
    "io"
    "log"
    "mime/multipart"
    "net/http"
    "os"
    "path/filepath"
    "strconv"
    "strings"
    "sync"
    "time"
)

// 1. 基本 HTTP 服务器
func basicHTTPServer() {
    fmt.Println("=== 基本 HTTP 服务器 ===")

    // 基本路由处理
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, `
        <h1>Go HTTP 服务器</h1>
        <ul>
            <li><a href="/hello">Hello</a></li>
            <li><a href="/time">当前时间</a></li>
            <li><a href="/api/users">用户 API</a></li>
            <li><a href="/form">表单示例</a></li>
        </ul>
        `)
    })

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

    http.HandleFunc("/time", func(w http.ResponseWriter, r *http.Request) {
        currentTime := time.Now().Format("2006-01-02 15:04:05")
        fmt.Fprintf(w, "当前时间: %s", currentTime)
    })

    // 启动服务器
    port := ":8080"
    fmt.Printf("服务器启动在 http://localhost%s\n", port)
    log.Fatal(http.ListenAndServe(port, nil))
}

// 2. RESTful API 服务器
type User struct {
    ID       int    `json:"id"`
    Name     string `json:"name"`
    Email    string `json:"email"`
    CreateAt string `json:"create_at"`
}

type UserStore struct {
    users map[int]User
    mu    sync.RWMutex
    nextID int
}

func NewUserStore() *UserStore {
    return &UserStore{
        users: make(map[int]User),
        nextID: 1,
    }
}

func (s *UserStore) CreateUser(user User) User {
    s.mu.Lock()
    defer s.mu.Unlock()

    user.ID = s.nextID
    user.CreateAt = time.Now().Format(time.RFC3339)
    s.users[user.ID] = user
    s.nextID++

    return user
}

func (s *UserStore) GetUser(id int) (User, bool) {
    s.mu.RLock()
    defer s.mu.RUnlock()

    user, exists := s.users[id]
    return user, exists
}

func (s *UserStore) GetAllUsers() []User {
    s.mu.RLock()
    defer s.mu.RUnlock()

    users := make([]User, 0, len(s.users))
    for _, user := range s.users {
        users = append(users, user)
    }
    return users
}

func (s *UserStore) UpdateUser(id int, updated User) (User, bool) {
    s.mu.Lock()
    defer s.mu.Unlock()

    if _, exists := s.users[id]; !exists {
        return User{}, false
    }

    updated.ID = id
    updated.CreateAt = s.users[id].CreateAt
    s.users[id] = updated
    return updated, true
}

func (s *UserStore) DeleteUser(id int) bool {
    s.mu.Lock()
    defer s.mu.Unlock()

    if _, exists := s.users[id]; !exists {
        return false
    }

    delete(s.users, id)
    return true
}

type APIServer struct {
    store *UserStore
}

func NewAPIServer() *APIServer {
    return &APIServer{
        store: NewUserStore(),
    }
}

func (s *APIServer) Start(port string) {
    mux := http.NewServeMux()

    // 用户 API 路由
    mux.HandleFunc("GET /api/users", s.getUsers)
    mux.HandleFunc("GET /api/users/{id}", s.getUser)
    mux.HandleFunc("POST /api/users", s.createUser)
    mux.HandleFunc("PUT /api/users/{id}", s.updateUser)
    mux.HandleFunc("DELETE /api/users/{id}", s.deleteUser)

    // 健康检查
    mux.HandleFunc("GET /health", s.healthCheck)

    // 静态文件服务
    mux.Handle("/static/", http.StripPrefix("/static/",
        http.FileServer(http.Dir("./static"))))

    // 添加中间件
    handler := s.loggingMiddleware(s.jsonMiddleware(mux))

    fmt.Printf("REST API 服务器启动在 http://localhost%s\n", port)
    log.Fatal(http.ListenAndServe(port, handler))
}

func (s *APIServer) 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 (s *APIServer) jsonMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        next.ServeHTTP(w, r)
    })
}

func (s *APIServer) healthCheck(w http.ResponseWriter, r *http.Request) {
    json.NewEncoder(w).Encode(map[string]string{
        "status":    "healthy",
        "timestamp": time.Now().Format(time.RFC3339),
    })
}

func (s *APIServer) getUsers(w http.ResponseWriter, r *http.Request) {
    users := s.store.GetAllUsers()
    json.NewEncoder(w).Encode(users)
}

func (s *APIServer) getUser(w http.ResponseWriter, r *http.Request) {
    idStr := strings.TrimPrefix(r.URL.Path, "/api/users/")
    id, err := strconv.Atoi(idStr)
    if err != nil {
        http.Error(w, `{"error": "Invalid user ID"}`, http.StatusBadRequest)
        return
    }

    user, exists := s.store.GetUser(id)
    if !exists {
        http.Error(w, `{"error": "User not found"}`, http.StatusNotFound)
        return
    }

    json.NewEncoder(w).Encode(user)
}

func (s *APIServer) createUser(w http.ResponseWriter, r *http.Request) {
    var user User
    if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
        http.Error(w, `{"error": "Invalid JSON"}`, http.StatusBadRequest)
        return
    }

    if user.Name == "" || user.Email == "" {
        http.Error(w, `{"error": "Name and email are required"}`, http.StatusBadRequest)
        return
    }

    created := s.store.CreateUser(user)
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(created)
}

func (s *APIServer) updateUser(w http.ResponseWriter, r *http.Request) {
    idStr := strings.TrimPrefix(r.URL.Path, "/api/users/")
    id, err := strconv.Atoi(idStr)
    if err != nil {
        http.Error(w, `{"error": "Invalid user ID"}`, http.StatusBadRequest)
        return
    }

    var user User
    if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
        http.Error(w, `{"error": "Invalid JSON"}`, http.StatusBadRequest)
        return
    }

    updated, exists := s.store.UpdateUser(id, user)
    if !exists {
        http.Error(w, `{"error": "User not found"}`, http.StatusNotFound)
        return
    }

    json.NewEncoder(w).Encode(updated)
}

func (s *APIServer) deleteUser(w http.ResponseWriter, r *http.Request) {
    idStr := strings.TrimPrefix(r.URL.Path, "/api/users/")
    id, err := strconv.Atoi(idStr)
    if err != nil {
        http.Error(w, `{"error": "Invalid user ID"}`, http.StatusBadRequest)
        return
    }

    if !s.store.DeleteUser(id) {
        http.Error(w, `{"error": "User not found"}`, http.StatusNotFound)
        return
    }

    w.WriteHeader(http.StatusNoContent)
}

// 3. 模板渲染示例
type PageData struct {
    Title   string
    Users   []User
    Time    string
}

func templateServer() {
    fmt.Println("=== 模板服务器 ===")

    // 创建模板
    tmpl := template.Must(template.New("").Parse(`
    <!DOCTYPE html>
    <html>
    <head>
        <title>{{.Title}}</title>
        <style>
            body { font-family: Arial, sans-serif; margin: 40px; }
            .user { border: 1px solid #ddd; padding: 10px; margin: 10px 0; }
            .time { color: #666; font-size: 0.9em; }
        </style>
    </head>
    <body>
        <h1>{{.Title}}</h1>
        <div class="time">服务器时间: {{.Time}}</div>

        <h2>用户列表</h2>
        {{range .Users}}
        <div class="user">
            <strong>{{.Name}}</strong> (ID: {{.ID}})<br>
            Email: {{.Email}}<br>
            创建时间: {{.CreateAt}}
        </div>
        {{else}}
        <p>暂无用户</p>
        {{end}}
    </body>
    </html>
    `))

    store := NewUserStore()

    // 添加示例数据
    store.CreateUser(User{Name: "张三", Email: "zhang@example.com"})
    store.CreateUser(User{Name: "李四", Email: "li@example.com"})

    http.HandleFunc("/template", func(w http.ResponseWriter, r *http.Request) {
        data := PageData{
            Title: "Go 模板示例",
            Users: store.GetAllUsers(),
            Time:  time.Now().Format("2006-01-02 15:04:05"),
        }

        w.Header().Set("Content-Type", "text/html; charset=utf-8")
        if err := tmpl.Execute(w, data); err != nil {
            http.Error(w, "模板渲染错误", http.StatusInternalServerError)
        }
    })

    port := ":8081"
    fmt.Printf("模板服务器启动在 http://localhost%s/template\n", port)
    log.Fatal(http.ListenAndServe(port, nil))
}

// 4. 文件上传示例
func uploadServer() {
    fmt.Println("=== 文件上传服务器 ===")

    // 创建上传目录
    os.MkdirAll("uploads", 0755)

    http.HandleFunc("/upload", func(w http.ResponseWriter, r *http.Request) {
        if r.Method == "GET" {
            // 显示上传表单
            fmt.Fprint(w, `
            <!DOCTYPE html>
            <html>
            <head><title>文件上传</title></head>
            <body>
                <h1>文件上传示例</h1>
                <form action="/upload" method="post" enctype="multipart/form-data">
                    <input type="file" name="file" multiple>
                    <button type="submit">上传</button>
                </form>
            </body>
            </html>
            `)
            return
        }

        if r.Method == "POST" {
            // 解析多部分表单
            err := r.ParseMultipartForm(10 << 20) // 10MB
            if err != nil {
                http.Error(w, "文件太大", http.StatusBadRequest)
                return
            }

            files := r.MultipartForm.File["file"]
            var results []string

            for _, fileHeader := range files {
                result, err := handleUpload(fileHeader)
                if err != nil {
                    results = append(results, fmt.Sprintf("错误: %s - %v", fileHeader.Filename, err))
                } else {
                    results = append(results, result)
                }
            }

            w.Header().Set("Content-Type", "text/html")
            fmt.Fprintf(w, "<h1>上传结果</h1><ul>")
            for _, result := range results {
                fmt.Fprintf(w, "<li>%s</li>", result)
            }
            fmt.Fprint(w, "</ul><a href='/upload'>继续上传</a>")
        }
    })

    // 文件下载
    http.HandleFunc("/download/", func(w http.ResponseWriter, r *http.Request) {
        filename := strings.TrimPrefix(r.URL.Path, "/download/")
        filepath := filepath.Join("uploads", filename)

        // 安全检查
        if strings.Contains(filename, "..") {
            http.Error(w, "非法文件名", http.StatusBadRequest)
            return
        }

        w.Header().Set("Content-Disposition", "attachment; filename="+filename)
        http.ServeFile(w, r, filepath)
    })

    port := ":8082"
    fmt.Printf("文件服务器启动在 http://localhost%s/upload\n", port)
    log.Fatal(http.ListenAndServe(port, nil))
}

func handleUpload(fileHeader *multipart.FileHeader) (string, error) {
    file, err := fileHeader.Open()
    if err != nil {
        return "", err
    }
    defer file.Close()

    // 创建目标文件
    dstPath := filepath.Join("uploads", fileHeader.Filename)
    dst, err := os.Create(dstPath)
    if err != nil {
        return "", err
    }
    defer dst.Close()

    // 复制文件内容
    if _, err := io.Copy(dst, file); err != nil {
        return "", err
    }

    return fmt.Sprintf("文件上传成功: %s (大小: %d bytes)",
        fileHeader.Filename, fileHeader.Size), nil
}

// 5. HTTP 客户端示例
func httpClientExamples() {
    fmt.Println("=== HTTP 客户端示例 ===")

    // 1. GET 请求
    fmt.Println("\n1. GET 请求示例:")
    resp, err := http.Get("https://httpbin.org/get")
    if err != nil {
        log.Printf("GET 请求失败: %v", err)
    } else {
        defer resp.Body.Close()
        body, _ := io.ReadAll(resp.Body)
        fmt.Printf("状态码: %d\n", resp.StatusCode)
        fmt.Printf("响应头: %v\n", resp.Header)
        fmt.Printf("响应体: %s\n", string(body[:100])) // 只显示前100字符
    }

    // 2. POST 请求 (JSON)
    fmt.Println("\n2. POST 请求示例:")
    data := map[string]string{"name": "Go HTTP Client", "value": "test"}
    jsonData, _ := json.Marshal(data)

    resp, err = http.Post("https://httpbin.org/post", "application/json", strings.NewReader(string(jsonData)))
    if err != nil {
        log.Printf("POST 请求失败: %v", err)
    } else {
        defer resp.Body.Close()
        body, _ := io.ReadAll(resp.Body)
        fmt.Printf("状态码: %d\n", resp.StatusCode)
        fmt.Printf("响应: %s\n", string(body[:200]))
    }

    // 3. 自定义请求
    fmt.Println("\n3. 自定义请求示例:")
    client := &http.Client{
        Timeout: 10 * time.Second,
    }

    req, err := http.NewRequest("GET", "https://httpbin.org/headers", nil)
    if err != nil {
        log.Printf("创建请求失败: %v", err)
        return
    }

    req.Header.Set("User-Agent", "Go-HTTP-Client/1.0")
    req.Header.Set("Authorization", "Bearer fake-token")

    resp, err = client.Do(req)
    if err != nil {
        log.Printf("请求执行失败: %v", err)
        return
    }
    defer resp.Body.Close()

    var result map[string]interface{}
    if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
        log.Printf("JSON 解析失败: %v", err)
        return
    }

    fmt.Printf("自定义头信息: %v\n", result["headers"])
}

// 6. 中间件链示例
type Middleware func(http.HandlerFunc) http.HandlerFunc

func ChainMiddleware(h http.HandlerFunc, middlewares ...Middleware) http.HandlerFunc {
    for i := len(middlewares) - 1; i >= 0; i-- {
        h = middlewares[i](h)
    }
    return h
}

func LoggingMiddleware(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        log.Printf("开始处理 %s %s", r.Method, r.URL.Path)

        // 创建自定义 ResponseWriter 来捕获状态码
        rw := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}

        next(rw, r)

        log.Printf("完成处理 %s %s - 状态码: %d, 耗时: %v",
            r.Method, r.URL.Path, rw.statusCode, time.Since(start))
    }
}

func AuthMiddleware(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("Authorization")
        if token != "Bearer secret-token" {
            http.Error(w, `{"error": "未授权"}`, http.StatusUnauthorized)
            return
        }
        next(w, r)
    }
}

type responseWriter struct {
    http.ResponseWriter
    statusCode int
}

func (rw *responseWriter) WriteHeader(code int) {
    rw.statusCode = code
    rw.ResponseWriter.WriteHeader(code)
}

func middlewareExample() {
    fmt.Println("=== 中间件示例 ===")

    protectedHandler := func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(map[string]string{
            "message": "这是受保护的内容",
            "user":    "认证用户",
        })
    }

    // 应用中间件链
    handler := ChainMiddleware(protectedHandler, LoggingMiddleware, AuthMiddleware)

    http.HandleFunc("/protected", handler)

    port := ":8083"
    fmt.Printf("中间件示例启动在 http://localhost%s/protected\n", port)
    fmt.Println("使用 Header: Authorization: Bearer secret-token")
    log.Fatal(http.ListenAndServe(port, nil))
}

func main() {
    if len(os.Args) < 2 {
        fmt.Println("HTTP 示例用法:")
        fmt.Println("  基本服务器: go run http.go basic")
        fmt.Println("  API服务器: go run http.go api")
        fmt.Println("  模板服务器: go run http.go template")
        fmt.Println("  文件服务器: go run http.go upload")
        fmt.Println("  客户端示例: go run http.go client")
        fmt.Println("  中间件示例: go run http.go middleware")
        return
    }

    mode := os.Args[1]

    switch mode {
    case "basic":
        basicHTTPServer()
    case "api":
        server := NewAPIServer()
        server.Start(":8080")
    case "template":
        templateServer()
    case "upload":
        uploadServer()
    case "client":
        httpClientExamples()
    case "middleware":
        middlewareExample()
    default:
        fmt.Println("未知模式")
    }
}

HTTP 编程关键要点

  1. ​ 核心组件 ​

​ 处理器 (Handler)​​:实现 ServeHTTP 方法

​ 多路复用器 (Mux)​​:路由请求到处理器

​ 响应编写器 (ResponseWriter)​​:构建 HTTP 响应

​ 请求 (Request)​​:包含请求信息

  1. ​ 重要特性 ​

​ 并发安全 ​:每个请求在独立 goroutine 中处理

​ 中间件支持 ​:灵活的请求处理管道

​ 自动路由 ​:支持模式匹配和参数提取

​HTTPS 支持 ​:内置 TLS/SSL 支持

  1. ​ 最佳实践 ​

​ 使用中间件 ​:日志、认证、限流等

​ 错误处理 ​:适当的 HTTP 状态码和错误信息

​ 超时控制 ​:设置合理的读写超时

​ 资源清理 ​:及时关闭响应体

​ 安全考虑 ​:输入验证、CORS、CSRF 防护

Go 的 HTTP 包功能强大且易于使用,是构建 Web 服务和 API 的理想选择。

通关密语:http