亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

深入了解Go語(yǔ)言中web框架的中間件運(yùn)行機(jī)制

 更新時(shí)間:2023年02月02日 11:46:11   作者:Go學(xué)堂  
大家在使用iris框架搭建web系統(tǒng)時(shí),一定會(huì)用到中間件。那么你了解中間件的運(yùn)行機(jī)制嗎?你知道為什么在iris和gin框架的請(qǐng)求處理函數(shù)中要加c.Next()函數(shù)嗎?本文就和大家一起探究該問(wèn)題的答案

一、中間件的基本使用

在web開(kāi)發(fā)中,中間件起著很重要的作用。比如,身份驗(yàn)證、權(quán)限認(rèn)證、日志記錄等。以下就是各框架對(duì)中間件的基本使用。

1.1 iris框架中間件的使用

package main

import (
	"github.com/kataras/iris/v12"
	"github.com/kataras/iris/v12/context"

	"github.com/kataras/iris/v12/middleware/recover"
)

func main() {
	app := iris.New()

	//通過(guò)use函數(shù)使用中間件recover
	app.Use(recover.New())

	app.Get("/home",func(ctx *context.Context) {
		ctx.Write([]byte("Hello Wolrd"))
	})

	app.Listen(":8080")
}

1.2 gin框架中使用中間件

package main

import (
	"github.com/gin-gonic/gin"
)

func main() {
	g := gin.New()
    // 通過(guò)Use函數(shù)使用中間件
	g.Use(gin.Recovery())
    
	g.GET("/", func(ctx *gin.Context){
		ctx.Writer.Write([]byte("Hello World"))
	})

	g.Run(":8000")
}

1.3 echo框架中使用中間件示例

package main

import (
	v4echo "github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
)

func main() {
	e := v4echo.New()
    // 通過(guò)use函數(shù)使用中間件Recover
	e.Use(middleware.Recover())
	e.GET("/home", func(c v4echo.Context) error {
		c.Response().Write([]byte("Hello World"))
		return nil
	})

	e.Start(":8080")
}

首先我們看下三個(gè)框架中使用中間件的共同點(diǎn):

  • 都是使用Use函數(shù)來(lái)使用中間件
  • 都內(nèi)置了Recover中間件
  • 都是先執(zhí)行中間件Recover的邏輯,然后再輸出Hello World

接下來(lái)我們繼續(xù)分析中間件的具體實(shí)現(xiàn)。

二、中間件的實(shí)現(xiàn)

2.1 iris中間件實(shí)現(xiàn)

2.1.1 iris框架中間件類(lèi)型

首先,我們看下Use函數(shù)的簽名,如下:

func (api *APIBuilder) Use(handlers ...context.Handler) {
	api.middleware = append(api.middleware, handlers...)
}

在該函數(shù)中,handlers是一個(gè)不定長(zhǎng)參數(shù),說(shuō)明是一個(gè)數(shù)組。參數(shù)類(lèi)型是context.Handler,我們?cè)賮?lái)看context.Handler的定義如下:

type Handler func(*Context)

這個(gè)類(lèi)型是不是似曾相識(shí)。是的,在注冊(cè)路由時(shí)定義的請(qǐng)求處理器也是該類(lèi)型。如下:

func (api *APIBuilder) Get(relativePath string, handlers ...context.Handler) *Route {
	return api.Handle(http.MethodGet, relativePath, handlers...)
}

總結(jié):在iris框架上中間件也是一個(gè)請(qǐng)求處理器。通過(guò)Use函數(shù)使用中間件,實(shí)際上是將該中間件統(tǒng)一加入到了api.middleware切片中。該切片我們?cè)诤竺嬖偕钊胙芯?/strong>。

2.1.2 iris中自定義中間件

了解了中間件的類(lèi)型,我們就可以根據(jù)其規(guī)則來(lái)定義自己的中間件了。如下:

import "github.com/kataras/iris/v12/context"

func CustomMiddleware(ctx *context.Context) {
	fmt.Println("this is the custom middleware")
	// 具體的處理邏輯
	
	ctx.Next()
}

當(dāng)然,為了代碼風(fēng)格統(tǒng)一,也可以類(lèi)似Recover中間件那樣定義個(gè)包,然后定義個(gè)New函數(shù),New函數(shù)返回的是一個(gè)中間件函數(shù),如下:

