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