Go语言net/http与Web开发:构建高性能HTTP服务

引言

Go语言的标准库net/http提供了完善的HTTP服务端和客户端实现,其设计简洁优雅,性能优异,是构建Web服务的主流选择。本文将深入剖析HTTP服务端的核心组件请求处理流程、中间件模式以及客户端使用,并通过实际案例展示如何构建完整的HTTP服务。

一、HTTP服务端核心概念

1.1 最简单的HTTP服务器

复制代码
package main
​
import (
    "fmt"
    "net/http"
)
​
func main() {
    // HandleFunc注册路由处理器
    http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, World!")
    })
​
    // 监听端口并启动服务
    fmt.Println("Server starting on :8080")
    if err := http.ListenAndServe(":8080", nil); err != nil {
        fmt.Printf("Server error: %v\n", err)
    }
}

1.2 Handler接口解析

http.Handler是HTTP服务的核心接口:

复制代码
type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

任何实现此接口的类型都可以作为处理器:

复制代码
package main
​
import (
    "fmt"
    "net/http"
)
​
// 自定义处理器类型
type greetingHandler struct {
    prefix string
}
​
// 实现Handler接口
func (g *greetingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "%s from custom handler!", g.prefix)
}
​
type statusHandler struct {
    statusCode int
}
​
func (s *statusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(s.statusCode)
    fmt.Fprintf(w, "Status code: %d", s.statusCode)
}
​
func main() {
    mux := http.NewServeMux()
​
    // 使用自定义处理器
    mux.Handle("/hello", &greetingHandler{prefix: "Hello"})
    mux.Handle("/status", &statusHandler{statusCode: http.StatusOK})
​
    fmt.Println("Server starting on :8080")
    http.ListenAndServe(":8080", mux)
}

1.3 ServeMux路由多路复用器

ServeMux是Go内置的路由多路复用器,支持基于模式匹配的分发:

复制代码
package main
​
import (
    "fmt"
    "net/http"
)
​
func main() {
    mux := http.NewServeMux()
​
    // 精确匹配
    mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "List all users")
    })
​
    mux.HandleFunc("/users/", func(w http.ResponseWriter, r *http.Request) {
        // 通过Request.URL.Path获取路径
        fmt.Fprintf(w, "User ID: %s", r.URL.Path[len("/users/"):])
    })
​
    // 使用路径参数模式
    mux.HandleFunc("/products/", func(w http.ResponseWriter, r *http.Request) {
        parts := r.URL.Path[len("/products/"):]
        fmt.Fprintf(w, "Product path: %s", parts)
    })
​
    fmt.Println("Server starting on :8080")
    http.ListenAndServe(":8080", mux)
}

二、请求处理流程与响应构建

2.1 请求对象详解

http.Request包含客户端请求的所有信息:

复制代码
package main
​
import (
    "encoding/json"
    "fmt"
    "net/http"
)
​
func inspectRequest(w http.ResponseWriter, r *http.Request) {
    info := map[string]interface{}{
        "Method":           r.Method,
        "URL":              r.URL.String(),
        "Path":             r.URL.Path,
        "RawQuery":         r.URL.RawQuery,
        "Host":             r.Host,
        "RemoteAddr":       r.RemoteAddr,
        "Protocol":         r.Proto,
        "Header":           r.Header,
        "ContentLength":    r.ContentLength,
        "TransferEncoding": r.TransferEncoding,
    }
​
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(info)
}
​
func main() {
    http.HandleFunc("/inspect", inspectRequest)
    http.ListenAndServe(":8080", nil)
}

2.2 请求体读取

复制代码
package main
​
import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)
​
type User struct {
    Name  string `json:"name"`
    Email string `json:"email"`
}
​
func readJSONBody(w http.ResponseWriter, r *http.Request) {
    // 确保内容类型是JSON
    if r.Header.Get("Content-Type") != "application/json" {
        w.WriteHeader(http.StatusBadRequest)
        fmt.Fprintf(w, "Expected Content-Type: application/json")
        return
    }
​
    // 限制请求体大小,防止过大请求
    r.Body = http.MaxBytesReader(w, r.Body, 1024*1024) // 1MB限制
​
    defer r.Body.Close()
​
    var user User
    decoder := json.NewDecoder(r.Body)
    // 支持解析未知字段
    decoder.DisallowUnknownFields()
​
    if err := decoder.Decode(&user); err != nil {
        w.WriteHeader(http.StatusBadRequest)
        fmt.Fprintf(w, "Invalid JSON: %v", err)
        return
    }
​
    fmt.Printf("Received user: %+v\n", user)
    fmt.Fprintf(w, "User %s (%s) received", user.Name, user.Email)
}
​
func main() {
    http.HandleFunc("/user", readJSONBody)
    http.ListenAndServe(":8080", nil)
}