package CustomMiddleware 

func New() context.Handler {
	return func(ctx *context.Context) {
    	fmt.Println("this is the custom middleware")
		// 具體的處理邏輯

		ctx.Next()
	}
}

到此為止,你有沒(méi)有發(fā)現(xiàn),無(wú)論是自定義的中間件,還是iris框架中已存在的中間件,在最后都有一行ctx.Next()代碼。那么,該為什么要有這行代碼呢? 通過(guò)函數(shù)名可以看到執(zhí)行下一個(gè)請(qǐng)求處理器。 再結(jié)合我們?cè)谑褂肬se函數(shù)使用中間件的時(shí)候,是把該中間件處理器加入到了一個(gè)切片中。所以,Next和請(qǐng)求處理器切片是有關(guān)系的。這個(gè)我們?cè)谙挛牡倪\(yùn)行機(jī)制部分詳細(xì)解釋。

2.2 gin中間件的實(shí)現(xiàn)

2.2.1 gin框架中間件類(lèi)型

同樣先查看gin的Use函數(shù)的簽名和實(shí)現(xiàn),如下:

func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
	engine.RouterGroup.Use(middleware...)
	engine.rebuild404Handlers()
	engine.rebuild405Handlers()
	return engine
}

在gin框架的Use函數(shù)中,middleware也是一個(gè)不定長(zhǎng)的參數(shù),其參數(shù)類(lèi)型是HandlerFunc。而HandlerFunc的定義如下:

type HandlerFunc func(*Context)

同樣,在gin框架中注冊(cè)路由時(shí)指定的請(qǐng)求處理器的類(lèi)型也是HandlerFunc,即func(*Context)。我們?cè)倏碪se中的第2行代碼engine.RouterGroup.Use(middleware...)的實(shí)現(xiàn):

func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
	group.Handlers = append(group.Handlers, middleware...)
	return group.returnObj()
}

同樣,也是將中間件加入到了路由的Handlers切片中。

總結(jié):在gin框架中,中間件也是一個(gè)請(qǐng)求處理函數(shù)。通過(guò)Use函數(shù)使用中間件,實(shí)際上也是將該中間件統(tǒng)一加入到了group.Handlers切片中。

2.2.2 gin中自定義中間件

了解了gin的中間件類(lèi)型,我們就可以根據(jù)其規(guī)則來(lái)定義自己的中間件了。如下:

import "github.com/gin-gonic/gin"

func CustomMiddleware(ctx *gin.Context) {
	fmt.Println("this is gin custom middleware")
	//	處理邏輯
	ctx.Next()
}

當(dāng)然,為了代碼風(fēng)格統(tǒng)一,也可以類(lèi)似Recover中間件那樣返回一個(gè),然后定義個(gè)New函數(shù),New函數(shù)返回的是一個(gè)中間件函數(shù),如下:

func CustomMiddleware() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		fmt.Println("this is gin custom middleware")
		//	處理邏輯
		ctx.Next()
	}
}

同樣,在gin的中間件中,代碼的最后一行也是ctx.Next()函數(shù)。如果不要這行代碼行不行呢?和iris的道理是一樣的,我們也在下文的運(yùn)行機(jī)制中講解。

2.3 echo框架中間件的實(shí)現(xiàn)

2.3.1 echo框架中間件類(lèi)型

func (e *Echo) Use(middleware ...MiddlewareFunc) {
	e.middleware = append(e.middleware, middleware...)
}

在echo框架中,Use函數(shù)中的middleware參數(shù)也是一個(gè)不定長(zhǎng)參數(shù),說(shuō)明可以添加多個(gè)中間件。其類(lèi)型是MiddlewareFunc。如下是MiddewareFunc類(lèi)型的定義:

type MiddlewareFunc func(next HandlerFunc) HandlerFunc

這個(gè)中間件的函數(shù)類(lèi)型跟iris和gin的不一樣。該函數(shù)類(lèi)型接收一個(gè)HandlerFunc,并返回一個(gè)HanderFunc。而HanderFunc的定義如下:

HandlerFunc func(c Context) error

HanderFunc類(lèi)型才是指定路由時(shí)的請(qǐng)求處理器類(lèi)型。我們?cè)倏聪耬cho框架中Use的實(shí)現(xiàn),也是將middleware加入到了一個(gè)全局的切片中。

