用 Go 好久了,也写了好几个小 Web 服务,基本都是在用标准库的 net/http + database/sql,其实开发难度、性能都还不错啦。写的多了还是觉得用标准库有些部分代码重复性还是很高的,慢慢地自己总结出一些通用的“框架”,但不成熟,问题很多。所以开始学一些成熟的框架,之前我开始用 Gorm 简化数据库这方面的流程,这次是考虑学一个 Web 框架啦—— Gin 足够简洁,使用的也比较广泛,所以就它了。
安装
下载安装 Gin 包:
1
$ go get -u github.com/gin-gonic/gin
在代码中导入:
1
import"github.com/gin-gonic/gin"
(可选)如果要使用诸如 http.StatusOK 的常量,还要导入 net/http
1
import"net/http"
起步
首先,创建一个文件 example.go,接下来的代码就写在这个文件里:
1
$ touch example.go
编辑文件,写代码:
1 2 3 4 5 6 7 8 9 10 11 12 13
package main
import"github.com/gin-gonic/gin"
funcmain() { r := gin.Default() // r 是 router 的意思 r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) r.Run() // listen and serve on 0.0.0.0:8080 }
$ curl '0.0.0.0:8080/welcome?firstname=Jane' Hello, Jane $ curl '0.0.0.0:8080/welcome?lastname=Doe' Hello, Guest Doe $ curl '0.0.0.0:8080/welcome?firstname=Jane&lastname=Doe' Hello, Jane Doe
POST Form 参数
对于 POST 等请求方式,我们常用 Multipart/Urlencoded Form 来传递参数信息。
r.GET("/someProtoBuf", func(c *gin.Context) { reps := []int64{int64(1), int64(2)} label := "test" // The specific definition of protobuf is written in the testdata/protoexample file. data := &protoexample.Test{ Label: &label, Reps: reps, } // Note that data becomes binary data in the response // Will output protoexample.Test protobuf serialized data c.ProtoBuf(http.StatusOK, data) })
c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) })
// Example for binding a HTML form: // user=manu&password=123 router.POST("/loginForm", func(c *gin.Context) { var form Login // This will infer what binder to use depending on the content-type header. if err := c.ShouldBind(&form); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return }
var bookableDate validator.Func = func(fl validator.FieldLevel)bool { date, ok := fl.Field().Interface().(time.Time) if ok { today := time.Now() if today.After(date) { returnfalse } } returntrue }
funcmain() { route := gin.Default()
if v, ok := binding.Validator.Engine().(*validator.Validate); ok { v.RegisterValidation("bookabledate", bookableDate) }
funcgetBookable(c *gin.Context) { var b Booking if err := c.ShouldBindWith(&b, binding.Query); err == nil { c.JSON(http.StatusOK, gin.H{"message": "Booking dates are valid!"}) } else { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) } }
1 2 3 4 5 6 7 8
$ curl "localhost:8085/bookable?check_in=2030-04-16&check_out=2030-04-17" {"message":"Booking dates are valid!"}
$ curl "localhost:8085/bookable?check_in=2030-03-10&check_out=2030-03-09" {"error":"Key: 'Booking.CheckOut' Error:Field validation for 'CheckOut' failed on the 'gtfield' tag"}
$ curl "localhost:8085/bookable?check_in=2000-03-09&check_out=2000-03-10" {"error":"Key: 'Booking.CheckIn' Error:Field validation for 'CheckIn' failed on the 'bookabledate' tag"}%
funcmain() { // Creates a router without any middleware by default r := gin.New()
// Global middleware // Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release. // By default gin.DefaultWriter = os.Stdout r.Use(gin.Logger())
// Recovery middleware recovers from any panics and writes a 500 if there was one. r.Use(gin.Recovery())
// Per route middleware, you can add as many as you desire. r.GET("/benchmark", MyBenchLogger(), benchEndpoint)
// Authorization group // authorized := r.Group("/", AuthRequired()) // exactly the same as: authorized := r.Group("/") // per group middleware! in this case we use the custom created // AuthRequired() middleware just in the "authorized" group. authorized.Use(AuthRequired()) { authorized.POST("/login", loginEndpoint) authorized.POST("/submit", submitEndpoint) authorized.POST("/read", readEndpoint)
// nested group testing := authorized.Group("testing") testing.GET("/analytics", analyticsEndpoint) }
// Listen and serve on 0.0.0.0:8080 r.Run(":8080") }