2.3 响应构建

复制代码
package main
​
import (
    "encoding/json"
    "fmt"
    "net/http"
    "time"
)
​
type Response struct {
    Success bool        `json:"success"`
    Message string     `json:"message,omitempty"`
    Data    interface{} `json:"data,omitempty"`
    Error   string     `json:"error,omitempty"`
}
​
func writeJSON(w http.ResponseWriter, statusCode int, data interface{}) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(statusCode)
    json.NewEncoder(w).Encode(data)
}
​
func successResponse(w http.ResponseWriter, data interface{}) {
    writeJSON(w, http.StatusOK, Response{
        Success: true,
        Data:    data,
    })
}
​
func errorResponse(w http.ResponseWriter, statusCode int, message string) {
    writeJSON(w, statusCode, Response{
        Success: false,
        Error:   message,
    })
}
​
func jsonAPI(w http.ResponseWriter, r *http.Request) {
    switch r.Method {
    case http.MethodGet:
        successResponse(w, map[string]interface{}{
            "time":    time.Now().Unix(),
            "message": "GET request successful",
        })
    case http.MethodPost:
        var data map[string]interface{}
        if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
            errorResponse(w, http.StatusBadRequest, "Invalid JSON")
            return
        }
        successResponse(w, map[string]interface{}{
            "received": data,
        })
    default:
        errorResponse(w, http.StatusMethodNotAllowed, "Method not allowed")
    }
}
​
func main() {
    http.HandleFunc("/api", jsonAPI)
    http.ListenAndServe(":8080", nil)
}

2.4 ResponseWriter原理

http.ResponseWriter是一个接口:

复制代码
type ResponseWriter interface {
    Header() Header
    Write([]byte) (int, error)
    WriteHeader(statusCode int)
}

写入顺序很重要:

复制代码
package main
​
import (
    "fmt"
    "net/http"
)
​
func badExample(w http.ResponseWriter, r *http.Request) {
    // 错误:先Write后WriteHeader
    w.Write([]byte("Content"))  // 隐式调用WriteHeader(200)
    w.WriteHeader(http.StatusInternalServerError) // 已经发送了200,这里无效
}
​
func goodExample(w http.ResponseWriter, r *http.Request) {
    // 正确:先WriteHeader后Write
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("Content"))
}
​
func main() {
    http.HandleFunc("/bad", badExample)
    http.HandleFunc("/good", goodExample)
    http.ListenAndServe(":8080", nil)
}

三、中间件模式实现

3.1 中间件基础

中间件是一个返回http.Handler的函数:

复制代码
package main
​
import (
    "fmt"
    "log"
    "net/http"
    "time"
)
​
// 中间件函数签名
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()
​
        // 使用responsewriter的包装器来记录状态码
        wrapped := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}
​
        next.ServeHTTP(wrapped, r)
​
        log.Printf("%s %s %d %v",
            r.Method,
            r.URL.Path,
            wrapped.statusCode,
            time.Since(start))
    })
}
​
type responseWriter struct {
    http.ResponseWriter
    statusCode int
}
​
func (rw *responseWriter) WriteHeader(code int) {
    rw.statusCode = code
    rw.ResponseWriter.WriteHeader(code)
}
​
func (rw *responseWriter) Write(b []byte) (int, error) {
    if rw.statusCode == 0 {
        rw.statusCode = http.StatusOK
    }
    return rw.ResponseWriter.Write(b)
}
​
// 认证中间件
func AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("Authorization")
        if token == "" {
            w.WriteHeader(http.StatusUnauthorized)
            fmt.Fprintf(w, "Missing authorization token")
            return
        }
​
        // 验证token(简化示例)
        if !validateToken(token) {
            w.WriteHeader(http.StatusUnauthorized)
            fmt.Fprintf(w, "Invalid token")
            return
        }
​
        next.ServeHTTP(wrapped, r)
    })
}
​
func validateToken(token string) bool {
    // 实际应该验证JWT或其他token机制
    return token == "valid-token-123"
}
​
func main() {
    finalHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Protected resource!")
    })
