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