2. Go Web 基础回顾:HTTP 协议基础、Go 标准库 net/http 快速搭建 Web 服务、路由与中间件概念
2.1 HTTP 协议核心要素
HTTP(HyperText Transfer Protocol)是 Web 服务的通信基石。Go 的 net/http 标准库完整实现了 HTTP/1.1 和 HTTP/2 协议,理解其核心要素是编写正确 Web 服务的前提。
| 要素 | 说明 | Go 中对应类型/方法 |
|---|---|---|
| 请求方法 | GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS | http.MethodGet、http.MethodPost 等常量 |
| URL 路径 | 标识资源的唯一地址,可包含查询参数 | *http.Request.URL.Path、URL.Query() |
| 请求头 | 携带元数据:Content-Type、Authorization、Accept 等 | http.Header 类型,r.Header.Get(key) |
| 请求体 | POST/PUT 时传输 JSON、表单、文件等数据 | r.Body(io.ReadCloser) |
| 状态码 | 服务端处理结果标识:200、201、400、404、500 等 | http.StatusOK、http.StatusNotFound 等常量 |
| 响应体 | 服务端返回的数据,通常为 JSON、HTML 或纯文本 | w.Write()、json.NewEncoder(w).Encode() |
契约压测视角: 压测时需精确构造请求方法、路径、头、体,并验证响应状态码与体结构。任何偏差都会导致测试失败或误报。
2.2 使用 net/http 快速搭建 Web 服务
Go 标准库 net/http 无需第三方依赖即可启动 HTTP 服务。以下是最小可运行的服务示例:
package main
import (
"encoding/json"
"log"
"net/http"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
func getUserHandler(w http.ResponseWriter, r *http.Request) {
// 仅允许 GET 方法
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
user := User{ID: 1, Name: "Alice"}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(user)
}
func main() {
http.HandleFunc("/api/user", getUserHandler)
log.Println("Server starting on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
关键点:
http.HandleFunc注册路由处理函数,默认使用DefaultServeMux。http.ListenAndServe启动 TCP 监听并处理 HTTP 请求。- 响应需显式设置
Content-Type和状态码,否则默认text/plain; charset=utf-8。
2.3 路由与 ServeMux 机制
Go 标准库的路由器是 http.ServeMux,它基于路径前缀匹配,不支持参数化路由(如 /user/:id)。
| 路由模式 | 匹配规则 | 示例 |
|---|---|---|
| 精确路径 | 完全匹配请求路径 | /api/user 只匹配 /api/user |
| 子树路径 | 以 / 结尾,匹配该路径下的所有子路径 |
/api/ 匹配 /api/user、/api/order |
| 根路径 | / 匹配所有未匹配的路径(兜底) |
/ 作为 404 处理或静态文件服务 |
注意:
ServeMux 不支持路径参数、HTTP 方法路由(需在 Handler 内手动判断 Method)。生产级服务建议使用 gorilla/mux、chi 或 Go 1.22+ 的增强路由。
2.4 中间件概念与链式处理
中间件(Middleware)是包裹在核心 Handler 前后的处理函数,用于横切关注点:日志、鉴权、限流、超时控制等。Go 中典型的中间件模式是函数式嵌套:
// 中间件类型:接收 Handler,返回 Handler
type Middleware func(http.Handler) http.Handler
// 日志中间件
func LoggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Printf("[%s] %s %s", r.Method, r.URL.Path, r.RemoteAddr)
next.ServeHTTP(w, r) // 调用下一个 Handler
})
}
// 鉴权中间件
func AuthMiddleware(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
}
// 验证 token...
next.ServeHTTP(w, r)
})
}
// 链式组合
func main() {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello, World!"))
})
// 从内到外:Auth → Logging → handler
wrapped := LoggingMiddleware(AuthMiddleware(handler))
http.Handle("/api/secure", wrapped)
http.ListenAndServe(":8080", nil)
}
中间件执行顺序:
- 请求进入时:LoggingMiddleware → AuthMiddleware → 核心 Handler
- 响应返回时:核心 Handler → AuthMiddleware → LoggingMiddleware
- 任一中间件调用
http.Error或w.WriteHeader后,后续中间件和 Handler 不再执行。
2.5 契约压测视角:为什么需要回顾这些基础?
- 路由匹配: 压测脚本必须使用正确的路径和 HTTP 方法,否则请求无法到达目标 Handler。
- 中间件影响: 鉴权、限流、日志中间件会改变请求处理路径和响应时间,压测需考虑中间件开销。
- 响应结构: 契约验证依赖精确的响应状态码、Header 和 Body 结构,这些由 Handler 和中间件共同决定。
- 错误处理: 中间件或 Handler 中未捕获的 panic 会导致 500 响应,压测需覆盖异常路径。
最佳实践: 在编写压测脚本前,先通过
curl 或 Postman 手动验证每个接口的请求/响应契约,确保基础通信无误。