​
    // 组合中间件
    handler := LoggingMiddleware(finalHandler)
    // handler = AuthMiddleware(handler)
​
    http.Handle("/protected", handler)
    http.HandleFunc("/public", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Public resource!")
    })
​
    http.ListenAndServe(":8080", nil)
}

3.2 中间件链式组合

复制代码
package main
​
import (
    "fmt"
    "log"
    "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 WithTimeout(timeout time.Duration) Middleware {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            done := make(chan struct{})
            go func() {
                next.ServeHTTP(w, r)
                close(done)
            }()
​
            select {
            case <-done:
                return
            case <-time.After(timeout):
                log.Printf("Request timeout: %s %s", r.Method, r.URL.Path)
                w.WriteHeader(http.StatusGatewayTimeout)
                fmt.Fprintf(w, "Request timeout")
            }
        })
    }
}
​
func WithCORS(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 WithRecovery(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 recovered: %v", err)
                w.WriteHeader(http.StatusInternalServerError)
                fmt.Fprintf(w, "Internal server error")
            }
        }()
        next.ServeHTTP(w, r)
    })
}
​
func mainHandler(w http.ResponseWriter, r *http.Request) {
    // 模拟业务逻辑
    time.Sleep(100 * time.Millisecond)
    fmt.Fprintf(w, "Hello, World!")
}
​
func main() {
    handler := Chain(
        http.HandlerFunc(mainHandler),
        WithRecovery,
        WithCORS,
        WithTimeout(5*time.Second),
    )
​
    http.Handle("/", handler)
    log.Println("Server starting on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

3.3 第三方中间件框架

实际项目中可以使用negroni等成熟框架:

复制代码
package main
​
import (
    "fmt"
    "log"
    "net/http"
​
    "github.com/urfave/negroni"
)
​
func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello from main handler!")
    })
​
    n := negroni.New()
​
    // 使用中间件
    n.Use(negroni.NewLogger())
    n.Use(negroni.NewRecovery())
    n.Use(negroni.NewCORS())
​
    // 添加自定义中间件
    n.UseFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
        log.Printf("Before request: %s", r.URL.Path)
        next(rw, r)
        log.Printf("After request: %s", r.URL.Path)
    })
​
    n.UseHandler(mux)
​
    log.Println("Server starting on :8080")
    log.Fatal(http.ListenAndServe(":8080", n))
}

四、HTTP客户端使用

4.1 基本客户端请求

复制代码
package main
​
import (
    "fmt"
    "io"
    "net/http"
    "time"
)
​
func main() {
    // 创建客户端并设置超时
    client := &http.Client{
        Timeout: 10 * time.Second,
    }
​
    // GET请求
    resp, err := client.Get("https://api.example.com/data")
    if err != nil {
        fmt.Printf("GET request failed: %v\n", err)
        return
    }
    defer resp.Body.Close()
​
    body, err := io.ReadAll(resp.Body)
    if err != nil {
        fmt.Printf("Read body failed: %v\n", err)
        return
    }
​
    fmt.Printf("Status: %d\n", resp.StatusCode)
    fmt.Printf("Body: %s\n", string(body))
}

4.2 设置请求头和自定义请求

复制代码
package main
​
import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)
​
func main() {
    client := &http.Client{}
​
    // 创建自定义请求
    reqBody := map[string]interface{}{
        "username": "testuser",
        "password": "securepass",
    }
    jsonBody, _ := json.Marshal(reqBody)
​
    req, err := http.NewRequest("POST", "https://api.example.com/login",
        bytes.NewBuffer(jsonBody))
    if err != nil {
        fmt.Printf("Create request failed: %v\n", err)
        return
    }
​
    // 设置请求头
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("User-Agent", "MyGoClient/1.0")
    req.Header.Set("Accept", "application/json")
​
    // 发送请求
    resp, err := client.Do(req)
    if err != nil {
        fmt.Printf("Request failed: %v\n", err)
        return
    }
    defer resp.Body.Close()
​
    body, _ := io.ReadAll(resp.Body)
    fmt.Printf("Response: %s\n", string(body))
}

4.3 处理Cookies

