中间件

每个请求在到达你的核心处理逻辑之前,或者在响应发送给客户端之后,都会先经过这些中间件。


中间件的签名Signature

在Go的标准库 net/http 中,一个中间件本质上是一个函数,它接收一个 http.Handler 作为参数,并且返回一个新的 http.Handler。

听起来有点绕?别担心,我们把它拆开来看。


它的“类型签名”可以这样定义:

type Middleware func(http.Handler) http.Handler

让我们用一个具体的函数骨架来展示这个结构:

func MyMiddleware(next http.Handler) http.Handler {
    
    // 我们返回一个新的Handler,通常用http.HandlerFunc来快速创建
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        
        // ----------------------------------------------------
        // 1. 在调用“下一个”Handler之前的逻辑
        //    比如:记录请求开始、检查用户权限等
        // ----------------------------------------------------
        
        // 调用链中的下一个Handler(这可能是另一个中间件,也可能是最终的业务逻辑)
        next.ServeHTTP(w, r) 
        
        // ----------------------------------------------------
        // 2. 在调用“下一个”Handler之后的逻辑
        //    比如:记录请求耗时、修改响应头等
        // ----------------------------------------------------
    })
}

代码实践

创建一个日志中间件,它能完成两件事:

  1. 在请求到达时,打印出请求的方法(GET, POST 等)和路径(URL)。
  2. 在请求处理完成后,计算并打印出整个请求花费了多长时间。
package main

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

func loggingMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()

		log.Printf("Incoming request: Method=%s, Path=%s", r.Method, r.URL.Path)

		// 让下一个handler处理请求
		next.ServeHTTP(w, r)

		log.Printf("Request completed in %v", time.Since(start))

	})
}
func helloHandler(w http.ResponseWriter, r *http.Request) {
	// Simulate some work
	time.Sleep(100 * time.Millisecond)
	log.Printf("hello\n")
	fmt.Fprintln(w, "Hello, Gopher!")
}

func main() {
	finalHelloHandler := http.HandlerFunc(helloHandler)
	http.Handle("/", loggingMiddleware(finalHelloHandler))

	log.Println("Server starting on port 8080...")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		log.Fatal(err)
	}

}

Gin的中间件

在Gin中,一个中间件就是一个 gin.HandlerFunc,它的签名是:

func(c *gin.Context)

c.Next()
它的作用就是告诉Gin:“我这个中间件的‘前置’任务做完了,现在请继续执行下一个中间件或者最终的业务处理函数”

func Logger() gin.HandlerFunc {
	return func(context *gin.Context) {
		start := time.Now()
		context.Next()
		log.Printf("time :%v", time.Since(start))
	}
}

func main() {
	// 1. Create a Gin router
	router := gin.Default()

	// 2. Use our middleware globally
	router.Use(Logger())

	// 3. Define a route
	router.GET("/test", func(c *gin.Context) {
		// Simulate some work
		time.Sleep(50 * time.Millisecond)
		c.String(http.StatusOK, "Hello, Gin!")
	})

	// 4. Start the server
	router.Run(":8080")
}

在这里插入图片描述


补充

  1. 状态码 (http.StatusOK):这就像是邮寄包裹时,在包裹外面盖的一个章,比如“已签收”或“地址错误”。它告诉接收方(浏览器)这个请求的结果是怎样的。
  2. 响应内容 (“Hello, Gin!”):这才是包裹里的东西,是用户真正想看到的内容。

所以,c.String(http.StatusOK, “Hello, Gin!”) 这行代码的完整意思是:“向浏览器发送一个状态为 200 OK 的成功响应,响应的内容是字符串Hello, Gin!

更多推荐