HTTP 服务
包总览
| 类型/函数 | 用途 |
|---|
http.ServeMux | 路由注册与分发 |
http.Handler | 请求处理器接口 |
http.HandlerFunc | 函数适配器 |
http.Client | HTTP 客户端 |
http.Server | HTTP 服务器配置 |
http.Request | 请求对象 |
http.ResponseWriter | 响应写入器 |
http.Cookie | Cookie 操作 |
http.Transport | 传输层配置 |
HTTP 服务端
基础路由
// Go 1.22+ 增强版 ServeMux:支持方法和路径参数
mux := http.NewServeMux()
// Go 1.22+ 新语法
mux.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
fmt.Fprintf(w, "User: %s", id)
})
mux.HandleFunc("POST /users", createUser)
mux.HandleFunc("PUT /users/{id}", updateUser)
mux.HandleFunc("DELETE /users/{id}", deleteUser)
// 通配符
mux.HandleFunc("GET /files/{path...}", serveFile)
// r.PathValue("path") 获取剩余路径
// Go 1.21 及之前
mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
// ...
case "POST":
// ...
}
})
http.ListenAndServe(":8080", mux)
ServeMux vs 第三方路由对比
| 特性 | ServeMux (1.22+) | chi | gin | echo |
|---|
| 路径参数 | ✓ {id} | ✓ /{id} | ✓ /:id | ✓ /:id |
| 方法路由 | ✓ GET /path | ✓ | ✓ | ✓ |
| 中间件 | ✗ | ✓ | ✓ | ✓ |
| 路由分组 | ✗ | ✓ | ✓ | ✓ |
| 性能 | 中 | 高 | 最高 | 高 |
| 依赖 | 无 | 小 | 大 | 中 |
| 推荐度 | 简单项目 | ★★★★★ | ★★★★☆ | ★★★★☆ |
Handler 接口
// http.Handler 接口
type Handler interface {
ServeHTTP(w http.ResponseWriter, r *http.Request)
}
// 实现 Handler 接口
type UserHandler struct {
svc UserService
}
func (h *UserHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// 路由逻辑
switch r.Method {
case "GET":
h.getUser(w, r)
case "POST":
h.createUser(w, r)
}
}
// http.HandlerFunc:函数适配器
type HandlerFunc func(http.ResponseWriter, *http.Request)
// 任何签名为 func(http.ResponseWriter, *http.Request) 的函数
// 都可以通过 http.HandlerFunc 转换为 Handler
mux.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
// ...
})
Request 解析
func handler(w http.ResponseWriter, r *http.Request) {
// 请求行
r.Method // "GET", "POST", "PUT", "DELETE", "PATCH"
r.URL // *url.URL
r.URL.Path // "/api/v1/users"
r.URL.RawQuery // "page=1&limit=20"
r.Proto // "HTTP/1.1"
// 路径参数(Go 1.22+)
r.PathValue("id")
// Query 参数
r.URL.Query().Get("page") // "1"
r.URL.Query().Get("limit") // "20"
r.URL.Query()["tags"] // []string{"go", "k8s"}
// Header
r.Header.Get("Content-Type") // "application/json"
r.Header.Get("Authorization") // "Bearer xxx"
r.Header.Get("X-Request-ID")
r.Header["Set-Cookie"] // 多值 header
// Cookie
cookie, err := r.Cookie("session_id")
if err != nil {
if errors.Is(err, http.ErrNoCookie) {
// cookie 不存在
}
}
cookies := r.Cookies() // []*http.Cookie
// Body
body, err := io.ReadAll(r.Body)
defer r.Body.Close()
// JSON Body 解析
var req CreateUserReq
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid JSON", http.StatusBadRequest)
return
}
// Form 表单
r.ParseForm()
r.Form.Get("username")
r.PostForm.Get("password")
// Multipart 表单
r.ParseMultipartForm(32 << 20) // 32MB max memory
r.FormFile("upload") // 文件上传
// 客户端信息
r.RemoteAddr // "192.168.1.1:12345"
r.Host // "example.com"
r.Referer() // 来源页
r.UserAgent() // 浏览器标识
// Context(请求取消、超时)
ctx := r.Context()
}
ResponseWriter 写入
func handler(w http.ResponseWriter, r *http.Request) {
// 设置 Header(必须在 WriteHeader/Write 之前)
w.Header().Set("Content-Type", "application/json")
w.Header().Set("X-Request-ID", "abc123")
// 多值 Header
w.Header().Add("Set-Cookie", "a=1")
w.Header().Add("Set-Cookie", "b=2")
// 设置状态码
w.WriteHeader(http.StatusOK) // 200
w.WriteHeader(http.StatusCreated) // 201
w.WriteHeader(http.StatusNotFound) // 404
// 写入 Body
w.Write([]byte("hello"))
// JSON 响应
json.NewEncoder(w).Encode(map[string]interface{}{
"code": 0,
"data": user,
})
// 设置 Cookie
http.SetCookie(w, &http.Cookie{
Name: "session_id",
Value: "abc123",
Path: "/",
HttpOnly: true,
Secure: true, // 仅 HTTPS
SameSite: http.SameSiteStrictMode,
Expires: time.Now().Add(24 * time.Hour),
MaxAge: 86400,
})
// 重定向
http.Redirect(w, r, "/new-path", http.StatusFound)
// 文件下载
w.Header().Set("Content-Disposition", `attachment; filename="report.csv"`)
http.ServeFile(w, r, "/path/to/report.csv")
// flush(流式响应)
flusher, ok := w.(http.Flusher)
if ok {
w.Write([]byte("data chunk 1"))
flusher.Flush()
w.Write([]byte("data chunk 2"))
flusher.Flush()
}
}
HTTP 状态码速查
| 状态码 | 常量 | 含义 |
|---|
| 200 | StatusOK | 成功 |
| 201 | StatusCreated | 创建成功 |
| 204 | StatusNoContent | 成功无内容 |
| 301 | StatusMovedPermanently | 永久重定向 |
| 302 | StatusFound | 临时重定向 |
| 304 | StatusNotModified | 缓存有效 |
| 400 | StatusBadRequest | 请求格式错误 |
| 401 | StatusUnauthorized | 未认证 |
| 403 | StatusForbidden | 无权限 |
| 404 | StatusNotFound | 资源不存在 |
| 409 | StatusConflict | 冲突 |
| 422 | StatusUnprocessableEntity | 验证失败 |
| 429 | StatusTooManyRequests | 限流 |
| 500 | StatusInternalServerError | 服务器错误 |
| 502 | StatusBadGateway | 网关错误 |
| 503 | StatusServiceUnavailable | 服务不可用 |
| 504 | StatusGatewayTimeout | 网关超时 |
中间件
中间件模式
// 中间件签名
type Middleware func(http.Handler) http.Handler
// 日志中间件
func Logging(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
})
}
// Recovery 中间件
func Recovery(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: %v\n%s", err, debug.Stack())
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}()
next.ServeHTTP(w, r)
})
}
// CORS 中间件
func CORS(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)
})
}
// 请求 ID 中间件
func RequestID(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
id := r.Header.Get("X-Request-ID")
if id == "" {
id = uuid.New().String()
}
w.Header().Set("X-Request-ID", id)
ctx := context.WithValue(r.Context(), "requestID", id)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// 认证中间件
func Auth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
user, err := verifyToken(token)
if err != nil {
http.Error(w, "Invalid token", http.StatusUnauthorized)
return
}
ctx := context.WithValue(r.Context(), "user", user)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// 限流中间件
func RateLimit(rate int, burst int) Middleware {
limiter := rate.NewLimiter(rate.Limit(rate), burst)
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !limiter.Allow() {
http.Error(w, "Too Many Requests", http.StatusTooManyRequests)
return
}
next.ServeHTTP(w, r)
})
}
}
// 中间件链
func Chain(handler http.Handler, middlewares ...Middleware) http.Handler {
for i := len(middlewares) - 1; i >= 0; i-- {
handler = middlewares[i](handler)
}
return handler
}
// 使用
handler := Chain(
myHandler,
Recovery,
Logging,
RequestID,
CORS,
)
可记录状态的 ResponseWriter
type statusRecorder struct {
http.ResponseWriter
status int
size int
}
func (r *statusRecorder) WriteHeader(status int) {
r.status = status
r.ResponseWriter.WriteHeader(status)
}
func (r *statusRecorder) Write(b []byte) (int, error) {
n, err := r.ResponseWriter.Write(b)
r.size += n
return n, err
}
func Logging(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
rec := &statusRecorder{ResponseWriter: w, status: 200}
next.ServeHTTP(rec, r)
log.Printf("%s %s %d %d %v",
r.Method, r.URL.Path, rec.status, rec.size, time.Since(start))
})
}
HTTP 服务器配置
server := &http.Server{
Addr: ":8080",
Handler: mux,
ReadTimeout: 10 * time.Second, // 读取请求超时
WriteTimeout: 30 * time.Second, // 写响应超时
IdleTimeout: 120 * time.Second, // 空闲连接超时
MaxHeaderBytes: 1 << 20, // 1MB header 限制
ErrorLog: log.New(os.Stderr, "http: ", log.LstdFlags),
}
// 使用 TLS
// server.ListenAndServeTLS("cert.pem", "key.pem")
// 优雅关闭
go func() {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
<-sigCh
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Printf("server shutdown: %v", err)
}
}()
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatal(err)
}
服务器超时配置建议
| 参数 | 推荐值 | 说明 |
|---|
| ReadTimeout | 10-15s | 客户端发送请求的时间限制 |
| WriteTimeout | 30-60s | 服务端写响应的时间限制 |
| IdleTimeout | 120s | keep-alive 空闲超时 |
| MaxHeaderBytes | 1MB | 防止超大 header 攻击 |
HTTP 客户端
基本请求
// 简单 GET
resp, err := http.Get("https://api.example.com/users")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
// 自定义请求
req, err := http.NewRequest("GET", "https://api.example.com/users", nil)
req.Header.Set("Authorization", "Bearer token")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
defer resp.Body.Close()
POST 请求
// JSON POST
body, _ := json.Marshal(map[string]string{"name": "小徐"})
resp, err := http.Post(
"https://api.example.com/users",
"application/json",
bytes.NewBuffer(body),
)
// Form POST
form := url.Values{}
form.Set("username", "小徐")
form.Set("password", "secret")
resp, err := http.PostForm("https://api.example.com/login", form)
// 带自定义 header 的 POST
req, _ := http.NewRequest("POST", "https://api.example.com/users", bytes.NewBuffer(body))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer token")
resp, err := http.DefaultClient.Do(req)
高级客户端配置
client := &http.Client{
Timeout: 30 * time.Second, // 总超时
Transport: &http.Transport{
DialContext: (&net.Dialer{
Timeout: 5 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
MaxIdleConns: 100, // 最大空闲连接数
MaxIdleConnsPerHost: 10, // 每个 host 的最大空闲连接数
IdleConnTimeout: 90 * time.Second, // 空闲连接超时
TLSHandshakeTimeout: 10 * time.Second,
ResponseHeaderTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
},
}
// 必须复用 client,不要每次请求创建新 client
// ❌ 每次 new http.Client 会导致连接泄漏
// ✓ 全局复用一个 client
带重试的请求
func DoWithRetry(client *http.Client, req *http.Request, maxRetries int) (*http.Response, error) {
var lastErr error
for i := 0; i < maxRetries; i++ {
if i > 0 {
time.Sleep(time.Duration(i*i) * time.Second) // 指数退避
}
resp, err := client.Do(req)
if err != nil {
lastErr = err
continue
}
// 5xx 重试,4xx 不重试
if resp.StatusCode >= 500 {
resp.Body.Close()
lastErr = fmt.Errorf("server error: %d", resp.StatusCode)
continue
}
return resp, nil
}
return nil, fmt.Errorf("after %d retries: %w", maxRetries, lastErr)
}
Multipart 文件上传
func uploadFile(client *http.Client, url, filePath string) error {
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
// 添加文件
file, err := os.Open(filePath)
if err != nil {
return err
}
defer file.Close()
part, err := writer.CreateFormFile("file", filepath.Base(filePath))
if err != nil {
return err
}
io.Copy(part, file)
// 添加额外字段
writer.WriteField("description", "my file")
writer.Close()
req, _ := http.NewRequest("POST", url, body)
req.Header.Set("Content-Type", writer.FormDataContentType())
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
return nil
}
SSE / 流式响应
// Server-Sent Events 服务端
func SSEHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "streaming not supported", http.StatusInternalServerError)
return
}
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
ctx := r.Context()
for {
select {
case <-ctx.Done():
return
case t := <-ticker.C:
fmt.Fprintf(w, "data: %s\n\n", t.Format(time.RFC3339))
flusher.Flush()
}
}
}
// SSE 客户端
resp, _ := http.Get("https://api.example.com/events")
scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "data: ") {
data := line[6:]
fmt.Println(data)
}
}
WebSocket(标准库不支持,需第三方)
// 使用 gorilla/websocket
import "github.com/gorilla/websocket"
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true // 生产环境应检查 Origin
},
}
func WSHandler(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println(err)
return
}
defer conn.Close()
for {
messageType, message, err := conn.ReadMessage()
if err != nil {
break
}
// 回显
conn.WriteMessage(messageType, message)
}
}
HTTP 客户端检查清单
| 检查项 | 说明 |
|---|
| ✅ 复用 http.Client | 全局共享,不要每次创建 |
| ✅ 设置超时 | 避免无限等待 |
| ✅ 关闭 resp.Body | defer resp.Body.Close() |
| ✅ 配置连接池 | MaxIdleConnsPerHost |
| ✅ 处理重试 | 5xx 和网络错误 |
| ✅ 检查状态码 | 不要假设 200 |
| ✅ 限制 body 大小 | io.LimitReader 防止 OOM |
| ✅ 使用 context | 支持取消和超时 |