GO-中间件(Middleware )
中间件是一种计算机软件,可为操作系统提供的软件应用程序提供服务,以便于各个软件之间的沟通,特别是系统软件和应用软件。广泛用于web应用和面向服务的体系结构等。
纵观GO语言,中间件应用比较普遍,主要应用:
- 记录对服务器发送的请求(request)
- 处理服务器响应(response )
- 请求和处理之间做一个权限认证工作
- 远程调用
- 安全
- 等等
中间件处理程序是简单的http.Handler
,它包装另一个http.Handler
做请求的一些预处理和/或后处理。它被称为“中间件”,因为它位于Go Web服务器和实际处理程序之间的中间位置。
下面是一些中间件例子
记录日志中间件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | package main import ( "fmt" "log" "net/http" ) func logging(f http.HandlerFunc) http.HandlerFunc { return func (w http.ResponseWriter, r *http.Request) { log.Println(r.URL.Path) f(w, r) } } func foo(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "foo" ) } func bar(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "bar" ) } func main() { http.HandleFunc( "/foo" , logging(foo)) http.HandleFunc( "/bar" , logging(bar)) http.ListenAndServe( ":8080" , nil) } |
访问 http://localhost:8080/foo
返回结果
1 | foo |
终端打印
1 | 2020/05/16 22:56:10 /foo |
将上面示例修改下,也可以实现相同的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | package main import ( "fmt" "log" "net/http" ) func foo(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "foo" ) } func bar(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "bar" ) } func loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc( func (w http.ResponseWriter, r *http.Request) { log.Println(r.URL.Path) next.ServeHTTP(w, r) }) } func main() { http.Handle( "/foo" , loggingMiddleware(http.HandlerFunc(foo))) http.Handle( "/bar" , loggingMiddleware(http.HandlerFunc(bar))) http.ListenAndServe( ":8080" , nil) } |
访问 http://localhost:8080/foo
返回结果
1 | foo |
终端打印
1 | 2020/05/16 22:56:10 /foo |
多中间件例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | package main import ( "fmt" "log" "net/http" "time" ) type Middleware func (http.HandlerFunc) http.HandlerFunc // Logging logs all requests with its path and the time it took to process func Logging() Middleware { // Create a new Middleware return func (f http.HandlerFunc) http.HandlerFunc { // Define the http.HandlerFunc return func (w http.ResponseWriter, r *http.Request) { // Do middleware things start := time.Now() defer func () { log.Println(r.URL.Path, time.Since(start)) }() // Call the next middleware/handler in chain f(w, r) } } } // Method ensures that url can only be requested with a specific method, else returns a 400 Bad Request func Method(m string) Middleware { // Create a new Middleware return func (f http.HandlerFunc) http.HandlerFunc { // Define the http.HandlerFunc return func (w http.ResponseWriter, r *http.Request) { // Do middleware things if r.Method != m { http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } // Call the next middleware/handler in chain f(w, r) } } } // Chain applies middlewares to a http.HandlerFunc func Chain(f http.HandlerFunc, middlewares ...Middleware) http.HandlerFunc { for _, m := range middlewares { f = m(f) } return f } func Hello(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "hello world" ) } func main() { http.HandleFunc( "/" , Chain(Hello, Method( "GET" ), Logging())) http.ListenAndServe( ":8080" , nil) } |
访问 http://localhost:8080
返回结果
1 | hello world |
终端打印
1 | 2020/05/16 23:00:26 / 28.601µs |
中间件本身只是将其http.HandlerFunc
作为其参数之一,包装它并返回一个新http.HandlerFunc
的服务器来调用。在这里,我们定义了一种新类型Middleware
,最终可以更容易地将多个中间件链接在一起。
当然我们也可以改成如下形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | package main import ( "fmt" "log" "net/http" "time" ) type Middleware func (http.Handler) http.Handler func Hello(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "hello world" ) } func Chain(f http.Handler, mmap ...Middleware) http.Handler { for _, m := range mmap { f = m(f) } return f } func Method(m string) Middleware { return func (f http.Handler) http.Handler { return http.HandlerFunc( func (w http.ResponseWriter, r *http.Request) { log.Println(r.URL.Path) if r.Method != m { http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } f.ServeHTTP(w, r) }) } } func Logging() Middleware { return func (f http.Handler) http.Handler { return http.HandlerFunc( func (w http.ResponseWriter, r *http.Request) { //log.Println(r.URL.Path) // Do middleware things start := time.Now() defer func () { log.Println(r.URL.Path, time.Since(start)) }() f.ServeHTTP(w, r) }) } } func main() { http.Handle( "/" , Chain(http.HandlerFunc(Hello), Method( "GET" ), Logging())) http.ListenAndServe( ":8080" , nil) } |
在gin框架下实现中间件
1 2 | r := gin.Default() 创建带有默认中间件的路由,默认是包含logger和recovery中间件的 r :=gin.new() 创建带有没有中间件的路由 |
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | package main import ( "github.com/gin-gonic/gin" "log" "time" ) func Logger() gin.HandlerFunc { return func (c *gin.Context) { t := time.Now() // Set example variable c.Set( "example" , "12345" ) // before request c.Next() // after request latency := time.Since(t) log.Print(latency) //时间 0s // access the status we are sending status := c.Writer.Status() log.Println(status) //状态 200 } } func main() { r := gin.New() r.Use(Logger()) r.GET( "/test" , func (c *gin.Context) { example := c.MustGet( "example" ).(string) // it would print: "12345" log.Println(example) c.JSON(200, gin.H{ "code" : 200}) }) // Listen and serve on 0.0.0.0:8080 r.Run( ":8080" ) } |
访问 http://localhost:8080
返回结果
1 | { "code" :200} |
终端打印
1 2 3 | 2020/05/16 23:07:34 12345 2020/05/16 23:07:34 245.296µs 2020/05/16 23:07:34 200 |
以上示例也可改为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | package main import ( "github.com/gin-gonic/gin" "log" "time" ) func Logger() gin.HandlerFunc { return func (c *gin.Context) { t := time.Now() // Set example variable c.Set( "example" , "12345" ) // before request c.Next() // after request latency := time.Since(t) log.Print(latency) //时间 0s // access the status we are sending status := c.Writer.Status() log.Println(status) //状态 200 } } func main() { r := gin.New() r.GET( "/test" , Logger(), func (c *gin.Context) { example := c.MustGet( "example" ).(string) // it would print: "12345" log.Println(example) c.JSON(200, gin.H{ "code" : 200}) }) // Listen and serve on 0.0.0.0:8080 r.Run( ":8080" ) } |
Songzhibin
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 如何使用 Uni-app 实现视频聊天(源码,支持安卓、iOS)
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)