Go Gin demo

项目目录

demo ─ router ─ router.go
     ├ handlers ─ hello.go
     └ main.go

hello.go

package handlers

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

func HelloPage(context *gin.Context) {
    context.JSON(http.StatusOK, gin.H{
        "message": "welcome",
    })
}

router.go

package router

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

func Init() {
    //创建一个默认 gin 路由器
    r := gin.Default()//分组路由
    //组:v1
    v1 := r.Group("/v1")
    {
        v1.GET("/hello", handlers.HelloPage)
    }
    r.Run(":8000")// 监听和服务在 0.0.0.0:8000
}

main.go

package main

import "./router"

func main() {
    router.Init()
}

测试

执行 main.go 文件后,在浏览器上面输入地址:

http://127.0.0.1:8000/v1/hello

即可以看到浏览器显示:

{"message":"welcome"}

Gin 参数使用

接收url参数

在之前的组v1路由下新定义一个路由:

v1 := r.Group("/v1")
{
    v1.GET("/hello", handlers.HelloPage)

    v1.GET("/hello/:name", func(context *gin.Context) {
        name := context.Param("name")
        context.String(http.StatusOK, "Hello %s", name)
    })
}

重新运行后,在浏览器输入:

http://127.0.0.1:8000/v1/hello/muzico

即可以看到浏览器显示:

Hello muzico

接收url参数,多个参数

v1.GET("/value/:a/:b", func(context *gin.Context) {
    value_a := context.Param("a")
    value_b := context.Param("b")
    context.String(http.StatusOK, "value a:%s b:%s", value_a, value_b)
})

重新运行后,在浏览器输入:

http://127.0.0.1:8000/v1/value/1/2

即可以看到浏览器显示:

value a:1 b:2

但如果地址少了一个参数就会处理失败,返回 "404 page not found" 页面。

除非另外追加相对应的路由进行处理:

v1.GET("/value/:a", func(context *gin.Context) {
    value_a := context.Param("a")
    context.String(http.StatusOK, "value a:%s", value_a)
})
v1.GET("/value/", func(context *gin.Context) {
    context.String(http.StatusOK, "value 没有参数")
})

通过c.Param("key")方法,Gin成功捕获了url请求路径中的参数

这种方式可以获取不定数据的参数。

v1.GET("/welcome", func(context *gin.Context) {
    firstname := context.DefaultQuery("firstname", "Guest")
    lastname := context.Query("lastname")
    context.String(http.StatusOK, "Hello %s %s", firstname, lastname)
})

重新运行后,在浏览器输入:

http://127.0.0.1:8000/v1/welcome

即可以看到浏览器显示:

Hello Guest 

该路由的 firstname 值默认为 "Guest"

在浏览器输入:

http://127.0.0.1:8000/v1/welcome?lastname=muzico&firstname=yococo

即可以看到浏览器显示:

Hello yococo muzico

Gin返回静态页面

创建目录文件

demo ─ templates ─ index.html
index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
    <h1>{{ .title }}</h1>
</body>
</html>

创建group v2,并创建 /index 路由,返回精通 html 页面:

r.LoadHTMLGlob(getCurrentPath() + "/demo/templates/*")
v2 := r.Group("/v2")
{
    v2.GET("/index", func(context *gin.Context) {
        context.HTML(http.StatusOK, "index.html", gin.H{
            "title": "hello world!",
        })
    })
}

获取项目路径

func getCurrentPath() string {
    str, _ := os.Getwd()
    return str
}

在浏览器输入:

http://127.0.0.1:8000/v2/index

即可以看到,Gin返回了静态文件index.html,并把title数据填充到了模板 {{ .title }}

Gin默认路由

//404 NotFound

r.NoRoute(func(context *gin.Context) {
    context.JSON(http.StatusNotFound, gin.H{
        "status" : 404,
        "error": "404, page not exists!",
    })
})

在浏览器输入不存在,或不正确的地址。

即可以看到浏览器显示:

{"error":"404, page not exists!","status":404}

Gin 中间件

在go的net/http中我们可以很方便的设计中间件,同样Gin也为我们提供了很方便的中间件使用。 我们可以定义全局中间件,群组中间件和单个路由的中间件,可以限定中间件的作用范围。

先定义一个简单的中间件,并将其设为全局中间件:

