天天看点

Go语言经典库使用分析(六)| Negroni 中间件(二)

Go语言经典库使用分析,未完待续,欢迎扫码关注公众号

flysnow_org

或者网站http://www.flysnow.org/,第一时间看后续系列。觉得有帮助的话,顺手分享到朋友圈吧,感谢支持。

上一篇 Go语言经典库使用分析(五)| Negroni 中间件(一) 中介绍了Negroni中间的入门使用和一些介绍,比如如何添加中间等,中间件的路由等。这一篇主要讲原理,比如如何构建的中间处理链,如何编写自己的中间件等。

Negroni Handler处理器

本质上来说

Negroni

是一个HTTP Handler,因为他实现了HTTP Handler接口,所以他可以被

http.ListenAndServe

使用,其次

Negroni

本身内部又有一套自己的Handler处理链,通过他们可以达到处理http请求的目的,这些Handler处理链中的处理器,就是一个个中间件。

func (n *Negroni) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	n.middleware.ServeHTTP(NewResponseWriter(rw), r)
}           

复制

以上代码,是

Negroni

实现了HTTP的Handler,这样就和标注库里的http无缝整合了。实现了HTTP Handler,对于HTTP Request来说,就有一个统一的入口,所有的对HTTP Requet的处理,都会被

Negroni

通过

ServeHTTP

方法转交给

Negroni

内部注册的中间件。

type Handler interface {
	ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)
}

type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)           

复制

这是

Negroni

自己定义的Handler处理器,它和HTTP Handler非常相似,唯一不同的是多了一个

next

参数,这个

next

参数是组成中间件处理链的核心。

如何构建中间件处理链

我们已经知道了

Negroni

有自己的一套Handler中间件处理链,那么这个处理链和如何构建的呢?要想解开这个谜底,我们先看下

Negroni

如何注册一个中间件的。

type Negroni struct {
	middleware middleware
	handlers   []Handler
}

func (n *Negroni) Use(handler Handler) {
	if handler == nil {
		panic("handler cannot be nil")
	}

	n.handlers = append(n.handlers, handler)
	n.middleware = build(n.handlers)
}           

复制

在我们调用

Use

方法的时候,会把

Negroni

的Handler存在自己的

handlers

字段中,这是一个Slice类型字段,可以保存我们存放的Negroni Handler。同时会基于这个存放Negroni Handler的Slice构建中间件处理链

middleware

type middleware struct {
	handler Handler
	next    *middleware
}           

复制

middleware

struct有很简单,有两个字段,一个是当前的Negroni Handler,一个是指向下一个

middleware

的指针

next

。有了这样一个

middleware

struct,就可以构建一个完美的中间件处理链了。

func build(handlers []Handler) middleware {
	var next middleware

	if len(handlers) == 0 {
		return voidMiddleware()
	} else if len(handlers) > 1 {
		next = build(handlers[1:])
	} else {
		next = voidMiddleware()
	}

	return middleware{handlers[0], &next}
}

func voidMiddleware() middleware {
	return middleware{
		HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {}),
		&middleware{},
	}
}           

复制

以上代码就是构建一个中间件处理链的逻辑,这是一个递归的函数,逻辑比较简单。

handlers

参数为空的时候,直接通过

voidMiddleware

函数返回一个空的

middleware

。 当

handlers

参数只有1个处理器的时候,构建的

milldeware

就没有

next

了,所以

next

是通过

voidMiddleware

函数获得的,然后再通过

return middleware{handlers[0], &next}

组成生成的

middleware

并返回。

最后一种情况,就是有1个以上的Negroni Handler,那就通过

build

函数循环递归了,从第2个Handler开始,不停的往后递归处理,所以最先被添加的Negroni Handler会被放在中间件处理链的前面,也就意味着会被优先执行。

中间件如何被调用

构建好了处理器链,那么这些中间件如何被调用的呢?前面的章节,我们讲了Negroni是一个HTTP Handler,所以总的入口在

ServeHTTP

方法里。

func (n *Negroni) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	n.middleware.ServeHTTP(NewResponseWriter(rw), r)
}           

复制

从以上代码可以看出,调用了

middleware.ServeHTTP

方法,这就是中间件被执行的开始。

func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	m.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
}           

复制

middleware.ServeHTTP

方法调用

middleware

中当前handler的

ServeHTTP

方法执行我们自己写的中间件处理逻辑。

type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)

func (h HandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	h(rw, r, next)
}           

复制

又回到这里了吧,handler的

ServeHTTP

方法执行,本质上就是执行的我们自己定义的negroni.HandlerFunc。

当前的中间件被执行了,那么下一个如何被触发的?这就是我们自己定义的中间件函数中的next参数了。我们在自己的中间件处理结束后,如果觉得有必要,就需要调用next函数,继续执行下一个中间件,如果我们不调用next函数,那么中间件链的处理,到这里就断了。看一个自定义中间件的例子。

func (l *Logger) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	start := time.Now()

	next(rw, r)

	//省略无关代码
}           

复制