總結(jié):在echo框架中,中間件是一個(gè)輸入請(qǐng)求處理器,并返回一個(gè)新請(qǐng)求處理器的函數(shù)類(lèi)型。這是和iris和gin框架不一樣的地方。通過(guò)Use函數(shù)使用中間件,也是將該中間件統(tǒng)一加入到全局的中間件切片中。

2.3.2 echo中自定義中間件

了解了echo的中間件類(lèi)型,我們就可以根據(jù)其規(guī)則來(lái)定義自己的中間件了。如下:

import (
	v4echo "github.com/labstack/echo/v4"
)

func CustomMiddleware(next v4echo.HandlerFunc) v4echo.HandlerFunc {
	return func(c v4echo.Context) error {
		fmt.Println("this is echo custom middleware")
		// 中間件處理邏輯
		return next(c)
	}
}

這里中間件的實(shí)現(xiàn)看起來(lái)比較復(fù)雜,做下簡(jiǎn)單的解釋。根據(jù)上面可知,echo的中間件類(lèi)型是輸入一個(gè)請(qǐng)求處理器,然后返回一個(gè)新的請(qǐng)求處理器。在該函數(shù)中,從第6行到第10行該函數(shù)其實(shí)是中間件的執(zhí)行邏輯。第9行的next(c)實(shí)際上是要執(zhí)行下一個(gè)請(qǐng)求處理器的邏輯,類(lèi)似于iris和gin中的ctx.Next()函數(shù)。** 本質(zhì)上是用一個(gè)新的請(qǐng)求處理器(返回的請(qǐng)求處理器)包裝了一下舊的請(qǐng)求處理器(輸入的next請(qǐng)求處理器)**。

中間件的定義和使用都介紹了。那么,中間件和具體路由中的請(qǐng)求處理器是如何協(xié)同工作的呢?下面我們介紹中間件的運(yùn)行機(jī)制。

三、中間件的運(yùn)行機(jī)制

3.1 iris中間件的運(yùn)行機(jī)制

根據(jù)上文介紹,我們知道使用iris.Use函數(shù)之后,是將中間件加入到了APIBuilder結(jié)構(gòu)體的middleware切片中。那么,該middleware是如何和路由中的請(qǐng)求處理器相結(jié)合的呢?我們還是從注冊(cè)路由開(kāi)始看。

	app.Get("/home",func(ctx *context.Context) {
		ctx.Write([]byte("Hello Wolrd"))
	})

使用Get函數(shù)指定一個(gè)路由。該函數(shù)的第二個(gè)參數(shù)就是對(duì)應(yīng)的請(qǐng)求處理器,我們稱(chēng)之為handler。然后,查看Get的源代碼,一直到APIBuilder.handle函數(shù),在該函數(shù)中有創(chuàng)建的路由的邏輯,如下:

routes := api.createRoutes(errorCode, []string{method}, relativePath, handlers...)

在api.createRoutes函數(shù)的入?yún)⒅?,我們只需關(guān)注handlers,該handlers即是在app.Get中傳遞的handler。繼續(xù)進(jìn)入api.createRoutes函數(shù)中,該函數(shù)是創(chuàng)建路由的邏輯。其實(shí)現(xiàn)如下:

func (api *APIBuilder) createRoutes(errorCode int, methods []string, relativePath string, handlers ...context.Handler) []*Route {
	//...省略代碼

	var (
		// global middleware to error handlers as well.
		beginHandlers = api.beginGlobalHandlers
		doneHandlers  = api.doneGlobalHandlers
	)

	if errorCode == 0 {
		beginHandlers = context.JoinHandlers(beginHandlers, api.middleware)
		doneHandlers = context.JoinHandlers(doneHandlers, api.doneHandlers)
	} else {
		beginHandlers = context.JoinHandlers(beginHandlers, api.middlewareErrorCode)
	}

	mainHandlers := context.Handlers(handlers)

	//...省略代碼
    
	routeHandlers := context.JoinHandlers(beginHandlers, mainHandlers)
	// -> done handlers
	routeHandlers = context.JoinHandlers(routeHandlers, doneHandlers)

    //...省略代碼
	routes := make([]*Route, len(methods))
	// 構(gòu)建routes對(duì)應(yīng)的handler
	for i, m := range methods { // single, empty method for error handlers.
		route, err := NewRoute(api, errorCode, m, subdomain, path, routeHandlers, *api.macros)
    	// ...省略代碼
		routes[i] = route
	}

	return routes
}