func PrintMiddleware(context *gin.Context) {
    fmt.Print("before request")
    context.Next()
}

接下来注册为全局中间件:

r := gin.Default()//分组路由
r.Use(PrintMiddleware)

在浏览器输入:

http://127.0.0.1:8000/v2/index

可以看到控制器输出的信息:

[GIN-debug] GET    /v2/index                 --> _/Users/muzico/Documents/Project/go/test03/demo/router.Init.func6 (4 handlers)
[GIN-debug] Listening and serving HTTP on :8000
before request[GIN] 2019/04/12 - 14:38:43 | 200 |     585.483µs |       127.0.0.1 | GET      /v2/index

可以看到Gin在执行请求前,成功执行了自定义的中间件函数,c.Next()表示当中间件执行完成之后,将请求传递给下一个函数处理。

现在我们想对v2组的请求进行一次验证(模拟登录),假设请求中包含一个token参数,存储认证信息,我们来实现这个中间件函数:

func ValidateToken() gin.HandlerFunc {
    return func(context *gin.Context) {
        token := context.Request.FormValue("token")
        if token == "" {
            context.JSON(401, gin.H{
                "message": "Token required",
            })
            context.Abort()
            return
        }
        if token != "accesstoken" {
            context.JSON(http.StatusOK, gin.H{
                "message": "Invalid Token",
            })
            context.Abort()
            return
        }
        context.Next()
    }
}

然后我们在group v2组注册这个中间件:

r.Use(ValidateToken())

在浏览器输入:

http://127.0.0.1:8000/v2/index

即可以看到浏览器显示:

{"message":"Token required"}

而在浏览器输入:

http://127.0.0.1:8000/v2/index?token=accesstoken

则可以看到:


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
</head>
<body>
    <h1>hello world!</h1>
</body>
</html>

可以看到已经通过验证,Gin正确响应了请求。c.Abort()表示请求被终止。

完整的 router.go 代码:

package router

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "../handlers"
    "net/http"
    "os"
)

func Init() {
    //创建一个默认 gin 路由器
    r := gin.Default()//分组路由
    r.Use(PrintMiddleware)
    r.Use(ValidateToken())

    //组:v1
    v1 := r.Group("/v1")
    {
        v1.GET("/hello", handlers.HelloPage)

        v1.GET("/hello/:name", func(context *gin.Context) {
            name := context.Param("name")
            context.String(http.StatusOK, "Hello %s", name)
        })

        v1.GET("/value/:a/:b", func(context *gin.Context) {
            value_a := context.Param("a")
            value_b := context.Param("b")
            context.String(http.StatusOK, "value a:%s b:%s", value_a, value_b)
        })
        v1.GET("/value/:a", func(context *gin.Context) {
            value_a := context.Param("a")
            context.String(http.StatusOK, "value a:%s", value_a)
        })
        v1.GET("/value/", func(context *gin.Context) {
            context.String(http.StatusOK, "value 没有参数")
        })


        v1.GET("/welcome", func(context *gin.Context) {
            firstname := context.DefaultQuery("firstname", "Guest")
            lastname := context.Query("lastname")
            context.String(http.StatusOK, "Hello %s %s", firstname, lastname)
        })
    }



    r.LoadHTMLGlob(getCurrentPath() + "/demo/templates/*")
    v2 := r.Group("/v2")
    {
        v2.GET("/index", func(context *gin.Context) {
            context.HTML(http.StatusOK, "index.html", gin.H{
                "title": "hello world!",
            })
        })
    }

    r.NoRoute(func(context *gin.Context) {
        context.JSON(http.StatusNotFound, gin.H{
            "status" : 404,
            "error": "404, page not exists!",
        })
    })


    r.Run(":8000")// 监听和服务在 0.0.0.0:8000
}

func getCurrentPath() string {
    str, _ := os.Getwd()
    return str
}

func PrintMiddleware(context *gin.Context) {
    fmt.Print("before request")
    context.Next()
}

func ValidateToken() gin.HandlerFunc {
    return func(context *gin.Context) {
        token := context.Request.FormValue("token")
        if token == "" {
            context.JSON(401, gin.H{
                "message": "Token required",
            })
            context.Abort()
            return
        }
        if token != "accesstoken" {
            context.JSON(http.StatusOK, gin.H{
                "message": "Invalid Token",
            })
            context.Abort()
            return
        }
        context.Next()
    }
}