复制代码
package main
​
import (
    "fmt"
    "net/http"
)
​
func main() {
    client := &http.Client{}
​
    // 第一次请求,获取Cookie
    req1, _ := http.NewRequest("GET", "https://example.com/login", nil)
    resp1, err := client.Do(req1)
    if err != nil {
        fmt.Printf("Request 1 failed: %v\n", err)
        return
    }
​
    cookies := resp1.Cookies()
    fmt.Printf("Got %d cookies\n", len(cookies))
    resp1.Body.Close()
​
    // 第二次请求,带上Cookie
    req2, _ := http.NewRequest("GET", "https://example.com/profile", nil)
    for _, cookie := range cookies {
        req2.AddCookie(cookie)
        fmt.Printf("Cookie: %s=%s\n", cookie.Name, cookie.Value)
    }
​
    resp2, err := client.Do(req2)
    if err != nil {
        fmt.Printf("Request 2 failed: %v\n", err)
        return
    }
    defer resp2.Body.Close()
​
    fmt.Printf("Profile response status: %d\n", resp2.StatusCode)
}

五、TCP保活与超时控制

5.1 连接池与复用

复制代码
package main
​
import (
    "fmt"
    "io"
    "net/http"
    "sync"
    "time"
)
​
// 使用共享的HTTP客户端(包含连接池)
var httpClient = &http.Client{
    Transport: &http.Transport{
        MaxIdleConns:        100,              // 最大空闲连接数
        MaxIdleConnsPerHost: 10,               // 每个主机最大空闲连接
        IdleConnTimeout:     90 * time.Second, // 空闲连接超时
    },
    Timeout: 30 * time.Second,
}
​
func fetchURLs(urls []string) []string {
    var wg sync.WaitGroup
    results := make([]string, len(urls))
    var mu sync.Mutex
​
    for i, url := range urls {
        wg.Add(1)
        go func(idx int, u string) {
            defer wg.Done()
​
            resp, err := httpClient.Get(u)
            if err != nil {
                results[idx] = fmt.Sprintf("Error: %v", err)
                return
            }
            defer resp.Body.Close()
​
            body, _ := io.ReadAll(resp.Body)
            mu.Lock()
            results[idx] = fmt.Sprintf("%s: %d bytes", u, len(body))
            mu.Unlock()
        }(i, url)
    }
​
    wg.Wait()
    return results
}
​
func main() {
    urls := []string{
        "https://example.com",
        "https://example.org",
        "https://example.net",
    }
​
    results := fetchURLs(urls)
    for _, r := range results {
        fmt.Println(r)
    }
}

5.2 超时控制详解

复制代码
package main
​
import (
    "context"
    "fmt"
    "net/http"
    "time"
)
​
func main() {
    // 1. 整体客户端超时
    client := &http.Client{
        Timeout: 5 * time.Second,
    }
​
    // 2. 使用Context控制超时
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel()
​
    req, _ := http.NewRequestWithContext(ctx, "GET", "https://httpbin.org/delay/10", nil)
​
    resp, err := client.Do(req)
    if err != nil {
        if ctx.Err() == context.DeadlineExceeded {
            fmt.Println("Request timed out!")
        } else {
            fmt.Printf("Request failed: %v\n", err)
        }
        return
    }
    defer resp.Body.Close()
​
    fmt.Printf("Response status: %d\n", resp.StatusCode)
}
​
// 分层超时示例
func layeredTimeoutExample() {
    // 创建带有超时的Context
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()
​
    // 创建请求
    req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.example.com/data", nil)
​
    // 使用http.Transport设置各层超时
    transport := &http.Transport{
        DialContext: (&net.Dialer{
            Timeout:   1 * time.Second,  // 连接建立超时
            KeepAlive: 30 * time.Second,
        }).DialContext,
        ResponseHeaderTimeout: 1 * time.Second, // 读取响应头超时
        ExpectContinueTimeout:  1 * time.Second,
    }
​
    client := &http.Client{
        Transport: transport,
    }
​
    resp, err := client.Do(req)
    if err != nil {
        fmt.Printf("Request failed: %v\n", err)
        return
    }
    defer resp.Body.Close()
​
    fmt.Printf("Response status: %d\n", resp.StatusCode)
}

六、RESTful API设计原则

6.1 RESTful路由设计

