前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Go语言经典库使用分析(六)| Negroni 中间件(二)

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

作者头像
飞雪无情
发布于 2018-08-28 03:23:43
发布于 2018-08-28 03:23:43
42700
代码可运行
举报
运行总次数:0
代码可运行

Go语言经典库使用分析,未完待续,欢迎扫码关注公众号flysnow_org或者网站http://www.flysnow.org/,第一时间看后续系列。觉得有帮助的话,顺手分享到朋友圈吧,感谢支持。

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

Negroni Handler处理器

本质上来说Negroni是一个HTTP Handler,因为他实现了HTTP Handler接口,所以他可以被http.ListenAndServe使用,其次Negroni本身内部又有一套自己的Handler处理链,通过他们可以达到处理http请求的目的,这些Handler处理链中的处理器,就是一个个中间件。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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内部注册的中间件。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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如何注册一个中间件的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
type middleware struct {
	handler Handler
	next    *middleware
}

middleware struct有很简单,有两个字段,一个是当前的Negroni Handler,一个是指向下一个middleware的指针next。有了这样一个middleware struct,就可以构建一个完美的中间件处理链了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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方法里。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func (n *Negroni) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	n.middleware.ServeHTTP(NewResponseWriter(rw), r)
}

从以上代码可以看出,调用了middleware.ServeHTTP方法,这就是中间件被执行的开始。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	m.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
}

middleware.ServeHTTP方法调用middleware中当前handler的ServeHTTP方法执行我们自己写的中间件处理逻辑。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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函数,那么中间件链的处理,到这里就断了。看一个自定义中间件的例子。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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)是什么呢?其实前面我们讲过。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	m.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
}

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

Go语言经典库使用分析,未完待续,欢迎扫码关注公众号flysnow_org或者网站http://www.flysnow.org/,第一时间看后续系列。觉得有帮助的话,顺手分享到朋友圈吧,感谢支持。

http Handler和negroni Handler之间的转换

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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.Handlerhttp.HandlerFuncUseHandlerFunc方法本质上还是调用的UseHandler方法,UseHandler方法实现的重点就是Wrap(handler),它完成了http.Handler到negroni.Handler的转换。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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的时候,就会有

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func Classic() *Negroni {
	return New(NewRecovery(), NewLogger(), NewStatic(http.Dir("public")))
}

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

编写自己的中间件

编写自己的中间件,在Negroni可以采用两种方式,一种是http.Handler的方式,这种方式优点是大家都熟悉,并且已经会了,缺点也有,就是不能控制中间件的处理链,默认是调用下一个中间件的,我们不能中断。

