Gin Usage Tutorial (1) GIN Usage Tutorial (2)

The previous tutorial focused on gin routing and parameter fetching. This tutorial focuses on GIN middleware. Middleware can do some processing before or after Handle when we receive an HTTP request. Usually, before handle, we can easily check through the middleware, and after handle, we can make some adjustments to response.

Basic usage

use

// Create a router without middleware
gin.New()
// Use custom middleware or middleware provided by GIN
gin.use(gin.Logger())
Copy the code

Instead of

gin.Default()
Copy the code

Gin uses Logger and Recovery middleware by default, and then calls New internally as well:

// gin.go

func Default(a) *Engine {
	debugPrintWARNINGDefault()
	engine := New()
	engine.Use(Logger(), Recovery()) // Use Logger and Recovery middleware
	return engine
}
Copy the code

Let’s do a simple understanding of these two middleware:

  • Logger middleware lets you do some custom configuration for printing
  • Recovery middleware allows us to recover from crashes
func main(a) {

	logfile, _ := os.Create("./logs/gin.log")
    
    // Here log is output to the specified file
    // Note that this configuration must precede gin.Default()
	gin.DefaultWriter = io.MultiWriter(logfile, os.Stdout)

	router := gin.Default()

    // Two middleware are used here
    router.Use(gin.Logger())
	router.Use(gin.Recovery())

	router.POST("/test".func(context *gin.Context) {
		var person Person
		iferr := 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")}Copy the code

Custom middleware

To implement middleware yourself, take a look at how officially defined Recovery middleware is implemented.

// recovery.goWe just need to return a HandlerFunc typefunc Recovery(a) HandlerFunc {
	return RecoveryWithWriter(DefaultErrorWriter)
}


// gin.goHandlerFunc is a function that takes *contexttype HandlerFunc func(*Context)
Copy the code

Understand the general idea of middleware, so we come to manually implement a. Let’s write an IP authentication middleware, assuming that we need only the IP in the whitelist to access the server, then we can implement this:

// ipauth.go

func Auth(a) gin.HandlerFunc {
	return func(context *gin.Context) {
        // Define an IP whitelist
		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()
		}

	}
}
Copy the code
// main.go

func main(a) {
    
	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")}Copy the code

Test example:

If you use localhost to access the IP address, it will display ::1. // Your IP is not trusted. This is because your computer has ipv6 enabled, which is a representation of the local loopback address under ipv6.$The curl http://127.0.0.1:3000/test
{"success":true}
Copy the code
// After whiteList 127.0.0.1 is changed to 127.0.0.2, let's try again$The curl http://127.0.0.1:3000/testYour IP is not trusted: 127.0.0.1Copy the code

Middleware is used in groups

In addition, our middleware can be used not globally, but only for partial groups:

func main(a) {

	router := gin.Default()

  // group is defined
	authorized := router.Group("/auth", ipauth.Auth())

  // Bind the route to the group above
	authorized.GET("/write", handle)

	router.GET("/read", handle)

	router.Run(": 3000")}func handle(context *gin.Context) {
	context.JSON(http.StatusOK, gin.H{
		"success": true})},Copy the code

Test case

$The curl http://127.0.0.1:3000/auth/writeYour IP is not trusted: 127.0.0.1$The curl http://127.0.0.1:3000/read
{"success":true}
Copy the code

A single route uses middleware

Or for a single route:

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

    // Register a route using middleware1 and middleware2 middleware
    router.GET("/someGet", middleware1, middleware2, handler)
  
    // Default binding :8080
    router.Run()
}

func handler(c *gin.Context) {
    log.Println("exec handler")}func middleware1(c *gin.Context) {
    log.Println("exec middleware1")
  
    // You can write some logical code
  
    // Execute the logic behind the middleware
    c.Next()
}

func middleware2(c *gin.Context) {
    log.Println("arrive at middleware2")
    // Before executing the middleware, skip to the next method of the process
    c.Next()
    // The rest of the logic in the process has been executed
    log.Println("exec middleware2")
  
    // You can write some logical code
}
Copy the code

As you can see, the middleware is written almost the same as the Handler of the route, except that c.ext () is called more often. With c.ext (), we can control changes in the call logic in the middleware, as shown in the middleware2 code below. In Middleware2, when c.ext () is executed, Gin jumps directly to the next method in the process, waits until the method is finished, and then comes back to execute the rest of middleware2 code.

Middleware2 calls c.next (), so middleware2’s code is not executed. Instead, we jump to handler, and when handler completes, we jump back to Middleware2 and execute the rest of middleware2 code.

So we can see the following log output on the console:

exec middleware1
arrive at middleware2
exec handler
exec middleware2
Copy the code

Use Goroutines in middleware

When starting a new Goroutine in middleware or handler, you should not use the original context in it, you must use a read-only copy (c.copy ()).

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

	r.GET("/long_async".func(c *gin.Context) {
		// Create a copy to use in goroutine
		cCp := c.Copy()
		go func(a) {
			// simulate a long task with time.Sleep(). 5 seconds
			time.Sleep(5 * time.Second)

			// Use the copy you created here
			log.Println("Done! in path " + cCp.Request.URL.Path)
		}()
	})

	r.Run(": 3000")}Copy the code