這里省略了大部分的代碼,只關(guān)注和中間件及對(duì)應(yīng)的請(qǐng)求處理器相關(guān)的邏輯。從實(shí)現(xiàn)上來(lái)看,可以得知:

  • 首先看第12行,將全局的beginGlobalHandlers(即beginHandlers)和中間件api.middleware進(jìn)行合并。這里的api.middleware就是我們開(kāi)頭處使用Use函數(shù)加入的中間件。
  • 再看第18行和22行,18行是將路由的請(qǐng)求處理器轉(zhuǎn)換成了切片 []Handler切片。這里的handlers就是使用Get函數(shù)進(jìn)行注冊(cè)的路由。22行是將beginHandlers和mainHandlers進(jìn)行合并,可以簡(jiǎn)單的認(rèn)為是將api.middlewares和路由注冊(cè)時(shí)的請(qǐng)求處理器進(jìn)行了合并。這里需要注意的是,通過(guò)合并請(qǐng)求處理器,中間件的處理器排在前面,具體的路由請(qǐng)求處理器排在了后面。
  • 再看第24行,將合并后的請(qǐng)求處理器再和全局的doneHandlers進(jìn)行合并。這里可暫且認(rèn)為doneHandlers為空。

根據(jù)以上邏輯,對(duì)于一個(gè)具體的路由來(lái)說(shuō),其對(duì)應(yīng)的請(qǐng)求處理器不僅僅是自己指定的那個(gè),而是形成如下順序的一組請(qǐng)求處理器

接下來(lái),我們?cè)倏丛诼酚善ヅ溥^(guò)程中,即匹配到了具體的路由后,這一組請(qǐng)求處理器是如何執(zhí)行的。

在iris中,路由匹配的過(guò)程是在文件的/iris/core/router/handler.go文件中的routerHandler結(jié)構(gòu)體的HandleRequest函數(shù)中執(zhí)行的。如下:

func (h *routerHandler) HandleRequest(ctx *context.Context) {
	method := ctx.Method()
	path := ctx.Path()
	// 省略代碼...

	for i := range h.trees {
		t := h.trees[i]

    	// 省略代碼...

        // 根據(jù)路徑匹配具體的路由
		n := t.search(path, ctx.Params())
		if n != nil {
			ctx.SetCurrentRoute(n.Route)
            // 這里是找到了路由,并執(zhí)行具體的請(qǐng)求邏輯
			ctx.Do(n.Handlers)
			// found
			return
		}
		// not found or method not allowed.
		break
	}

	ctx.StatusCode(http.StatusNotFound)
}

在匹配到路由后,會(huì)執(zhí)行該路由對(duì)應(yīng)的請(qǐng)求處理器n.Handlers,這里的Handlers就是上面提到的那組包含中間件的請(qǐng)求處理器數(shù)組。我們?cè)賮?lái)看ctx.Do函數(shù)的實(shí)現(xiàn):

func (ctx *Context) Do(handlers Handlers) {
	if len(handlers) == 0 {
		return
	}

	ctx.handlers = handlers
	handlers[0](ctx)
}

這里看到在第7行中,首先執(zhí)行第1個(gè)請(qǐng)求處理器。到這里是不是有疑問(wèn):handlers既然是一個(gè)切片,那后面的請(qǐng)求處理器是如何執(zhí)行的呢?這里就涉及到在每個(gè)請(qǐng)求處理器中都有一個(gè)ctx.Next函數(shù)了。我們?cè)倏聪耤tx.Nex函數(shù)的實(shí)現(xiàn):

func (ctx *Context) Next() {
	// ...省略代碼
	nextIndex, n := ctx.currentHandlerIndex+1, len(ctx.handlers)
	if nextIndex < n {
		ctx.currentHandlerIndex = nextIndex
		ctx.handlers[nextIndex](ctx)
	}
}

