我是靠谱客的博主 斯文面包,最近开发中收集的这篇文章主要介绍跟Gin一块搭建自己的web框架(四),觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

这一篇介绍Web框架中的中间件技术。

先来设想一个简单的场景,在处理每一个url的时候需要打印一条日志,说明收到了来自外界的请求。 考虑上一篇的情况,需要在两个handler里边都加上一个print的代码。对于少数的几个url的处理情况还可以介绍,但是一旦url非常多的时候,这个就变成了一个非常繁重并且容易出错的工作。

如果了解python,可能很容易的就想到在python中的处理方式:装饰器。只要定义一个打印日志的装饰器,任何加了这个装饰器的handler就能够实现打印日志的功能。

在Web框架中, 我们也要进行类似的处理,把非业务的需求和业务性的需求分离开来,类似这种打印日志的功能用中间件来实现。

先来看一下,如果没有用之前的基本框架,如何实现中间件的功能。

func hello(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hi there!")
}

func main() {
    http.HandleFunc("/", hello)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

以上是原始代码, 然后我们添加一个打印日志的中间件

func logMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(wr http.ResponseWriter, r *http.Request) {
       
        // next handler
        next.ServeHTTP(wr, r)
        logger.Println("receive request")
    })
}

func hello(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hi there!")
}

func main() {
    http.HandleFunc("/", logMiddleware(http.HandlerFunc(hello)))
    log.Fatal(http.ListenAndServe(":8080", nil))
}

其实原理也是跟python中的装饰器的原理一样,都是将原来的handler函数包裹在一个新函数中返回,在这个新函数中执行了一些非业务的代码,比如上边的打印日志。

其中最关键的步骤,是把hello函数,转成http.HandlerFunc,然后在middleware里边调用ServeHTTP。因为http.HandlerFunc()的参数是func (ResponseWriter, *Request),hello函数能够转成http.HandlerFunc()。 而http.HandlerFunc实现了http.Handler这个接口,所以能够在中间件中调用ServeHTTP函数。

类似的, 除了这个打印日志的中间件, 能够连环地添加多个中间件,比如

timeoutMiddleware(logMiddleware(http.HandlerFunc(hello)))

接下去再看上一篇中gin框架中如何用中间件。

首先我们要做的是把Context和RouterGroup中的HandlerFunc参数都改成slice格式的[]HandlerFunc,用来存放中间件。

我们定义一个user函数用来将中间件加入到上边说的函数链中

func (group *RouterGroup) Use(middlewares ...HandlerFunc) {
	group.Handlers = append(group.Handlers, middlewares...)
}

其实就是把新的中间件加入到了group的handler的slice中。

同时,在处理具体的GET请求的时候,接受的参数也需要从单一的HandlerFunc变成可变参数

func (group *RouterGroup) GET(path string, handlers ...HandlerFunc) {
	group.Handle("GET", path, handlers)
}

他可以接受多个handler函数作为参数传入,同样,group的Handle函数也做相应的调整

func (group *RouterGroup) Handle(method, p string, handlers []HandlerFunc) {
	p = path.Join(group.prefix, p)
	handlers = group.combineHandlers(handlers)
	group.engine.router.Handle(method, p, func(w http.ResponseWriter, req *http.Request, params httprouter.Params) {
		group.createContext(w, req, params, handlers).Next()
	})
}

这边多了个group.combineHandlers的步骤用来将多个handler聚合,然后一块传入到router。combineHandlers的作用其实就是将当前group的Handler和新传入的handler进行整合。

整合完之后调用的时候,需要将slice中的hander依次取出来调用

func (c *Context) Next() {
	c.index++
	s := int8(len(c.handlers))
	for ; c.index < s; c.index++ {
		c.handlers[c.index](c)
	}
}

最终我们在main函数中如何使用gin的中间件呢,可以对路由的整体加中间件,比如

func main() {
	r := gin.New()
	r.Use(gin.Logger())

	v1 := r.Group("/v1")
	{
		v1.GET("/login", v1IndexLoginfunc)
		v1.GET("/submit", v1IndexSubmitfunc)
	}

	// Listen and server on 0.0.0.0:8080
	r.Run(":8080")
}

也可以只针对某个group加中间件

func main() {
	r := gin.New()
	
	v1 := r.Group("/v1")
	v1.Use(gin.Logger())
	{
		v1.GET("/login", v1IndexLoginfunc)
		v1.GET("/submit", v1IndexSubmitfunc)
	}

	// Listen and server on 0.0.0.0:8080
	r.Run(":8080")
}

最终的代码参见: https://github.com/harleylau/myGin/tree/master/v0.2

下一篇扩展上下文Context的功能。

最后

以上就是斯文面包为你收集整理的跟Gin一块搭建自己的web框架(四)的全部内容,希望文章能够帮你解决跟Gin一块搭建自己的web框架(四)所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(45)

评论列表共有 0 条评论

立即
投稿
返回
顶部