中间件这个东西其实指的很多,比如消息队列。可以说但凡是在业务逻辑之前的,都可以被说是中间件。比如鉴权,日志这些。go语言里面对中间件的使用比较有意思。先看一个简单的逻辑:
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.HandlerFunc.
首先定义两个中间件:
定义好以后,可以直接在主函数使用:
使用r.Use 使用自己定义好的中间件,就可以对所有挂载的访问路径实现拦截功能。但是如果只是某些挂载的请求进行拦截,可以这样:
这样就不会进行全局的拦截。
首先肯定是上面提到了,日志记录。然后还可以使用middleware做一些登录的信息获取以及鉴权。用户在最开始登录的时候,可以使用middleware给它一个cookie,然后再使用其他middleware就可以来获取这些信息,从而判断用户当前的状态,以及是不是具有权限等。