路漫漫其修远兮
吾将上下而求索

go学习:gin框架Next()和Abort()理解

一、 Next() 和 Abort() 的含义

1、Next() 的含义

语法:
func (c *Context) Next()

英文原文
Next should be used only inside middleware. It executes the pending handlers in the chain inside the calling handler. 
大意:Next 应该仅可以在中间件中使用,它在调用的函数中的链中执行挂起的函数。

2、Abort() 的含义

语法:
func (c *Context)Abort()

英文原文
Abort prevents pending handlers from being called. Note that this will not stop the current handler. Let's say you have an authorization middleware 
that validates that the current request is authorized. If the authorization fails (ex: the password does not match), call Abort to ensure the remaining 
handlers for this request are not called.
大意:Abort 在被调用的函数中阻止挂起函数。注意这将不会停止当前的函数。例如,你有一个验证当前的请求是否是认证过的 Authorization 中间件。
如果验证失败(例如,密码不匹配),调用 Abort 以确保这个请求的其他函数不会被调用。

二、示例分析

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"strconv"
	"time"
)

func HelloWordGet(c *gin.Context) {
	fmt.Println("exec hello word get func")
	c.String(http.StatusOK, "hello world get")
}

func main() {
	router := gin.Default()
	router.Use(FirstMiddleware(), SecondMiddleware())

	router.GET("/HelloWordGet", Logger(), HelloWordGet)

	router.Run(":8080")

}

func FirstMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		fmt.Println("first middleware before next()")

		isAbort := c.Query("isAbort")
		bAbort, err := strconv.ParseBool(isAbort)
		if err != nil {
			fmt.Printf("is abort value err, value %s\n", isAbort)
			c.Next() // (2)
		}
		if bAbort {
			fmt.Println("first middleware abort") //(3)
			c.Abort()
			//c.AbortWithStatusJSON(http.StatusOK, "abort is true")
			return
		} else {
			fmt.Println("first middleware doesnot abort") //(4)
			return
		}

		fmt.Println("first middleware after next()")
	}
}

func SecondMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		fmt.Println("current inside of second middleware")
	}
}

func Logger() gin.HandlerFunc {
	return func(context *gin.Context) {
		host := context.Request.Host
		url := context.Request.URL
		method := context.Request.Method
		fmt.Printf("%s::%s \t %s \t %s ", time.Now().Format("2006-01-02 15:04:05"), host, url, method)
		//context.Next()
		fmt.Println(context.Writer.Status())
	}

}

1、状态1

调用:http://localhost:8080/HelloWordGet?isAbort=tru

在调用时传递的 isAbort() 的值不能解析为布尔类型的值时

会执行到 (2) 处。此时调用的是 Next() 方法。由第一部分的内容可知。它执行后面的中间件以及业务处理函数,

程序在执行完业务处理后仍然会回到当前的函数。然后打印其他的内容。在使用的过程中,可以根据其余部分是否继续执行而在 c.Next() 后添加 return 语句。

程序打印:

first middleware before next()
is abort value err, value 
current inside of second middleware
2021-04-25 10:56:27::localhost:8080 	 /HelloWordGet?isAbor=tru 	 GET 200
exec hello word get func
first middleware doesnot abort
[GIN] 2021/04/25 - 10:56:27 | 200 |      22.969ms |             ::1 | GET      "/HelloWordGet?isAbor=tru"

2、状态2

调用:http://localhost:8080/HelloWordGet?isAbort=true

在调用时传递的 isAbort() 的值如果是可以解析为布尔类型的值时,如果值为真,则执行到 (3) 处。由于此处调用了 Abort() ,根据第一部分的内容可知,它阻止了链中后续的函数的调用。所以 SecondMiddleware 函数和业务函数 Hello() 函数不会被调用。但它不能停止当前的函数

first middleware before next()
first middleware abort
[GIN] 2021/04/25 - 11:19:45 | 200 |            0s |             ::1 | GET      "/HelloWordGet?isAbort=true"

注意: Abort()和AbortWithStatusJSON` 一直都是中止链的调用,不同之处在于,前者不会返回给前端任何内容,而后者则可以返回给前端一个JSON串。

3、状态3

http://localhost:8080/HelloWordGet?isAbort=false

在调用时传递的 isAbort() 的值可以解析为布尔类型,如果值为假,则流程会进入到 (4)处。它只是在当前的函数调用中返回,不会对整个链造成影响。

first middleware before next()
first middleware doesnot abort
current inside of second middleware
2021-04-25 11:24:04::localhost:8080 	 /HelloWordGet?isAbort=false 	 GET 200
exec hello word get func
[GIN] 2021/04/25 - 11:24:04 | 200 |            0s |             ::1 | GET      "/HelloWordGet?isAbort=false"

三、说明

之所以即使不使用c.next()也会依次执行下面的程序,原因是最开始从树节点拿到方法对应的切片时,就执行了c.next,方法里面会循环执行后面的方法

// gin.go
func (engine *Engine) handleHTTPRequest(c *Context) {
	// ...

	// 根据请求方法找到对应的路由树
	t := engine.trees
	for i, tl := 0, len(t); i < tl; i++ {
		if t[i].method != httpMethod {
			continue
		}
		root := t[i].root
		// 在路由树中根据path查找
		value := root.getValue(rPath, c.Params, unescape)
		if value.handlers != nil {
			c.handlers = value.handlers
			c.Params = value.params
			c.fullPath = value.fullPath
			c.Next()  // 执行函数链条
			c.writermem.WriteHeaderNow()
			return

之所以中间件里面某个方法写了c.next,执行完后,handleHTTPRequest里面的c.next不会重复执行,原因是中间件里面执行c.next,会将此结构体中index变量加到最大,导致最外面的直接退出了

未经允许不得转载:江哥架构师笔记 » go学习:gin框架Next()和Abort()理解

分享到:更多 ()

评论 抢沙发

评论前必须登录!