HTTP 服务器

37.1 HTTP 服务器基础

Go 通过 net/http 包提供了强大的 HTTP 服务器功能。

最简单的服务器

package main

import (
    "fmt"
    "net/http"
)

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

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

多个路由

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", homeHandler)
    http.HandleFunc("/about", aboutHandler)
    http.HandleFunc("/contact", contactHandler)

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

func homeHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "首页")
}

func aboutHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "关于我们")
}

func contactHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "联系我们")
}

37.2 请求处理

获取请求信息

package main

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

func handler(w http.ResponseWriter, r *http.Request) {
    // 请求方法
    fmt.Fprintf(w, "方法: %s\n", r.Method)

    // 请求路径
    fmt.Fprintf(w, "路径: %s\n", r.URL.Path)

    // 查询参数
    query := r.URL.Query()
    fmt.Fprintf(w, "查询参数: %v\n", query)

    // 请求头
    fmt.Fprintf(w, "User-Agent: %s\n", r.Header.Get("User-Agent"))

    // 请求体
    body, _ := io.ReadAll(r.Body)
    fmt.Fprintf(w, "请求体: %s\n", body)

    // 客户端地址
    fmt.Fprintf(w, "客户端地址: %s\n", r.RemoteAddr)
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

处理表单数据

package main

import (
    "fmt"
    "net/http"
)

func formHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != "POST" {
        http.Error(w, "方法不允许", http.StatusMethodNotAllowed)
        return
    }

    // 解析表单
    r.ParseForm()

    // 获取表单值
    username := r.FormValue("username")
    password := r.FormValue("password")

    fmt.Fprintf(w, "用户名: %s\n", username)
    fmt.Fprintf(w, "密码: %s\n", password)
}

func main() {
    http.HandleFunc("/login", formHandler)
    http.ListenAndServe(":8080", nil)
}

处理 JSON 数据

package main

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

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

func jsonHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != "POST" {
        http.Error(w, "方法不允许", http.StatusMethodNotAllowed)
        return
    }

    // 读取请求体
    body, _ := io.ReadAll(r.Body)

    // 解析 JSON
    var user User
    json.Unmarshal(body, &user)

    // 返回 JSON 响应
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(map[string]interface{}{
        "code":    0,
        "message": "success",
        "data":    user,
    })
}

func main() {
    http.HandleFunc("/api/user", jsonHandler)
    http.ListenAndServe(":8080", nil)
}

37.3 路由处理

使用 http.ServeMux

package main

import (
    "fmt"
    "net/http"
)

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

    // 精确匹配
    mux.HandleFunc("/", homeHandler)
    mux.HandleFunc("/about", aboutHandler)

    // 前缀匹配(以 / 结尾)
    mux.HandleFunc("/api/", apiHandler)

    // 动态路由(手动解析)
    mux.HandleFunc("/user/", userHandler)

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

func homeHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "首页")
}

func aboutHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "关于我们")
}

func apiHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "API: %s", r.URL.Path)
}

func userHandler(w http.ResponseWriter, r *http.Request) {
    // 从路径中提取用户 ID
    userID := r.URL.Path[len("/user/"):]
    fmt.Fprintf(w, "用户 ID: %s", userID)
}

RESTful API 设计

package main

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

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

var users = []User{
    {ID: 1, Name: "Alice", Email: "alice@example.com"},
    {ID: 2, Name: "Bob", Email: "bob@example.com"},
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/users", usersHandler)
    mux.HandleFunc("/users/", userHandler)

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

func usersHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")

    switch r.Method {
    case "GET":
        json.NewEncoder(w).Encode(users)
    case "POST":
        var user User
        json.NewDecoder(r.Body).Decode(&user)
        user.ID = len(users) + 1
        users = append(users, user)
        json.NewEncoder(w).Encode(user)
    default:
        http.Error(w, "方法不允许", http.StatusMethodNotAllowed)
    }
}

func userHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")

    // 提取用户 ID
    idStr := strings.TrimPrefix(r.URL.Path, "/users/")
    id, _ := strconv.Atoi(idStr)

    // 查找用户
    var user *User
    for i := range users {
        if users[i].ID == id {
            user = &users[i]
            break
        }
    }

    if user == nil {
        http.Error(w, "用户不存在", http.StatusNotFound)
        return
    }

    switch r.Method {
    case "GET":
        json.NewEncoder(w).Encode(user)
    case "PUT":
        json.NewDecoder(r.Body).Decode(user)
        json.NewEncoder(w).Encode(user)
    case "DELETE":
        // 删除用户
        for i := range users {
            if users[i].ID == id {
                users = append(users[:i], users[i+1:]...)
                break
            }
        }
        w.WriteHeader(http.StatusNoContent)
    default:
        http.Error(w, "方法不允许", http.StatusMethodNotAllowed)
    }
}

