Go语言如何实现简单中间件?从日志到认证手把手教


在Web开发中,中间件是无处不在的“幕后帮手”——它介于请求到达服务器和业务处理之间,默默处理日志记录、身份认证、请求限流等公共逻辑,让业务代码更聚焦核心功能。Go语言的net/http包天然支持中间件,且实现方式极其简洁。本文将从0到1,带你手写两个实用HTTP中间件,彻底理解Go中间件的核心原理。

一、Go中间件的核心:Handler接口

Go的HTTP处理围绕http.Handler接口展开,所有请求处理器都必须实现它:

type Handler interface {
    ServeHTTP(w http.ResponseWriter, r *http.Request)
}

http.HandlerFunc是该接口的适配器,能把普通函数func(http.ResponseWriter, *http.Request)转换为Handler。而中间件的本质,就是一个接收http.Handler并返回新http.Handler的函数,签名通常为:

func Middleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // 1. 请求前逻辑(如认证、日志)
        next.ServeHTTP(w, r) // 2. 调用下一个处理器(业务逻辑)
        // 3. 请求后逻辑(如耗时统计、响应处理)
    })
}

二、第一个中间件:请求日志记录

日志是最常见的中间件需求,我们需要记录请求方法、路径、耗时等信息。

步骤1:写基础业务Handler

先实现一个简单的Hello接口:

随机图片

import "fmt"

func helloHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, Go Middleware!")
}

步骤2:实现日志中间件

import (
    "log"
    "net/http"
    "time"
)

func LogMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // 请求前:记录开始时间
        start := time.Now()

        // 调用下一个处理器(业务逻辑)
        next.ServeHTTP(w, r)

        // 请求后:计算耗时并记录日志
        duration := time.Since(start)
        log.Printf("请求信息:method=%s, path=%s, 耗时=%v", 
            r.Method, r.URL.Path, duration)
    })
}

步骤3:注册并运行服务

func main() {
    // 用日志中间件包装业务Handler
    http.Handle("/hello", LogMiddleware(http.HandlerFunc(helloHandler)))

    log.Println("服务启动在: http://localhost:8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

运行后访问http://localhost:8080/hello,控制台会输出:

2024/05/20 10:00:00 请求信息:method=GET, path=/hello, 耗时=123.456µs

三、第二个中间件:简单身份认证

假设我们有一个受保护的接口,只有携带合法Token才能访问。

步骤1:实现认证中间件

func AuthMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // 从请求头获取Token(格式:Bearer xxx)
        token := r.Header.Get("Authorization")

        // 简化验证:固定合法Token
        if token != "Bearer valid-token-123" {
            http.Error(w, "认证失败:无效Token", http.StatusUnauthorized)
            return
        }

        // Token合法,继续调用下一个处理器
        next.ServeHTTP(w, r)
    })
}

步骤2:写受保护的业务Handler

func protectedHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "欢迎访问受保护接口!")
}

步骤3:串联多个中间件

中间件可以嵌套调用,顺序决定执行逻辑:

http.Handle("/protected", 
    AuthMiddleware(LogMiddleware(http.HandlerFunc(protectedHandler)))
)

执行顺序

  • 请求方向:AuthMiddlewareLogMiddlewareprotectedHandler
  • 响应方向:protectedHandlerLogMiddlewareAuthMiddleware

测试:

  • 无Token访问:返回401 Unauthorized
  • 携带合法Token(Header加Authorization: Bearer valid-token-123):正常返回,且日志记录请求

四、Go中间件的优势

  1. 解耦公共逻辑:日志、认证等与业务无关的代码抽离,业务Handler更专注核心功能;
  2. 高度复用:一个中间件可包装任意Handler,避免重复编码;
  3. 灵活串联:多个中间件可嵌套,顺序可按需调整(如先限流再认证);
  4. 零依赖:基于标准库实现,无需引入第三方框架,轻量高效。

总结

Go中间件的核心是http.Handler接口的灵活性,通过“接收Handler→返回新Handler”的模式,实现了公共逻辑的复用与解耦。掌握本文的日志和认证中间件后,你不仅能写出更简洁的Web服务,还能理解生态中(如gorilla/handlers、Echo框架)中间件的实现逻辑——这正是Go语言“简洁高效”的体现。

(全文约1050字)

发表评论