复制代码
package main
​
import (
    "encoding/json"
    "fmt"
    "net/http"
    "strconv"
    "strings"
)
​
// RESTful资源处理器
type UserHandler struct{}
​
func (h *UserHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    path := r.URL.Path
    parts := strings.Split(strings.Trim(path, "/"), "/")
​
    // /users - 资源集合
    // /users/:id - 单个资源
    if len(parts) == 1 && parts[0] == "users" {
        switch r.Method {
        case http.MethodGet:
            h.ListUsers(w, r)
        case http.MethodPost:
            h.CreateUser(w, r)
        }
        return
    }
​
    if len(parts) == 2 && parts[0] == "users" {
        id, err := strconv.Atoi(parts[1])
        if err != nil {
            http.Error(w, "Invalid user ID", http.StatusBadRequest)
            return
        }
​
        switch r.Method {
        case http.MethodGet:
            h.GetUser(w, r, id)
        case http.MethodPut:
            h.UpdateUser(w, r, id)
        case http.MethodDelete:
            h.DeleteUser(w, r, id)
        }
        return
    }
​
    http.NotFound(w, r)
}
​
func (h *UserHandler) ListUsers(w http.ResponseWriter, r *http.Request) {
    users := []map[string]interface{}{
        {"id": 1, "name": "张三"},
        {"id": 2, "name": "李四"},
    }
    writeJSON(w, http.StatusOK, users)
}
​
func (h *UserHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
    var user map[string]interface{}
    if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
        http.Error(w, "Invalid JSON", http.StatusBadRequest)
        return
    }
    user["id"] = 3 // 模拟创建
    writeJSON(w, http.StatusCreated, user)
}
​
func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request, id int) {
    user := map[string]interface{}{
        "id":   id,
        "name": fmt.Sprintf("User %d", id),
    }
    writeJSON(w, http.StatusOK, user)
}
​
func (h *UserHandler) UpdateUser(w http.ResponseWriter, r *http.Request, id int) {
    var updates map[string]interface{}
    if err := json.NewDecoder(r.Body).Decode(&updates); err != nil {
        http.Error(w, "Invalid JSON", http.StatusBadRequest)
        return
    }
    updates["id"] = id
    writeJSON(w, http.StatusOK, updates)
}
​
func (h *UserHandler) DeleteUser(w http.ResponseWriter, r *http.Request, id int) {
    writeJSON(w, http.StatusOK, map[string]interface{}{
        "message": fmt.Sprintf("User %d deleted", id),
    })
}
​
func writeJSON(w http.ResponseWriter, status int, data interface{}) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(status)
    json.NewEncoder(w).Encode(data)
}
​
func main() {
    handler := &UserHandler{}
    http.Handle("/users/", handler)
​
    fmt.Println("RESTful API server starting on :8080")
    http.ListenAndServe(":8080", nil)
}

6.2 错误处理规范

复制代码
package main
​
import (
    "encoding/json"
    "fmt"
    "net/http"
)
​
// RFC 7807 Problem Details for HTTP APIs
type ProblemDetail struct {
    Type   string `json:"type"`
    Title  string `json:"title"`
    Status int    `json:"status"`
    Detail string `json:"detail"`
    Instance string `json:"instance"`
}
​
func writeProblem(w http.ResponseWriter, status int, title, detail string) {
    problem := ProblemDetail{
        Type:     fmt.Sprintf("https://example.com/probs/%d", status),
        Title:    title,
        Status:   status,
        Detail:   detail,
        Instance: "/users", // 简化
    }
​
    w.Header().Set("Content-Type", "application/problem+json")
    w.WriteHeader(status)
    json.NewEncoder(w).Encode(problem)
}
​
func userHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodGet {
        writeProblem(w, http.StatusMethodNotAllowed,
            "Method Not Allowed",
            "Only GET method is supported for this endpoint")
        return
    }
​
    userID := r.URL.Query().Get("id")
    if userID == "" {
        writeProblem(w, http.StatusBadRequest,
            "Missing Required Parameter",
            "The 'id' query parameter is required")
        return
    }
​
    fmt.Fprintf(w, "User: %s", userID)
}
​
func main() {
    http.HandleFunc("/users", userHandler)
    http.ListenAndServe(":8080", nil)
}

七、实际案例:构建完整的HTTP服务

7.1 项目结构

复制代码
myapp/
├── main.go
├── handlers/
│   ├── user.go
│   └── product.go
├── middleware/
│   ├── logging.go
│   └── auth.go
├── models/
│   └── models.go
└── go.mod

7.2 完整实现

