golang web框架——gin使用教程(二)

gin使用教程(一)
gin使用教程(二)shell

上篇教程主要讲了gin的路由以及参数获取,这篇主要讲解gin的中间件。
中间件能够在咱们接受到一个http请求时,在handle以前或者handle以后作一些处理。一般,在handle以前,咱们能够经过中间件很方便地进行校验,若是再handle以后,咱们能够对response进行一些调整。bash

基本用法

使用服务器

// 建立一个不包含中间件的路由器
gin.New()
// 使用自定义中间件或者gin提供的中间件
gin.use(gin.Logger())
复制代码

代替curl

gin.Default()
复制代码

其实gin默认使用了Logger和Recovery两个中间件,而后也是在内部调用了New:async

// gin.go

func Default() *Engine {
	debugPrintWARNINGDefault()
	engine := New()
	engine.Use(Logger(), Recovery()) // 使用了Logger和Recovery两个中间件
	return engine
}
复制代码

咱们对这两个中间件作一个简单的了解:函数

  • Logger中间件可让咱们作打印的一些自定义配置
  • Recovery中间件可让咱们从崩溃中恢复
func main() {

	logfile, _ := os.Create("./logs/gin.log")
    
    // 这里将log输出到指定文件
    // 注意这个配置必定要在gin.Default()以前
	gin.DefaultWriter = io.MultiWriter(logfile, os.Stdout)

	router := gin.Default()

    // 这里分别使用两个中间件
    router.Use(gin.Logger())
	router.Use(gin.Recovery())

	router.POST("/test", func(context *gin.Context) {
		var person Person
		if err := context.ShouldBind(&person); err != nil {
			context.JSON(http.StatusBadRequest, gin.H{
				"error": err.Error(),
			})
			return
		}
		context.JSON(http.StatusOK, gin.H{
			"success": true,
		})
	})

	router.Run(":3000")
}
复制代码

自定义中间件

要本身实现中间件,不妨先看一下官方定义的Recovery中间件是如何实现的便可。post

// recovery.go

这里只要返回一个HandlerFunc类型便可
func Recovery() HandlerFunc {
	return RecoveryWithWriter(DefaultErrorWriter)
}


// gin.go

HandlerFunc就是一个参数为*context的函数
type HandlerFunc func(*Context) 复制代码

看懂了中间件的大概思路,那么咱们本身来手动实现一个。
咱们来写一个IP鉴权的中间件,假设咱们的需求是只有白名单中的ip才能够访问服务器,那么咱们能够这么实现:测试

// ipauth.go

func Auth() gin.HandlerFunc {
	return func(context *gin.Context) {
        // 定义ip白名单
		whiteList := []string{
			"127.0.0.1",
		}

		ip := context.ClientIP()

		flag := false

		for _, host := range whiteList {
			if ip == host {
				flag = true
				break
			}
		}

		if !flag {
			context.String(http.StatusNetworkAuthenticationRequired, "your ip is not trusted: %s", ip)
			context.Abort()
		}

	}
}
复制代码
// main.go

func main() {
    
	router := gin.New()

	router.Use(ipauth.Auth())

	router.GET("/test", func(context *gin.Context) {
		context.JSON(http.StatusOK, gin.H{
			"success": true,
		})
	})

	router.Run(":3000")
}
复制代码

测试实例:ui

// 若是你用localhost访问ip会显示为::1。
// 致使your ip is not trusted。这是由于你的电脑开启了ipv6支持,这是ipv6下的本地回环地址的表示。
$ curl http://127.0.0.1:3000/test
{"success":true}
复制代码
// 把whiteList中的127.0.0.1改为127.0.0.2以后,咱们再试一下
$ curl http://127.0.0.1:3000/test
your ip is not trusted: 127.0.0.1
复制代码

Group中使用中间件

此外,咱们的中间件能够不全局使用,而只针对部分的group:url

func main() {

	router := gin.Default()

  // 定义了group
	authorized := router.Group("/auth", ipauth.Auth())

  // 对上面这个group进行路由绑定
	authorized.GET("/write", handle)

	router.GET("/read", handle)

	router.Run(":3000")
}

func handle(context *gin.Context) {
	context.JSON(http.StatusOK, gin.H{
		"success": true,
	})
}
复制代码

测试实例

$ curl http://127.0.0.1:3000/auth/write
your ip is not trusted: 127.0.0.1
$ curl http://127.0.0.1:3000/read
{"success":true}
复制代码

单个路由使用中间件

也能够只针对单个路由:

func main() {
    router := gin.Default()

    // 注册一个路由,使用了 middleware1,middleware2 两个中间件
    router.GET("/someGet", middleware1, middleware2, handler)
  
    // 默认绑定 :8080
    router.Run()
}

func handler(c *gin.Context) {
    log.Println("exec handler")
}

func middleware1(c *gin.Context) {
    log.Println("exec middleware1")
  
    //你能够写一些逻辑代码
  
    // 执行该中间件以后的逻辑
    c.Next()
}

func middleware2(c *gin.Context) {
    log.Println("arrive at middleware2")
    // 执行该中间件以前,先跳到流程的下一个方法
    c.Next()
    // 流程中的其余逻辑已经执行完了
    log.Println("exec middleware2")
  
    //你能够写一些逻辑代码
}
复制代码

能够看出,中间件的写法和路由的 Handler 几乎是同样的,只是多调用c.Next()。正是有个c.Next(),咱们能够在中间件中控制调用逻辑的变化,看下面的 middleware2 代码。在 middleware2中,执行到 c.Next()时,Gin 会直接跳到流程的下一个方法中,等到这个方法执行完后,才会回来接着执行 middleware2 剩下的代码。

因此请求上面注册的路由 url /someGet ,请求先到达middleware1,而后到达 middleware2,但此时 middleware2调用了 c.Next(),因此 middleware2的代码并无执行,而是跳到了 handler ,等 handler执行完成后,跳回到 middleware2,执行 middleware2剩下的代码。

因此咱们能够在控制台上看到如下日志输出:

exec middleware1
arrive at middleware2
exec handler
exec middleware2
复制代码

在中间件中使用goroutines

在中间件或处理程序中启动新的goroutine时,你不该该使用其中的原始上下文,你必须使用只读副本(c.Copy()

func main() {
	r := gin.Default()

	r.GET("/long_async", func(c *gin.Context) {
		// 建立要在goroutine中使用的副本
		cCp := c.Copy()
		go func() {
			// simulate a long task with time.Sleep(). 5 seconds
			time.Sleep(5 * time.Second)

			// 这里使用你建立的副本
			log.Println("Done! in path " + cCp.Request.URL.Path)
		}()
	})

	r.Run(":3000")
}
复制代码
相关文章
相关标签/搜索