3. HTTP 基础服务搭建:使用 net/http 标准库创建简单 Web 服务;路由注册与中间件概念引入
3.1 使用 net/http 标准库创建 Web 服务
Go 语言的标准库 net/http 提供了构建 HTTP 服务所需的核心能力,无需依赖任何第三方框架即可快速搭建一个可用的 Web 服务。本节将演示如何通过标准库创建基础服务,并理解其核心组件。
3.1.1 核心组件与工作流程
| 组件 | 职责 | 关键方法/类型 |
|---|---|---|
| Handler | 定义处理 HTTP 请求的接口 | ServeHTTP(ResponseWriter, *Request) |
| ServeMux | 默认的路由多路复用器,将请求 URL 匹配到注册的 Handler | http.NewServeMux()、mux.HandleFunc() |
| Server | 监听端口、接受连接、分发请求 | http.Server{Addr: ":8080", Handler: mux} |
| ListenAndServe | 启动 HTTP 服务器并阻塞等待 | http.ListenAndServe(":8080", nil) |
3.1.2 最小化 Web 服务示例
以下代码创建了一个监听 8080 端口的 HTTP 服务,根路径返回 "Hello, Go!"。
package main
import (
"fmt"
"net/http"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, Go!")
}
func main() {
// 方式一:使用默认的 ServeMux(Handler 为 nil)
http.HandleFunc("/", helloHandler)
http.ListenAndServe(":8080", nil)
// 方式二:显式创建 ServeMux(推荐)
// mux := http.NewServeMux()
// mux.HandleFunc("/", helloHandler)
// http.ListenAndServe(":8080", mux)
}
实践要点: 在生产环境中,建议始终显式创建
http.Server 结构体,以便设置超时、读写缓冲区和错误日志等参数,避免使用默认值带来的安全隐患。
3.2 路由注册与模式匹配
net/http 标准库的路由器(ServeMux)支持基于路径前缀的模式匹配。理解其匹配规则是构建稳定 API 的基础。
3.2.1 路由注册方式
- 精确路径:
/api/login仅匹配该路径。 - 子树路径:
/api/匹配以/api/开头的所有路径,包括/api本身(会触发 301 重定向到/api/)。 - 根路径:
/匹配所有未被其他模式匹配的请求,通常作为兜底处理器。
3.2.2 路由注册示例
func main() {
mux := http.NewServeMux()
// 精确匹配
mux.HandleFunc("/health", healthHandler)
mux.HandleFunc("/api/login", loginHandler)
// 子树匹配:所有 /api/v1/ 开头的请求
mux.HandleFunc("/api/v1/", apiV1Handler)
// 兜底匹配
mux.HandleFunc("/", notFoundHandler)
http.ListenAndServe(":8080", mux)
}
func healthHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"status":"ok"}`))
}
func apiV1Handler(w http.ResponseWriter, r *http.Request) {
// r.URL.Path 可以获取完整路径,用于进一步路由
fmt.Fprintf(w, "API V1: %s", r.URL.Path)
}
常见陷阱: 当注册
/api(无尾部斜杠)和 /api/(有尾部斜杠)时,ServeMux 会对 /api 的请求自动发起 301 重定向到 /api/。如果希望 /api 和 /api/ 返回不同内容,需要分别注册两个处理器。
3.3 中间件概念引入
在限流熔断与权限验证骨架中,中间件是核心设计模式。它允许我们在不修改业务处理器的情况下,横向添加横切关注点(如日志、鉴权、限流)。
3.3.1 中间件的本质
中间件本质上是一个函数,它接收一个 http.Handler 作为参数,并返回一个新的 http.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("Request: %s %s", r.Method, r.URL.Path)
// 调用下一个处理器
next.ServeHTTP(w, r)
// 后置处理
log.Printf("Response sent for %s", r.URL.Path)
})
}
3.3.2 中间件链式组合
多个中间件可以嵌套组合,形成洋葱模型。请求从外层中间件进入,依次经过内层中间件,到达业务处理器,响应再反向返回。
// 组合多个中间件
func ChainMiddleware(handler http.Handler, middlewares ...Middleware) http.Handler {
for i := len(middlewares) - 1; i >= 0; i-- {
handler = middlewares[i](handler)
}
return handler
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/api/data", dataHandler)
// 应用中间件链:日志 -> 鉴权 -> 业务
wrappedHandler := ChainMiddleware(
mux,
LoggingMiddleware,
AuthMiddleware,
)
http.ListenAndServe(":8080", wrappedHandler)
}
架构价值: 在限流熔断骨架中,中间件模式使得我们可以将 令牌桶限流、滑动窗口计数、JWT 鉴权 等逻辑独立封装,通过配置即可灵活组合,无需侵入业务代码。
3.4 中间件与路由的集成架构
下图展示了中间件在 HTTP 请求处理链中的位置与数据流向。
3.5 构建可扩展的 HTTP 骨架
基于上述概念,我们可以构建一个支持中间件插拔的 HTTP 服务骨架,为后续集成限流、熔断和权限验证奠定基础。
3.5.1 骨架代码结构
package main
import (
"log"
"net/http"
"time"
)
// 定义中间件类型
type Middleware func(http.Handler) http.Handler
// 路由组:包含路径前缀和中间件列表
type RouteGroup struct {
prefix string
middlewares []Middleware
mux *http.ServeMux
}
// 创建路由组
func NewRouteGroup(prefix string) *RouteGroup {
return &RouteGroup{
prefix: prefix,
mux: http.NewServeMux(),
}
}
// 为路由组添加中间件
func (g *RouteGroup) Use(middlewares ...Middleware) {
g.middlewares = append(g.middlewares, middlewares...)
}
// 注册路由并应用组中间件
func (g *RouteGroup) Handle(pattern string, handler http.Handler) {
fullPattern := g.prefix + pattern
// 应用组中间件
for i := len(g.middlewares) - 1; i >= 0; i-- {
handler = g.middlewares[i](handler)
}
g.mux.Handle(fullPattern, handler)
}
func (g *RouteGroup) HandleFunc(pattern string, handlerFunc func(http.ResponseWriter, *http.Request)) {
g.Handle(pattern, http.HandlerFunc(handlerFunc))
}
// 超时中间件示例
func TimeoutMiddleware(timeout time.Duration) Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 使用 http.TimeoutHandler 包装
timeoutHandler := http.TimeoutHandler(next, timeout, "Request timeout")
timeoutHandler.ServeHTTP(w, r)
})
}
}
func main() {
// 创建 API 路由组
apiGroup := NewRouteGroup("/api/v1")
apiGroup.Use(
LoggingMiddleware,
TimeoutMiddleware(5*time.Second),
)
// 注册业务路由
apiGroup.HandleFunc("/users", listUsers)
apiGroup.HandleFunc("/orders", listOrders)
// 启动服务
server := &http.Server{
Addr: ":8080",
Handler: apiGroup.mux,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
log.Println("Server starting on :8080")
log.Fatal(server.ListenAndServe())
}
设计建议: 在限流熔断骨架中,建议将中间件分为两类:全局中间件(如日志、恢复、超时)和路由组中间件(如限流、鉴权)。全局中间件在顶层 Server 级别注入,路由组中间件在特定 API 前缀下生效,实现精细化的控制粒度。
3.6 本章小结
通过本章的学习,我们掌握了以下核心能力:
- 使用
net/http标准库快速搭建 HTTP 服务,理解 Handler、ServeMux、Server 三者的协作关系。 - 掌握路由注册的精确匹配与子树匹配规则,避免常见路由陷阱。
- 理解中间件的本质是
Handler的包装器,通过链式组合实现横切关注点的解耦。 - 构建了可扩展的 HTTP 骨架,支持按路由组灵活配置中间件,为后续集成限流、熔断与权限验证提供了清晰的架构基础。