另外一个就是实现negroni.Handler的方式,Negroni推荐的也是这种方式。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//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的时候,就可以在控制台看到如下信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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/,第一时间看后续系列。觉得有帮助的话,顺手分享到朋友圈吧,感谢支持。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017年9月2日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Go语言经典库使用分析(五)| Negroni 中间件(一)
上一篇介绍的Gorilla Handlers中间件,严格来说不能称之为一个库或者框架,他只是一系列包装http.Handler的中间件函数,并且他们之间没有任何关系,也没有定义一种规则如何让我们基于它来开发我们自己的中间件,总之它是孤立的,中间件之间没有关系的。
飞雪无情
2018/08/28
7460
Go语言经典库使用分析(三)| Gorilla Handlers 详细介绍
在我们编写web服务端程序的时候,我们可能会对一些甚至全部的Http Request统一处理,比如我们记录每个访问的Request,对提交的Form表单进行映射等,要达到这些目的,比较优雅的做法是Http 中间件。
飞雪无情
2018/08/28
6830
Go语言中的中间件设计与实现
在Go语言中,中间件是一个强大的工具,用于在处理HTTP请求和响应之间插入逻辑层。中间件模式在Web开发中广泛使用,因为它允许我们灵活地添加日志、认证、限流等功能,而不会让核心路由代码变得复杂。
Jimaks
2024/05/04
3720
Go语言经典库使用分析(四)| Gorilla Handlers 源代码实现分析
上一篇 Go语言经典库使用分析(三)| Gorilla Handlers 详细介绍 中介绍了Handlers常用中间件的使用,这一篇介绍下这些中间件实现的原理,以了解他们的实现原理,更好的理解Go Http中间件的设计。
飞雪无情
2018/08/28
8320
Go语言经典库使用分析(二)| Gorilla Context
在Go1.7之前,Go标准库还没有内置Context的时候,如果我们想在一个Http.Request里附加值,怎么做呢?一般都是Map对象,存储对应的Request以及附加的值,然后在需要的时候取出来,今天我们介绍的这个就是实现了一个类似于这样功能的库,因为比较简单,而且实用,所以就先选择它来分析。
飞雪无情
2018/08/28
5690
学习Golang的HTTP中间件机制
因为 Golang 内置的 net/http 天生就支持 HTTP 中间件机制,所以即便不用 gin 之类的 Web 框架,我们也可以写出扩展性很好的 Web 应用。
LA0WAN9
2021/12/14
3740
学习Golang的HTTP中间件机制
Go语言经典库使用分析(七)| 高性能可扩展 HTTP 路由 httprouter
Go语言(golang)的一个很大的优势,就是很容易的开发出网络后台服务,而且性能快,效率高。在开发后端HTTP网络应用服务的时候,我们需要处理很多HTTP的请求访问,比如常见的API服务,我们就要处理很多HTTP请求,然后把处理的信息返回给使用者。对于这类需求,Golang提供了内置的net/http包帮我们来处理这些HTTP请求,让我们可以比较方便的开发一个HTTP服务。
飞雪无情
2020/02/10
1.2K0
Go 语言 Web 编程系列(六)—— 基于 gorilla/mux 包实现路由匹配:路由中间件
和 Laravel 路由一样,Mux 也支持在路由中使用中间件,并且按照顺序匹配执行。如果你对中间件不太了解,可以先去看下我们在 Laravel 中间件文档中的简单介绍:https://xueyuanjun.com/post/19926。和 Laravel 一样,在 Go Web 编程中,中间件的典型使用场景包括认证、日志、请求头操作和 ResponseWriter “劫持”等。
学院君
2020/01/17
1.4K0
Go 语言 Web 编程系列(六)—— 基于 gorilla/mux 包实现路由匹配:路由中间件
Go-记录请求日志中间件
在 Go Web 编程中,记录请求日志是非常常见的需求。记录请求日志可以帮助我们了解应用程序的运行情况,例如请求的数量、响应时间、客户端 IP 等等。这些信息可以帮助我们诊断问题、优化应用程序性能、满足监管要求等等。
堕落飞鸟
2023/04/23
6580
Go 每日一库之 negroni
negroni是一个专注于 HTTP 中间件的库。它小巧,无侵入,鼓励使用标准库net/http的处理器(Handler)。本文就来介绍一下这个库。
用户7731323
2020/09/08
5470
Go语言实战笔记(二十一)| Go 单元测试
相信我们做程序员的,对单元测试都不陌生。单元测试一般是用来测试我们的代码逻辑有没有问题,有没有按照我们期望的运行,以保证代码质量。
飞雪无情
2018/08/28
7540
Go语言实战笔记(二十一)| Go 单元测试
Go 每日一库之 net/http(基础和中间件)
几乎所有的编程语言都以Hello World作为入门程序的示例,其中有一部分以编写一个 Web 服务器作为实战案例的开始。每种编程语言都有很多用于编写 Web 服务器的库,或以标准库,或通过第三方库的方式提供。Go 语言也不例外。本文及后续的文章就去探索 Go 语言中的各个Web 编程框架,它们的基本使用,阅读它们的源码,比较它们优缺点。让我们先从 Go 语言的标准库net/http开始。标准库net/http让编写 Web 服务器的工作变得非常简单。我们一起探索如何使用net/http库实现一些常见的功能或模块,了解这些对我们学习其他的库或框架将会很有帮助。
用户7731323
2021/07/23
1.3K0
Go语言中间件框架 Negroni 的静态文件处理源码分析
Negroni是一个非常棒的中间件,尤其是其中间件调用链优雅的设计,以及对GO HTTP 原生处理器的兼容。我以前写过两篇文章,对Negroni进行了专门的分析,没有看过的朋友可以在看下。
飞雪无情
2018/10/10
5470
[译] 理解并用 Go 语言实现一个 HTTP 中间件
当运行在不同计算机上的客户端与服务器进行通信时,就需要使用中间件。通过本文,读者将会了解什么是中间件、中间件使用场景以及它们是如何在 Go 语言中构建的。
pseudoyu
2023/04/11
5730
[译] 理解并用 Go 语言实现一个 HTTP 中间件
在 Go Web 服务器中实现 TPS 限制
在我们的日常工作中,服务器的性能和稳定性至关重要。一个常见的问题是,当服务器接收到大量并发请求时,如果没有适当的控制机制,可能会导致服务器过载。为了解决这个问题,我们可以使用每秒事务数(TPS)限制,限制服务器在一秒内可以处理的请求数量。
运维开发王义杰
2023/08/10
4350
在 Go Web 服务器中实现 TPS 限制
「Go工具箱」一文读懂主流web框架中路由的实现原理
大家好,我是渔夫子。本号新推出「Go工具箱」系列,意在给大家分享使用go语言编写的、实用的、好玩的工具。同时了解其底层的实现原理,以便更深入地了解Go语言。
Go学堂
2023/01/31
8400
十分钟学会用Go编写Web中间件
中间件(通常)是一小段代码,它们接收一个请求,对其进行处理,每个中间件只处理一件事情,完成后将其传递给另一个中间件或最终处理程序,这样就做到了程序的解耦。如果没有中间件那么我们必须在最终的处理程序中来完成这些处理操作,这无疑会造成处理程序的臃肿和代码复用率不高的问题。中间件的一些常见用例是请求日志记录, Header操纵、 HTTP请求认证和 ResponseWriter劫持等等。
KevinYan
2020/02/17
2.7K0
十分钟学会用Go编写Web中间件
Web框架的设计方案和Go源码实现
那么为何要用web框架,或者说现在的主流web后端开发都要选定一个框架,然后再开发,就是为了提高效率,共通的业务以外的逻辑都由框架实现了,有了框架,开发只需要专注业务逻辑。
后端云
2022/11/25
3800
Web框架的设计方案和Go源码实现
Go-鉴权中间件
在 Web 应用程序中,身份验证和授权是非常重要的安全功能。为了实现这些功能,我们需要一种方法来验证用户身份并检查他们是否有权访问特定的资源。在 Go 中,我们可以使用中间件来实现鉴权功能。
堕落飞鸟
2023/04/23
6930
手把手教你用Go语言封装一个Web框架
Web框架是现代Web应用开发中必不可少的工具,它能够简化开发流程,提供常用的功能和工具,让开发者能够更专注于业务逻辑的实现。在本篇文章中,我们将介绍如何用Go语言封装一个简单的Web框架,以便快速构建Web应用。
大盘鸡拌面
2023/12/22
4680
推荐阅读
相关推荐
Go语言经典库使用分析(五)| Negroni 中间件(一)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验