37.4 中间件

基本中间件

package main

import (
    "fmt"
    "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)
        fmt.Printf("%s %s %v\n", 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 token == "" {
            http.Error(w, "未授权", http.StatusUnauthorized)
            return
        }
        next.ServeHTTP(w, r)
    })
}

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

    // 应用中间件
    handler := loggingMiddleware(mux)

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

中间件链

package main

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

type Middleware func(http.Handler) http.Handler

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

func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        next.ServeHTTP(w, r)
        fmt.Printf("[%s] %s %v\n", 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 == "OPTIONS" {
            return
        }

        next.ServeHTTP(w, r)
    })
}

func recoverMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if err := recover(); err != nil {
                http.Error(w, "服务器内部错误", http.StatusInternalServerError)
                fmt.Printf("Panic: %v\n", err)
            }
        }()
        next.ServeHTTP(w, r)
    })
}

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

    // 应用中间件链
    handler := chain(mux, loggingMiddleware, corsMiddleware, recoverMiddleware)

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

37.5 静态文件服务

package main

import (
    "net/http"
)

func main() {
    // 方式1:使用 http.FileServer
    fs := http.FileServer(http.Dir("./static"))
    http.Handle("/static/", http.StripPrefix("/static/", fs))

    // 方式2:使用 http.ServeFile
    http.HandleFunc("/download", func(w http.ResponseWriter, r *http.Request) {
        http.ServeFile(w, r, "./files/document.pdf")
    })

    // 方式3:自定义文件服务器
    http.HandleFunc("/files", func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Disposition", "attachment; filename=document.pdf")
        http.ServeFile(w, r, "./files/document.pdf")
    })

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

37.6 HTTPS 服务器

使用证书文件

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "HTTPS 安全连接")
    })

    // 使用证书文件启动 HTTPS
    err := http.ListenAndServeTLS(
        ":443",
        "server.crt",
        "server.key",
        nil,
    )
    if err != nil {
        fmt.Println("启动失败:", err)
    }
}

自动重定向 HTTP 到 HTTPS

package main

import (
    "fmt"
    "net/http"
)

func main() {
    // HTTP 重定向到 HTTPS
    go func() {
        http.ListenAndServe(":80", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            target := "https://" + r.Host + r.URL.Path
            http.Redirect(w, r, target, http.StatusMovedPermanently)
        }))
    }()

    // HTTPS 服务器
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "HTTPS 安全连接")
    })

    http.ListenAndServeTLS(":443", "server.crt", "server.key", nil)
}

37.7 优雅关闭

package main

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

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        time.Sleep(2 * time.Second) // 模拟耗时操作
        fmt.Fprintf(w, "Hello, World!")
    })

    server := &http.Server{
        Addr:    ":8080",
        Handler: mux,
    }

    // 启动服务器
    go func() {
        fmt.Println("服务器启动在 :8080")
        if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            fmt.Printf("服务器错误: %v\n", err)
        }
    }()

    // 监听系统信号
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit
    fmt.Println("正在关闭服务器...")

    // 优雅关闭
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    if err := server.Shutdown(ctx); err != nil {
        fmt.Printf("服务器强制关闭: %v\n", err)
    }

    fmt.Println("服务器已关闭")
}

37.8 服务器配置

package main

import (
    "net/http"
    "time"
)

func main() {
    server := &http.Server{
        Addr:              ":8080",
        Handler:           nil,
        ReadTimeout:       10 * time.Second,
        WriteTimeout:      10 * time.Second,
        IdleTimeout:       120 * time.Second,
        ReadHeaderTimeout: 5 * time.Second,
        MaxHeaderBytes:    1 << 20, // 1 MB
    }

    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello, World!"))
    })

    server.ListenAndServe()
}

37.9 小结

本章详细介绍了 Go 语言的 HTTP 服务器编程:

  1. 基本服务器:使用 http.HandleFunchttp.ListenAndServe
  2. 请求处理:获取请求信息、处理表单和 JSON
  3. 路由处理:使用 http.ServeMux 和 RESTful API 设计
  4. 中间件:日志、认证、CORS 等中间件实现
  5. 静态文件:使用 http.FileServer 提供静态文件服务
  6. HTTPS:使用证书启动 HTTPS 服务器
  7. 优雅关闭:使用 server.Shutdown 优雅关闭服务器
  8. 服务器配置:超时、缓冲区等配置

HTTP 服务器是 Go 语言最常用的功能之一,掌握它能让你快速构建 Web 服务。在下一章中,我们将学习 TCP 和 UDP 编程。