Gin 是一个高性能 Go Web 框架。GitHub

安装

go get -u github.com/gin-gonic/gin
import "github.com/gin-gonic/gin"

Hello World

package main

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

func main() {
  r := gin.Default()
  r.GET("/ping", func(c *gin.Context) {
    c.JSON(http.StatusOK, gin.H{"message": "pong"})
  })
  r.Run() // 0.0.0.0:8080
}
go run main.go

路由

HTTP 方法

router.GET("/someGet", getting)
router.POST("/somePost", posting)
router.PUT("/somePut", putting)
router.DELETE("/someDelete", deleting)
router.PATCH("/somePatch", patching)

路径参数

// 匹配 /user/john,不匹配 /user/
router.GET("/user/:name", func(c *gin.Context) {
    name := c.Param("name")
    c.String(http.StatusOK, "Hello %s", name)
})

// 匹配 /user/john/send 等
router.GET("/user/:name/*action", func(c *gin.Context) {
    name := c.Param("name")
    action := c.Param("action")
    c.String(http.StatusOK, name+" is "+action)
})

Query 参数

// /welcome?firstname=Jane&lastname=Doe
router.GET("/welcome", func(c *gin.Context) {
    firstname := c.DefaultQuery("firstname", "Guest")
    lastname := c.Query("lastname")
    c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
})

Post 参数

router.POST("/form_post", func(c *gin.Context) {
    message := c.PostForm("message")
    nick := c.DefaultPostForm("nick", "anonymous")
    c.JSON(200, gin.H{"status": "posted", "message": message, "nick": nick})
})

路由分组

v1 := router.Group("/v1")
{
    v1.POST("/login", loginEndpoint)
    v1.POST("/submit", submitEndpoint)
}

中间件

r := gin.New()
r.Use(gin.Logger())
r.Use(gin.Recovery())

authorized := r.Group("/")
authorized.Use(AuthRequired())
{
    authorized.POST("/login", loginEndpoint)
}

文件上传

// 单文件
router.POST("/upload", func(c *gin.Context) {
    file, _ := c.FormFile("file")
    c.SaveUploadedFile(file, "./"+file.Filename)
    c.String(http.StatusOK, "'%s' uploaded!", file.Filename)
})

// 多文件
router.POST("/upload", func(c *gin.Context) {
    form, _ := c.MultipartForm()
    files := form.File["upload[]"]
    for _, file := range files {
        c.SaveUploadedFile(file, "./"+file.Filename)
    }
    c.String(http.StatusOK, "%d files uploaded!", len(files))
})

模型绑定

type Login struct {
    User     string `form:"user" json:"user" binding:"required"`
    Password string `form:"password" json:"password" binding:"required"`
}

router.POST("/login", func(c *gin.Context) {
    var json Login
    if err := c.ShouldBindJSON(&json); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    c.JSON(http.StatusOK, gin.H{"status": "logged in"})
})
router.GET("/cookie", func(c *gin.Context) {
    cookie, err := c.Cookie("gin_cookie")
    if err != nil {
        c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true)
    }
    _ = cookie
})

优雅重启

srv := &http.Server{Addr: ":8080", Handler: router}
go srv.ListenAndServe()

quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
<-quit

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
srv.Shutdown(ctx)

测试

func TestPingRoute(t *testing.T) {
    router := setupRouter()
    w := httptest.NewRecorder()
    req, _ := http.NewRequest("GET", "/ping", nil)
    router.ServeHTTP(w, req)
    assert.Equal(t, 200, w.Code)
    assert.Equal(t, "pong", w.Body.String())
}

参考