复制代码
package main
​
import (
    "context"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"
)
​
// ============ 模型定义 ============
​
type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}
​
type Product struct {
    ID       int     `json:"id"`
    Name     string  `json:"name"`
    Price    float64 `json:"price"`
   Quantity int     `json:"quantity"`
}
​
type Order struct {
    ID        int       `json:"id"`
    UserID    int       `json:"user_id"`
    Products  []Product `json:"products"`
    Total     float64   `json:"total"`
    CreatedAt time.Time `json:"created_at"`
}
​
type APIResponse struct {
    Success bool        `json:"success"`
    Data    interface{} `json:"data,omitempty"`
    Error   string      `json:"error,omitempty"`
    Meta    *Meta       `json:"meta,omitempty"`
}
​
type Meta struct {
    Page       int `json:"page,omitempty"`
    PageSize   int `json:"page_size,omitempty"`
    TotalCount int `json:"total_count,omitempty"`
}
​
// ============ 中间件 ============
​
type loggingResponseWriter struct {
    http.ResponseWriter
    statusCode int
    size       int
}
​
func (w *loggingResponseWriter) WriteHeader(code int) {
    w.statusCode = code
    w.ResponseWriter.WriteHeader(code)
}
​
func (w *loggingResponseWriter) Write(b []byte) (int, error) {
    size, err := w.ResponseWriter.Write(b)
    w.size += size
    return size, err
}
​
func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        lrw := &loggingResponseWriter{w, http.StatusOK, 0}
​
        next.ServeHTTP(lrw, r)
​
        log.Printf("[%s] %s %s %d %d bytes %v",
            r.Method,
            r.URL.Path,
            r.RemoteAddr,
            lrw.statusCode,
            lrw.size,
            time.Since(start))
    })
}
​
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 recovered: %v", err)
                writeJSON(w, http.StatusInternalServerError, APIResponse{
                    Success: false,
                    Error:   "Internal server error",
                })
            }
        }()
        next.ServeHTTP(w, r)
    })
}
​
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)
    })
}
​
// ============ 处理器 ============
​
type UserHandler struct {
    users []User
}
​
func NewUserHandler() *UserHandler {
    return &UserHandler{
        users: []User{
            {ID: 1, Name: "张三", Email: "zhangsan@example.com"},
            {ID: 2, Name: "李四", Email: "lisi@example.com"},
        },
    }
}
​
func (h *UserHandler) ListUsers(w http.ResponseWriter, r *http.Request) {
    page, _ := strconv.Atoi(r.URL.Query().Get("page"))
    pageSize, _ := strconv.Atoi(r.URL.Query().Get("page_size"))
​
    if page <= 0 {
        page = 1
    }
    if pageSize <= 0 || pageSize > 100 {
        pageSize = 10
    }
​
    start := (page - 1) * pageSize
    end := start + pageSize
​
    if start >= len(h.users) {
        writeJSON(w, http.StatusOK, APIResponse{
            Success: true,
            Data:    []User{},
            Meta:    &Meta{Page: page, PageSize: pageSize, TotalCount: len(h.users)},
        })
        return
    }
​
    if end > len(h.users) {
        end = len(h.users)
    }
​
    writeJSON(w, http.StatusOK, APIResponse{
        Success: true,
        Data:    h.users[start:end],
        Meta:    &Meta{Page: page, PageSize: pageSize, TotalCount: len(h.users)},
    })
}
​
func (h *UserHandler) GetUser(w http.ResponseWriter, r *http.Request, id int) {
    for _, user := range h.users {
        if user.ID == id {
            writeJSON(w, http.StatusOK, APIResponse{Success: true, Data: user})
            return
        }
    }
    writeJSON(w, http.StatusNotFound, APIResponse{
        Success: false,
        Error:   "User not found",
    })
}
​
type ProductHandler struct {
    products []Product
}
​
func NewProductHandler() *ProductHandler {
    return &ProductHandler{
        products: []Product{
            {ID: 1, Name: "iPhone 15", Price: 7999.00, Quantity: 100},
            {ID: 2, Name: "MacBook Pro", Price: 19999.00, Quantity: 50},
        },
    }
}
​
func (h *ProductHandler) ListProducts(w http.ResponseWriter, r *http.Request) {
    writeJSON(w, http.StatusOK, APIResponse{Success: true, Data: h.products})
}
​
func (h *ProductHandler) GetProduct(w http.ResponseWriter, r *http.Request, id int) {
    for _, p := range h.products {
        if p.ID == id {
            writeJSON(w, http.StatusOK, APIResponse{Success: true, Data: p})
            return
        }
    }
    writeJSON(w, http.StatusNotFound, APIResponse{Success: false, Error: "Product not found"})
}
​
// ============ 辅助函数 ============
​
func writeJSON(w http.ResponseWriter, status int, resp APIResponse) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(status)
    json.NewEncoder(w).Encode(resp)
}
​
func requireJSON(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        if r.Header.Get("Content-Type") != "application/json" {
            writeJSON(w, http.StatusUnsupportedMediaType, APIResponse{
                Success: false,
                Error:   "Content-Type must be application/json",
            })
            return
        }
        next(w, r)
    }
}
​
// ============ 路由设置 ============
​
func setupRoutes(userHandler *UserHandler, productHandler *ProductHandler) *http.ServeMux {
    mux := http.NewServeMux()
​
    // 用户路由
    mux.HandleFunc("GET /api/users", userHandler.ListUsers)
    mux.HandleFunc("GET /api/users/", func(w http.ResponseWriter, r *http.Request) {
        idStr := r.URL.Path[len("/api/users/"):]
        id, err := strconv.Atoi(idStr)
        if err != nil {
            writeJSON(w, http.StatusBadRequest, APIResponse{Success: false, Error: "Invalid user ID"})
            return
        }
        userHandler.GetUser(w, r, id)
    })
​
    // 产品路由
    mux.HandleFunc("GET /api/products", productHandler.ListProducts)
    mux.HandleFunc("GET /api/products/", func(w http.ResponseWriter, r *http.Request) {
        idStr := r.URL.Path[len("/api/products/"):]
        id, err := strconv.Atoi(idStr)
        if err != nil {
            writeJSON(w, http.StatusBadRequest, APIResponse{Success: false, Error: "Invalid product ID"})
            return
        }
        productHandler.GetProduct(w, r, id)
    })
​
    // 健康检查
    mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
        writeJSON(w, http.StatusOK, APIResponse{Success: true, Data: map[string]string{"status": "healthy"}})
    })
