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"})
})
Cookie
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())
}