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服务。

相关推荐
叼烟扛炮2 小时前
C++第一讲:C++ 入门基础
开发语言·c++·函数重载·引用·内联函数·nullptr
Ulyanov2 小时前
《现代 Python 桌面应用架构实战:PySide6 + QML 从入门到工程化》:QML 声明式语法与霓虹按钮 —— 当 Python 遇见现代美学
开发语言·python·ui·qml·系统仿真·雷达电子对抗仿真
弹不出的5h3ll2 小时前
Ghost Bits:高位截断如何让 Java WAF 形同虚设
java·开发语言
码界筑梦坊2 小时前
113-基于Python的国际超市电商销售数据可视化分析系统
开发语言·python·信息可视化·毕业设计·fastapi
memories1982 小时前
Go 语言 Channel(管道/通道)
开发语言·后端·golang
初心未改HD2 小时前
Go语言结构体Struct:内存布局、标签、接收者与内存对齐
开发语言·golang
lsx2024062 小时前
JavaScript 类
开发语言
jieyucx3 小时前
Go 数据结构入门:线性表、顺序表、链表
数据结构·链表·golang
专科3年的修炼3 小时前
uni-app移动应用开发第四章
开发语言·javascript·uni-app