这是Negroni内置的log中间件的实现,可以看到,它调用了

next(rw, r)

函数,让中间件处理链继续执行。这个

next(rw, r)

是什么呢?其实前面我们讲过。

func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	m.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
}           

复制

next

函数的调用,就是对下一个

middleware

ServeHTTP

的再次调用,这是一个递归,也是中间件处理链设计的巧妙之处,灵活控制,自由的选择是否调用下个中间件。

Go语言经典库使用分析,未完待续,欢迎扫码关注公众号

flysnow_org

或者网站http://www.flysnow.org/,第一时间看后续系列。觉得有帮助的话,顺手分享到朋友圈吧,感谢支持。

http Handler和negroni Handler之间的转换

在刚介绍Negroni的时候,我们知道,它是兼容HTTP Handler的,Negroni可以直接把HTTP Handler转换为Negroni HTTP,让我们可以直接使用HTTP Handler作为Negroni的中间件,下面我们看下是如何转换的。

func (n *Negroni) UseHandler(handler http.Handler) {
	n.Use(Wrap(handler))
}

func (n *Negroni) UseHandlerFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request)) {
	n.UseHandler(http.HandlerFunc(handlerFunc))
}           

复制

Negroni提供两个方法,分别兼容

http.Handler

http.HandlerFunc

UseHandlerFunc

方法本质上还是调用的

UseHandler

方法,

UseHandler

方法实现的重点就是

Wrap(handler)

,它完成了http.Handler到negroni.Handler的转换。

func Wrap(handler http.Handler) Handler {
	return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
		handler.ServeHTTP(rw, r)
		next(rw, r)
	})
}           

复制

这个函数本质上是Negroni中间件的实现,它的代码逻辑就是先执行

http.Handler

中间件的实现,然后调用

next(rw, r)

执行下一个中间件。

内置中间件介绍

Negroni内置了几个中间件,当我们通过

Classic

函数创建一个

*Negroni

的时候,就会有

func Classic() *Negroni {
	return New(NewRecovery(), NewLogger(), NewStatic(http.Dir("public")))
}           

复制

NewRecovery

,

NewLogger

NewStatic

就是这几个内置的中间件,都是实现了negroni handler而成的中间件,具体源代码大家可以看下,这里就不具体介绍了。

编写自己的中间件

编写自己的中间件,在Negroni可以采用两种方式,一种是

http.Handler

的方式,这种方式优点是大家都熟悉,并且已经会了,缺点也有,就是不能控制中间件的处理链,默认是调用下一个中间件的,我们不能中断。

另外一个就是实现

negroni.Handler

的方式,Negroni推荐的也是这种方式。

//Blog:www.flysnow.org
//Wechat:flysnow_org
func main() {
	n := negroni.New()
	n.UseFunc(printAuthorInfo)

	router:=http.NewServeMux()
	router.Handle("/",handler())

	n.UseHandler(router)

	n.Run(":1234")

}

func printAuthorInfo(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc){
	fmt.Println("Blog:www.flysnow.org")
	fmt.Println("Wechat:flysnow_org")
	next(rw,r)
}


func handler() http.Handler{
	return http.HandlerFunc(myHandler)
}

func myHandler(rw http.ResponseWriter, r *http.Request) {
	rw.Header().Set("Content-Type", "text/plain")
	io.WriteString(rw,"Hello World")
}           

复制

我们通过

printAuthorInfo

函数实现了一个Negroni Handler中间件,这个中间件很简单,只是打印一些作者信息,然后就执行下一个中间件。

使用的时候,我们通过

n.UseFunc(printAuthorInfo)

注册这个中间件,我们启动服务,访问

http://localhost:1234

的时候,就可以在控制台看到如下信息:

Blog:www.flysnow.org
Wechat:flysnow_org           

复制

为了举例,我这个中间件比较简单,大家可以根据自己的需求实现满足自己业务的中间件。

一些中间件介绍

Github上有很多专门为Negroni开发的第三方中间件,以配合Negroni的使用,比如gzip的,oauth2.0的等,大家可以通过如下链接查看第三方的中间列表:

https://github.com/urfave/negroni#third-party-middleware

小结

到这里,Negroni这个中间件分析完了,可能还有一些没有分析的,比如

With

方法,

Run

方法,他们都是比较简单的,大家看下就可以了。

还有一个比较重要的是

NewResponseWriter

这个函数,它是对我们现在的

NewResponseWriter

的包装,增加了一些额外的信息,这个知识点我在这个篇 Go语言经典库使用分析(四)| Gorilla Handlers 源代码实现分析 文章里详细介绍过,就不再重复介绍了。

HTTP的中间件,就是一个HTTP的拦截器,Negroni对于该拦截器处理的更好,可以灵活控制,是否终端等,合理的使用Negroni,可以让你事半功倍。

Go语言经典库使用分析,未完待续,欢迎扫码关注公众号

flysnow_org

或者网站http://www.flysnow.org/,第一时间看后续系列。觉得有帮助的话,顺手分享到朋友圈吧,感谢支持。