這里我們看第11行到15行的代碼。在ctx中有一個(gè)當(dāng)前執(zhí)行到哪個(gè)handler的下標(biāo)currentHandlerIndex,如果還有未執(zhí)行完的hander,則繼續(xù)執(zhí)行下一個(gè),即ctx.handlers[nextIndex](ctx)這也就是為什么在每個(gè)請(qǐng)求處理器中都應(yīng)該加一行ctx.Next的原因。如果不加改行代碼,則就執(zhí)行不到后續(xù)的請(qǐng)求處理器。

完整的執(zhí)行流程如下:

3.2 gin中間件運(yùn)行機(jī)制

由于gin和iris都是使用數(shù)組來(lái)存儲(chǔ)中間件,所以中間件運(yùn)行的機(jī)制本質(zhì)上是和iris一樣的。也是在注冊(cè)路由時(shí),將中間件的請(qǐng)求處理器和路由的請(qǐng)求處理器進(jìn)行合并后作為該路由的最終的請(qǐng)求處理器組。在匹配到路由后,也是通過(guò)先執(zhí)行請(qǐng)求處理器組的第一個(gè)處理器,然后調(diào)用ctx.Next()函數(shù)進(jìn)行迭代調(diào)用的。

但是,gin的請(qǐng)求處理器比較簡(jiǎn)單,只有中間件和路由指定的請(qǐng)求處理器組成。我們還是從路由注冊(cè)指定請(qǐng)求處理器開(kāi)始,如下

	g.GET("/", func(ctx *gin.Context){
		ctx.Writer.Write([]byte("Hello World"))
	})

進(jìn)入GET的源代碼,直到進(jìn)入到/gin/routergroup.go文件中的handle源碼,如下:

func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
	absolutePath := group.calculateAbsolutePath(relativePath)
	handlers = group.combineHandlers(handlers)
	group.engine.addRoute(httpMethod, absolutePath, handlers)
	return group.returnObj()
}

在該函數(shù)中我們可以看到第3行處是將group.combineHandlers(handlers),由名字可知是對(duì)請(qǐng)求處理器進(jìn)行組合。我們進(jìn)入繼續(xù)查看:

func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
	finalSize := len(group.Handlers) + len(handlers)
	assert1(finalSize < int(abortIndex), "too many handlers")
	mergedHandlers := make(HandlersChain, finalSize)
	copy(mergedHandlers, group.Handlers)
	copy(mergedHandlers[len(group.Handlers):], handlers)
	return mergedHandlers
}

在第5行,是先將group.Handlers即中間件加入到mergedHandlers,然后再第6行再將路由具體的handlers加入到mergedHandlers,最后將組合好的mergedHandlers作為該路由最終的handlers。如下:

接下來(lái),我們?cè)倏丛诼酚善ヅ溥^(guò)程中,即匹配到了具體的路由后,這一組請(qǐng)求處理器是如何執(zhí)行的。

在gin中,路由匹配的邏輯是在/gin/gin.go文件的Engine.handleHTTPRequest函數(shù)中,如下:

func (engine *Engine) handleHTTPRequest(c *Context) {
	httpMethod := c.Request.Method
	rPath := c.Request.URL.Path
	// ...省略代碼
	
	t := engine.trees
	for i, tl := 0, len(t); i < tl; i++ {
    	// ...省略代碼
		root := t[i].root
		// Find route in tree
		value := root.getValue(rPath, c.params, c.skippedNodes, unescape)
    	//...省略代碼
		if value.handlers != nil {
			c.handlers = value.handlers
			c.fullPath = value.fullPath
			c.Next()
			c.writermem.WriteHeaderNow()
			return
		}
		// ...省略代碼
		break
	}

	// ...省略代碼
}

匹配路由以及執(zhí)行對(duì)應(yīng)路由處理的邏輯是在第13行到18行。在第14行,首先將匹配到的路由的handlers(即中間件+具體的路由處理器)賦值給上下文c,然后執(zhí)行c.Next()函數(shù)。c.Next()函數(shù)如下:

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

Next函數(shù)中直接就是使用下標(biāo)c.index進(jìn)行循環(huán)handlers的執(zhí)行。這里需要注意的是c.index是從-1開(kāi)始的。所以先進(jìn)行c.index++則初始值就是0。整體執(zhí)行流程如下:

3.3 echo中間件的運(yùn)行機(jī)制

