Go的中间件

中间件

中间件这个东西其实指的很多,比如消息队列。可以说但凡是在业务逻辑之前的,都可以被说是中间件。比如鉴权,日志这些。go语言里面对中间件的使用比较有意思。先看一个简单的逻辑:

一个简单的http请求

package main

import (
	"log"
	"net/http"
)

func hello(wr http.ResponseWriter,r *http.Request){
	status,err:=wr.Write([]byte("hello,world"))
	if err!=nil{
		log.Print("write fail:%v",err)
	}else{
		log.Println("success: ",status)
	}
}
func main(){
	http.HandleFunc("/hello",hello)
	err:=http.ListenAndServe(":9999",nil)
	if err!=nil{
		log.Println("hand fail:%v",err)
	}
}

这是个很简单的http请求,这个请求挂载了一个hello服务。简单的调用一下:
在这里插入图片描述
现在如果说要计算这个函数的耗时,那么可以加点这样的逻辑:

package main

import (
	"log"
	"net/http"
	"os"
	"time"
)
var logger = log.New(os.Stdout, "", 0)
func hello(wr http.ResponseWriter,r *http.Request){
	timeStart:=time.Now()
	status,err:=wr.Write([]byte("hello,world"))
	if err!=nil{
		log.Print("write fail:%v",err)
	}else{
		log.Println("success: ",status)
	}
	timeElapsed:=time.Since(timeStart)
	logger.Println(timeElapsed)
}
func main(){
	http.HandleFunc("/hello",hello)
	err:=http.ListenAndServe(":9999",nil)
	if err!=nil{
		log.Println("hand fail:%v",err)
	}
}

请求一下可以看到日志:
在这里插入图片描述

存在的问题

其实很明显,新加的功能其实是修改了代码,这个是不符合程序设计的开闭原则。但是现在也不是oop?你给我讲OC原则?emmm,确实,但是如果这样的函数有很多,每个都加一下,其实也是不小的工作量,更难受的是容易漏掉。如果在java里面,我们可以用aop织如一个切面,来完成这个功能,那么在go里面呢?

问题所在

其实开发中,很多这样的问题。这些功能其实和业务不相关,但是这些信息需要统计。比如日志功能,服务渗透率统计,鉴权这些。其实这些东西和业务不是很相关,这里可以用中间件来剥离这些非业务逻辑,来完成功能的拓展。思考了java的aop实现,其实就是一种动态代理,核心要点就是使用了代理模式,所以可以参考这个逻辑,设计go语言里面的代理逻辑。

问题解决

package main

import (
	"log"
	"net/http"
	"os"
	"time"
)
var logger = log.New(os.Stdout, "", 0)
func hello(wr http.ResponseWriter,r *http.Request){
	status,err:=wr.Write([]byte("hello,world"))
	if err!=nil{
		log.Print("write fail:%v",err)
	}else{
		log.Println("success: ",status)
	}

}
func timeMiddleware(next http.HandlerFunc)http.Handler{
	return http.HandlerFunc(func(wr http.ResponseWriter, r *http.Request) {
		timeStart:=time.Now()
		// 业务逻辑
		next.ServeHTTP(wr,r)

		timeElapsed:=time.Since(timeStart)
		logger.Println(timeElapsed)
	})
}
func main(){
	http.Handle("/hello",timeMiddleware(hello))
	err:=http.ListenAndServe(":9999",nil)
	if err!=nil{
		log.Println("hand fail:%v",err)
	}
}

可以看到,这种模式就是可以很好地剥离出业务逻辑。但是这种写法并不是很优雅。其实可以看到,其实中间件就是一个函数,返回了一个处理函数,这个函数可以被说是代理函数。下面用gin框架怎么使用中间件为例,说明真实的开发中,是怎么使middleware的。

gin中的middleware

首先,gin中的middleware返回的处理函数类型是固定的,都是:gin.HandlerFunc.
首先定义两个中间件:
在这里插入图片描述定义好以后,可以直接在主函数使用:
在这里插入图片描述
使用r.Use 使用自己定义好的中间件,就可以对所有挂载的访问路径实现拦截功能。但是如果只是某些挂载的请求进行拦截,可以这样:
在这里插入图片描述
这样就不会进行全局的拦截。

middleware的一些应用场景

首先肯定是上面提到了,日志记录。然后还可以使用middleware做一些登录的信息获取以及鉴权。用户在最开始登录的时候,可以使用middleware给它一个cookie,然后再使用其他middleware就可以来获取这些信息,从而判断用户当前的状态,以及是不是具有权限等。

原创文章 386 获赞 63 访问量 7万+

猜你喜欢

转载自blog.csdn.net/weixin_41863129/article/details/105885515