​
    return mux
}
​
// ============ 主函数 ============
​
import (
    "strconv"
)
​
func main() {
    userHandler := NewUserHandler()
    productHandler := NewProductHandler()
    mux := setupRoutes(userHandler, productHandler)
​
    // 创建服务器
    server := &http.Server{
        Addr:         ":8080",
        Handler:      chainHandlers(mux, recoveryMiddleware, loggingMiddleware, corsMiddleware),
        ReadTimeout:  10 * time.Second,
        WriteTimeout: 10 * time.Second,
        IdleTimeout:  60 * time.Second,
    }
​
    // 启动服务器到Goroutine
    go func() {
        log.Println("Server starting on :8080")
        if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatalf("Server failed: %v", err)
        }
    }()
​
    // 优雅关闭
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit
​
    log.Println("Shutting down server...")
​
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()
​
    if err := server.Shutdown(ctx); err != nil {
        log.Fatalf("Server forced to shutdown: %v", err)
    }
​
    log.Println("Server exited properly")
}
​
func chainHandlers(h http.Handler, middlewares ...func(http.Handler) http.Handler) http.Handler {
    for i := len(middlewares) - 1; i >= 0; i-- {
        h = middlewares[i](h)
    }
    return h
}

总结

本文全面介绍了Go语言net/http包的Web开发能力:

  1. Handler与ServeMux :理解http.Handler接口和ServeMux路由多路复用器是构建HTTP服务的基础。

  2. 请求处理流程 :深入理解http.Requesthttp.ResponseWriter的工作原理,正确处理请求和构建响应。

  3. 中间件模式:掌握中间件函数签名和链式组合方式,实现日志、认证、限流等功能。

  4. HTTP客户端 :使用http.Client进行HTTP请求,注意连接池和超时控制。

  5. 性能优化:合理设置TCP保活、超时控制、连接池参数以提升服务性能。

  6. RESTful设计:遵循REST原则设计清晰的API接口。

  7. 工程实践:完整的HTTP服务需要考虑中间件链、优雅关闭、健康检查等生产级特性。

Go的net/http包设计简洁但功能完善,足以应对大多数Web开发场景。深入理解其底层原理,才能构建高性能、稳定的Web服务。

相关推荐
Scott9999HH3 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社4 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海4 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖5 小时前
JDK 26 新特性详解
java·开发语言
马优晨6 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区7 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大8 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai8 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜8 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa8 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio