引言
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开发能力:
Handler与ServeMux:理解
http.Handler接口和ServeMux路由多路复用器是构建HTTP服务的基础。请求处理流程:深入理解
http.Request和http.ResponseWriter的工作原理,正确处理请求和构建响应。中间件模式:掌握中间件函数签名和链式组合方式,实现日志、认证、限流等功能。
HTTP客户端:使用
http.Client进行HTTP请求,注意连接池和超时控制。性能优化:合理设置TCP保活、超时控制、连接池参数以提升服务性能。
RESTful设计:遵循REST原则设计清晰的API接口。
工程实践:完整的HTTP服务需要考虑中间件链、优雅关闭、健康检查等生产级特性。
Go的net/http包设计简洁但功能完善,足以应对大多数Web开发场景。深入理解其底层原理,才能构建高性能、稳定的Web服务。