根據(jù)上文介紹,我們知道使用echo.Use函數(shù)來(lái)注冊(cè)中間件,注冊(cè)的中間件是放到了Echo結(jié)構(gòu)體的middleware切片中。那么,該middleware是如何和路由中的請(qǐng)求處理器相結(jié)合的呢?我們還是從注冊(cè)路由開(kāi)始看。

	e.GET("/home", func(c v4echo.Context) error {
		c.Response().Write([]byte("Hello World"))
		return nil
	})

使用Get函數(shù)指定一個(gè)路由。該函數(shù)的第二個(gè)參數(shù)就是對(duì)應(yīng)的請(qǐng)求處理器,我們稱(chēng)之為handler。當(dāng)然,在該函數(shù)中還有第三個(gè)可選的參數(shù)是針對(duì)該路由的中間件的,其原理和全局的中間件是一樣的。

echo框架的中間件和路由的處理器結(jié)合并是在路由注冊(cè)的時(shí)候進(jìn)行的,而是在匹配到路由后才結(jié)合的。其邏輯是在Echo的ServeHTTP函數(shù)中,如下:

func (e *Echo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Acquire context
	c := e.pool.Get().(*context)
	c.Reset(r, w)
	var h HandlerFunc

	if e.premiddleware == nil {
		e.findRouter(r.Host).Find(r.Method, GetPath(r), c)
		h = c.Handler()
		h = applyMiddleware(h, e.middleware...)
	} else {
		h = func(c Context) error {
			e.findRouter(r.Host).Find(r.Method, GetPath(r), c)
			h := c.Handler()
			h = applyMiddleware(h, e.middleware...)
			return h(c)
		}
		h = applyMiddleware(h, e.premiddleware...)
	}

	// Execute chain
	if err := h(c); err != nil {
		e.HTTPErrorHandler(err, c)
	}

	// Release context
	e.pool.Put(c)
}

在該函數(shù)的第10行或第18行。我們接著看第10行中的applyMiddleware(h, e.middleware...)函數(shù)的實(shí)現(xiàn):

func applyMiddleware(h HandlerFunc, middleware ...MiddlewareFunc) HandlerFunc {
	for i := len(middleware) - 1; i >= 0; i-- {
		h = middleware[i](h)
	}
	return h
}

這里的h是注冊(cè)路由時(shí)指定的請(qǐng)求處理器。middelware就是使用Use函數(shù)注冊(cè)的所有的中間件。這里實(shí)際上循環(huán)對(duì)h進(jìn)行層層包裝。 索引i從middleware切片的最后一個(gè)元素開(kāi)始執(zhí)行,這樣就實(shí)現(xiàn)了先試用Use函數(shù)注冊(cè)的中間件先執(zhí)行。

這里的實(shí)現(xiàn)跟使用數(shù)組實(shí)現(xiàn)不太一樣。我們以使用Recover中間件為例看下具體的嵌套過(guò)程。

package main

import (
	v4echo "github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
)

func main() {
	e := v4echo.New()
    // 通過(guò)use函數(shù)使用中間件Recover
	e.Use(middleware.Recover())
	e.GET("/home", func(c v4echo.Context) error {
		c.Response().Write([]byte("Hello World"))
		return nil
	})

	e.Start(":8080")
}

這里的Recover中間件實(shí)際上是如下函數(shù):

func(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		if config.Skipper(c) {
			return next(c)
		}

		defer func() {
			// ...省略具體邏輯代碼
		}()
		return next(c)
	}
}

然后路由對(duì)應(yīng)的請(qǐng)求處理器我們假設(shè)是h:

func(c v4echo.Context) error {
	c.Response().Write([]byte("Hello World"))
	return nil
}

那么,執(zhí)行applyMiddleware函數(shù),則結(jié)果執(zhí)行了Recover函數(shù),傳給Recover函數(shù)的next參數(shù)的值是h(即路由注冊(cè)的請(qǐng)求處理器),如下: 那么新的請(qǐng)求處理器就變成了如下:

func(c echo.Context) error {
	if config.Skipper(c) {
		return next(c)
	}

	defer func() {
		// ...省略具體邏輯代碼
	}()
	
    return h(c) // 這里的h就是路由注冊(cè)的請(qǐng)求處理
}

你看,最終還是個(gè)請(qǐng)求處理器的類(lèi)型。這就是echo框架中間件的包裝原理:返回一個(gè)新的請(qǐng)求處理器,該處理器的邏輯是 中間件的邏輯 + 輸入的請(qǐng)求處理的邏輯。其實(shí)這個(gè)也是經(jīng)典的pipeline模式。如下:

