2. Go Web服务基础

构建契约压测闭环的第一步,是拥有一个可运行、可测试的Web服务。本章将带领你使用Go标准库 net/http 与流行框架 gin 分别搭建RESTful API服务,并深入理解路由与中间件这两个核心概念。

2.1 标准库 net/http:零依赖的RESTful服务

Go标准库 net/http 提供了构建HTTP服务所需的一切基础组件,无需引入任何第三方依赖即可快速启动一个API服务。

2.1.1 基础服务搭建

以下代码使用 net/http 创建了一个监听 :8080 端口的服务,并注册了三个RESTful风格的路由:

package main

import (
    "encoding/json"
    "log"
    "net/http"
)

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

var users = []User{{ID: 1, Name: "Alice"}}

func getUsers(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(users)
}

func createUser(w http.ResponseWriter, r *http.Request) {
    var newUser User
    if err := json.NewDecoder(r.Body).Decode(&newUser); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    newUser.ID = len(users) + 1
    users = append(users, newUser)
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(newUser)
}

func main() {
    http.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
        switch r.Method {
        case http.MethodGet:
            getUsers(w, r)
        case http.MethodPost:
            createUser(w, r)
        default:
            http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        }
    })
    log.Fatal(http.ListenAndServe(":8080", nil))
}
关键点:标准库的路由器 http.DefaultServeMux 仅支持路径匹配,不支持HTTP方法匹配。开发者需要在处理函数内部通过 r.Method 手动分发,这是标准库与gin等框架的主要差异之一。

2.1.2 标准库路由的局限性

特性 net/http 默认路由 说明
HTTP方法匹配 不支持 需手动 switch r.Method
路径参数 不支持 无法直接获取 /api/users/:id
路由分组 不支持 所有路由平铺注册
中间件链 需手动包装 通过函数嵌套实现

2.2 Gin框架:高性能的RESTful路由引擎

Gin是目前Go社区最流行的Web框架之一,其核心优势在于高性能的路由树(基于radix tree)和丰富的中间件生态。

2.2.1 使用Gin重构API服务

以下代码使用Gin实现了与上一节完全相同的功能,但代码更加简洁、可读性更强:

package main

import (
    "net/http"
    "github.com/gin-gonic/gin"
)

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

var users = []User{{ID: 1, Name: "Alice"}}

func main() {
    r := gin.Default()

    r.GET("/api/users", func(c *gin.Context) {
        c.JSON(http.StatusOK, users)
    })

    r.POST("/api/users", func(c *gin.Context) {
        var newUser User
        if err := c.ShouldBindJSON(&newUser); err != nil {
            c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
            return
        }
        newUser.ID = len(users) + 1
        users = append(users, newUser)
        c.JSON(http.StatusCreated, newUser)
    })

    r.Run(":8080")
}
对比优势:Gin通过 r.GET()、r.POST() 等方法直接绑定HTTP方法和路径,无需手动判断 r.Method;同时内置 ShouldBindJSON 简化了请求体解析,c.JSON 自动设置Content-Type并序列化响应。

2.3 路由与中间件核心机制

路由和中间件是Web框架的两大基石。路由决定了请求如何被分发到对应的处理函数,中间件则提供了在请求处理前后执行通用逻辑的能力。

2.3.1 Gin路由详解

Gin的路由支持路径参数、查询参数和路由分组,这些特性在构建RESTful API时非常实用:

// 路径参数
r.GET("/api/users/:id", func(c *gin.Context) {
    id := c.Param("id")
    c.JSON(http.StatusOK, gin.H{"user_id": id})
})

// 查询参数
r.GET("/api/search", func(c *gin.Context) {
    keyword := c.Query("q")
    page := c.DefaultQuery("page", "1")
    c.JSON(http.StatusOK, gin.H{"keyword": keyword, "page": page})
})

// 路由分组
v1 := r.Group("/api/v1")
{
    v1.GET("/users", listUsersV1)
    v1.POST("/users", createUserV1)
}

2.3.2 中间件:请求处理管道

中间件是Gin框架的核心设计模式之一。每个中间件都是一个 gin.HandlerFunc,通过 c.Next() 控制请求在管道中的流转。

以下是一个日志记录中间件的实现:

func LoggerMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        // 请求前
        start := time.Now()
        path := c.Request.URL.Path

        // 继续执行后续处理
        c.Next()

        // 请求后
        latency := time.Since(start)
        log.Printf("[%s] %s %d %v",
            c.Request.Method,
            path,
            c.Writer.Status(),
            latency,
        )
    }
}

// 使用中间件
r := gin.New()
r.Use(LoggerMiddleware())
r.Use(gin.Recovery())
注意:使用 gin.Default() 会自动附加 Logger 和 Recovery 两个内置中间件。如果使用 gin.New() 创建引擎,则需要手动添加中间件。在契约压测场景中,建议使用 gin.New() 以精确控制中间件栈,避免日志输出影响压测性能数据。

2.4 中间件执行顺序与架构

理解中间件的执行顺序对于调试和性能优化至关重要。以下SVG图展示了Gin框架中多个中间件与最终处理函数的执行流程:

Gin中间件执行管道 HTTP请求 中间件1 日志记录 中间件2 认证鉴权 处理函数 业务逻辑 响应 执行顺序说明 1. 请求依次经过中间件1 → 中间件2 → 处理函数 2. 每个中间件调用 c.Next() 将控制权交给下一个处理单元 3. 响应沿虚线路径逆向返回,中间件可在 c.Next() 之后执行后置逻辑

2.5 中间件在契约压测中的价值

在契约压测闭环中,中间件承担着三个关键角色:

  • 请求审计:记录每个请求的路径、方法、状态码和耗时,为契约验证提供原始数据。
  • 契约校验:在请求进入业务逻辑之前,对请求体进行结构校验,确保符合契约规范。
  • 性能隔离:在压测场景中,通过中间件注入延迟或模拟故障,验证服务在异常契约下的行为。
实践建议:在构建契约压测闭环时,建议将契约校验逻辑封装为独立中间件,与业务中间件分离。这样在压测阶段可以灵活启用/禁用契约校验,避免校验逻辑成为性能瓶颈。

2.6 本章小结

知识点 核心要点 契约压测关联
net/http 基础 零依赖、手动方法分发、无路径参数 适合轻量级契约验证服务
Gin框架 高性能路由树、路径参数、路由分组 推荐用于生产级契约压测服务
中间件机制 c.Next() 控制流、前置/后置逻辑 契约校验、日志审计、性能隔离
路由分组 版本管理、模块化组织 按契约版本分组管理API