Gin Gin is a HTTP web framework written in Go
Installation use command to install Gin
1 $ go get -u github.com/gin-gonic/gin
QuickStart 1 2 3 4 5 6 7 8 9 10 11 12 13 func main () { r := gin.Default() r.GET("/" , func (context *gin.Context) { context.String(http.StatusOK, "hello world" ) context.JSON(http.StatusOK, gin.H{ "message" : "helloWorld" , }) }) r.Run() }
the difference of new() and default() :
Default() Creates an Engine instance with the Logger and Recovery middleware already attached.
1 2 r := gin.Default() r := gin.New()
Gin group 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package mainimport ( "github.com/gin-gonic/gin" "net/http" )func main () { router := gin.Default() goodsGroup := router.Group("/goods" ) { goodsGroup.GET("/list" , goodsList) goodsGroup.GET("/add" , goodsAdd) } router.Run() }func goodsAdd (context *gin.Context) { context.String(http.StatusOK, "added a goods" ) }func goodsList (context *gin.Context) { context.String(http.StatusOK, "checked the goods list" ) }
Url Params get parameters from url
use :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package mainimport ( "github.com/gin-gonic/gin" "net/http" )func main () { router := gin.Default() goodsGroup := router.Group("/goods" ) { goodsGroup.GET("/:id/:action" , goodsDetail) } router.Run() }func goodsDetail (context *gin.Context) { id := context.Param("id" ) action := context.Param("action" ) context.JSON(http.StatusOK, gin.H{ "id" : id, "action" : action, }) }
when we need the path in url
use *
1 goodsGroup.GET("/:id/*action" , goodsDetail)
1 2 3 http: {"action" :"/delete/ok/o" ,"id" :"1" }
bind uri with struct
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package mainimport ( "github.com/gin-gonic/gin" "net/http" )type Person struct { Id int `uri:"id" binding:"required"` Name string `uri:"name" binding:"required"` }func main () { router := gin.Default() router.GET("/:id/:name" , func (context *gin.Context) { var person Person if err := context.ShouldBindUri(&person); err != nil { context.Status(404 ) return } context.JSON(http.StatusOK, gin.H{ "name" : person.Name, "id" : person.Id, }) }) router.Run() }
Get/Post Parms get 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package mainimport ( "github.com/gin-gonic/gin" "net/http" )func main () { r := gin.Default() r.GET("/welcome" , welcome) r.Run() }func welcome (context *gin.Context) { firstName := context.DefaultQuery("firstname" , "issak" ) lastName := context.DefaultQuery("lastname" , "kenny" ) context.JSON(http.StatusOK, gin.H{ "first_name" : firstName, "last_name" : lastName, }) }
url:
1 http://localhost:8080/welcome?firstname=a&lastname=b
post 1 2 3 4 5 6 7 8 9 10 11 r.POST("/form_post" , formPost)func formPost (context *gin.Context) { message := context.PostForm("message" ) nick := context.DefaultPostForm("nick" , "anonymous" ) context.JSON(http.StatusOK, gin.H{ "first_name" : message, "last_name" : nick, }) }
send post request
get and post 1 2 3 4 5 6 7 8 9 10 11 12 13 14 r.POST("/post" , getPost)func getPost (context *gin.Context) { id := context.Query("id" ) page := context.DefaultQuery("page" , "0" ) nick := context.PostForm("nick" ) message := context.DefaultPostForm("message" , "m" ) context.JSON(http.StatusOK, gin.H{ "id" : id, "page" : page, "nick" : nick, "message" : message, }) }
JSON、Protobuf json 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package mainimport ( "github.com/gin-gonic/gin" "net/http" )func main () { r := gin.Default() r.GET("/moreJSON" , moreJSON) r.Run() }func moreJSON (context *gin.Context) { var msg struct { Name string `json:"user"` Message string Number int } msg.Name = "xxx" msg.Message = "this is a test json" msg.Number = 20 context.JSON(http.StatusOK, msg) }
protobuf 1 2 3 4 5 6 7 8 9 syntax = "proto3" ;option go_package = "example.com/gin;proto" ;message Teacher { string name = 1 ; repeated string course = 2 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package mainimport ( "example.com/gin/05/proto" "github.com/gin-gonic/gin" "net/http" )func main () { r := gin.Default() r.GET("/someProtobuf" , returnProto) r.Run() }func returnProto (context *gin.Context) { course := []string {"python" , "blockchain" , "golang" } user := &proto.Teacher{ Name: "kennyS" , Course: course, } context.ProtoBuf(http.StatusOK, user) }
1 2 E:\Project\Go\src\gin\05\proto>protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative u ser.proto
pureJSON generally, json transfers html character into corresponding unicode character
such as <
to /u003c
. pureJSON helps us keep the same html
1 r.GET("/pureJSON" , pureJSON)
1 2 3 4 5 func pureJSON (context *gin.Context) { context.PureJSON(http.StatusOK, gin.H{ "html" : "<b>Hello, world</b>" , }) }
1 {"html" :"<b>Hello, world</b>" }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package mainimport ( "github.com/gin-gonic/gin" "net/http" )type LoginForm struct { User string `json:"user" binding:"required,min=3,max=10"` Password string `json:"password" binding:"required"` }type SignUpForm struct { Age uint8 `json:"age" binding:"gte=1,lte=130"` Name string `json:"name" binding:"required,min=3"` Email string `json:"email" binding:"required,email"` Password string `json:"password" binding:"required"` RePassword string `json:"rePassword" binding:"required,eqfield=Password"` }func main () { r := gin.Default() r.POST("/loginJSON" , func (context *gin.Context) { var loginForm LoginForm if err := context.ShouldBind(&loginForm); err != nil { context.JSON(http.StatusBadRequest, gin.H{ "error" : err.Error(), }) return } context.JSON(http.StatusOK, gin.H{ "message" : "logged in successfully" , }) }) r.POST("/signUp" , func (context *gin.Context) { var signUpForm SignUpForm if err := context.ShouldBind(&signUpForm); err != nil { context.JSON(http.StatusBadRequest, gin.H{ "error" : err.Error(), }) return } context.JSON(http.StatusOK, gin.H{ "message" : "signed up successfully" , }) }) r.Run() }
validator err translate 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 package mainimport ( "fmt" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" "github.com/go-playground/locales/en" "github.com/go-playground/locales/zh" ut "github.com/go-playground/universal-translator" "github.com/go-playground/validator/v10" en2 "github.com/go-playground/validator/v10/translations/en" zh2 "github.com/go-playground/validator/v10/translations/zh" "net/http" )var trans ut.Translatortype LoginForm struct { User string `json:"user" binding:"required,min=3,max=10"` Password string `json:"password" binding:"required"` }func InitTrans (locale string ) (err error) { if v, ok := binding.Validator.Engine().(*validator.Validate); ok { zhT := zh.New() enT := en.New() uni := ut.New(enT, zhT, enT) trans, ok = uni.GetTranslator(locale) if !ok { return fmt.Errorf("uni.GetTranslator(%s)" , locale) } switch locale { case "en" : en2.RegisterDefaultTranslations(v, trans) case "zh" : zh2.RegisterDefaultTranslations(v, trans) default : en2.RegisterDefaultTranslations(v, trans) } return } return }func main () { if err := InitTrans("zh" ); err != nil { fmt.Println("init failed!" ) return } r := gin.Default() r.POST("/loginJSON" , func (context *gin.Context) { var loginForm LoginForm if err := context.ShouldBind(&loginForm); err != nil { errs, ok := err.(validator.ValidationErrors) if !ok{ context.JSON(http.StatusOK, gin.H{ "msg" : err.Error(), }) } context.JSON(http.StatusBadRequest, gin.H{ "error" : errs.Translate(trans), }) return } context.JSON(http.StatusOK, gin.H{ "message" : "logged in successfully" , }) }) r.Run() }
if we don’t want the User
field to appear but make it to ‘json tag’ we define in the struct
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 func InitTrans (locale string ) (err error) { if v, ok := binding.Validator.Engine().(*validator.Validate); ok { v.RegisterTagNameFunc(func (fld reflect.StructField) string { name := strings.SplitN(fld.Tag.Get("json" ), "," , 2 )[0 ] if name == "-" { return "" } return name }) zhT := zh.New() enT := en.New() uni := ut.New(enT, zhT, enT) trans, ok = uni.GetTranslator(locale) if !ok { return fmt.Errorf("uni.GetTranslator(%s)" , locale) } switch locale { case "en" : en2.RegisterDefaultTranslations(v, trans) case "zh" : zh2.RegisterDefaultTranslations(v, trans) default : en2.RegisterDefaultTranslations(v, trans) } return } return }
now:
also we don’t want LoginForm
:
1 2 3 4 5 6 7 8 9 10 11 func removeTopStruct (fields map [string ]string ) map [string ]string { rsp := map [string ]string {} for field, err := range fields{ rsp[field[strings.Index(field, "." )+1 :]] = err } return rsp } context.JSON(http.StatusBadRequest, gin.H{ "error" : removeTopStruct(errs.Translate(trans)), })
customized middleware 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package mainimport ( "fmt" "github.com/gin-gonic/gin" "net/http" "time" )func MyLogger () gin .HandlerFunc { return func (context *gin.Context) { t := time.Now() context.Set("example" , "123" ) context.Next() end := time.Since(t) fmt.Printf("wasted time:%v" , end) fmt.Printf("status: %v" , context.Writer.Status()) } }func main () { r := gin.New() r.Use(gin.Logger(), gin.Recovery()) r.Use(MyLogger()) r.GET("/test" , func (context *gin.Context) { context.JSON(http.StatusOK, gin.H{ "message" :"666" , }) }) r.Run() }
set static path and html 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package mainimport ( "fmt" "github.com/gin-gonic/gin" "net/http" "os" "path/filepath" )func main () { r := gin.Default() dir, _ := filepath.Abs(filepath.Dir(os.Args[0 ])) fmt.Println(dir) r.LoadHTMLFiles("templates/index.tmpl" ) r.GET("/index" , func (context *gin.Context) { context.HTML(http.StatusOK, "index.tmpl" , gin.H{ "title" :"Issak" , }) }) r.Run() }
1 2 3 4 5 6 7 8 9 10 11 12 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > title</title > </head > <body > <h1 > {{ .title }} </h1 > </body > </html >
goland builds go files in C:\Users\Arison\AppData\Local\Temp, it’s a temp path, so the static html file can’t be found. We should use teminal to build an exe file and run it. Or use absolute path but not recommended
to load all files in a path
1 r.LoadHTMLGlob("template/*" )
same name in different path
main.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package mainimport ( "github.com/gin-gonic/gin" "net/http" )func main () { r := gin.Default() r.LoadHTMLGlob("templates/**/*" ) r.GET("/index" , func (context *gin.Context) { context.HTML(http.StatusOK, "index.tmpl" , gin.H{ "title" :"Issak" , }) }) r.GET("/goods/list" , func (context *gin.Context) { context.HTML(http.StatusOK, "goods/list.html" , gin.H{ "title" :"Issak" , }) }) r.GET("/users/list" , func (context *gin.Context) { context.HTML(http.StatusOK, "users/list.html" , gin.H{ "title" :"Issak" , }) }) r.Run() }
goods/list
1 2 3 4 5 6 7 8 9 10 11 12 {{define "goods/list.html"}}<!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <h1 > goods list</h1 > </body > </html > {{end}}
users/list
1 2 3 4 5 6 7 8 9 10 11 12 {{define "users/list.html"}}<!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <h1 > users list</h1 > </body > </html > {{end}}
load static files main.go
prefix with /static
would be replace with ./static
to find the local static files
1 r.Static("/static" , "./static" )
users/list.html
1 2 3 4 5 6 7 8 9 10 11 12 13 {{define "users/list.html"}}<!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" > <title > Title</title > <link rel ="stylesheet" href ="/static/css/style.css" > </head > <body > <h1 > users list</h1 > </body > </html > {{end}}
graceful restart or stop 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package mainimport ( "fmt" "github.com/gin-gonic/gin" "net/http" "os" "os/signal" "syscall" )func main () { r := gin.Default() r.GET("/" , func (context *gin.Context) { context.JSON(http.StatusOK, gin.H{ "msg" :"66" , }) }) go func () { r.Run() }() quit := make (chan os.Signal) signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) <-quit fmt.Println("shutdown server" ) fmt.Println("logout service" ) }