四、總結(jié)

本文分析了gin、iris和echo主流框架的中間件的實(shí)現(xiàn)原理。其中g(shù)in和iris是通過(guò)遍歷切片的方式實(shí)現(xiàn)的,結(jié)構(gòu)也比較簡(jiǎn)單。而echo是通過(guò)pipeline模式實(shí)現(xiàn)的。相信通過(guò)本篇文章,你對(duì)中間件的運(yùn)行原理有了更深的理解。

以上就是深入了解Go語(yǔ)言中web框架的中間件運(yùn)行機(jī)制的詳細(xì)內(nèi)容,更多關(guān)于Go語(yǔ)言web框架中間件運(yùn)行機(jī)制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Go 語(yǔ)言中的死鎖問(wèn)題解決

    Go 語(yǔ)言中的死鎖問(wèn)題解決

    本文主要介紹了Go 語(yǔ)言中的死鎖問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-08-08
  • goland中使用leetcode插件實(shí)現(xiàn)

    goland中使用leetcode插件實(shí)現(xiàn)

    本文主要介紹了goland中使用leetcode插件實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • 一文詳解在Go中如何使用Viper來(lái)管理配置

    一文詳解在Go中如何使用Viper來(lái)管理配置

    Viper 是一個(gè)功能齊全的 Go 應(yīng)用程序配置庫(kù),支持很多場(chǎng)景。在本文中,我們將深入探討 Viper 的各種用法和使用場(chǎng)景,以幫助讀者更好地了解和使用 Viper 來(lái)管理應(yīng)用程序配置,感興趣的同學(xué)可以參考閱讀
    2023-05-05
  • golang日志包logger的用法詳解

    golang日志包logger的用法詳解

    這篇文章主要介紹了golang日志包logger的用法詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-05-05
  • 詳解Gotorch多機(jī)定時(shí)任務(wù)管理系統(tǒng)

    詳解Gotorch多機(jī)定時(shí)任務(wù)管理系統(tǒng)

    遵循著“學(xué)一門(mén)語(yǔ)言最好的方式是使用它”的理念,想著用Go來(lái)實(shí)現(xiàn)些什么,剛好有一個(gè)比較讓我煩惱的問(wèn)題,于是用Go解決一下,即使不在生產(chǎn)環(huán)境使用,也可以作為Go語(yǔ)言學(xué)習(xí)的一種方式。
    2021-05-05
  • Golang常量iota的使用實(shí)例

    Golang常量iota的使用實(shí)例

    今天小編就為大家分享一篇關(guān)于Golang常量iota的使用實(shí)例,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-01-01
  • GO語(yǔ)言字符串常用操作小結(jié)

    GO語(yǔ)言字符串常用操作小結(jié)

    本文主要介紹了GO語(yǔ)言字符串常用操作小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • Gin框架限流實(shí)現(xiàn)示例

    Gin框架限流實(shí)現(xiàn)示例

    本文主要介紹了Gin框架限流實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • GoLang中的互斥鎖Mutex和讀寫(xiě)鎖RWMutex使用教程

    GoLang中的互斥鎖Mutex和讀寫(xiě)鎖RWMutex使用教程

    RWMutex是一個(gè)讀/寫(xiě)互斥鎖,在某一時(shí)刻只能由任意數(shù)量的reader持有或者一個(gè)writer持有。也就是說(shuō),要么放行任意數(shù)量的reader,多個(gè)reader可以并行讀;要么放行一個(gè)writer,多個(gè)writer需要串行寫(xiě)
    2023-01-01
  • GO的基礎(chǔ)知識(shí)掃盲注意事項(xiàng)

    GO的基礎(chǔ)知識(shí)掃盲注意事項(xiàng)

    這篇文章主要介紹了GO的基礎(chǔ)知識(shí)注意事項(xiàng),本文是GO語(yǔ)言小白的掃盲文,主要講解了go語(yǔ)言的基本知識(shí),GO程序目錄結(jié)構(gòu),GO程序包的導(dǎo)入與別名運(yùn)用,GO內(nèi)置關(guān)鍵字,GO注釋方法需要的朋友可以參考下
    2022-12-12

最新評(píng)論