Hero Image
Go 文章

學會 gin 參數校驗之 validator 函式庫,看這一篇就夠了 字串約束 excludesall:不包含參數中任意的 UNICODE 字元,例如 excludesall=ab excludesrune:不包含參數表示的 rune 字元,excludesrune=asong startswith:以參數子字串為前綴,例如 startswith=hi endswith:以參數子字串為後綴,例如 endswith=bye。 contains=:包含參數子字串,例如 contains=email containsany:包含參數中任意的 UNICODE 字元,例如 containsany=ab containsrune:包含參數表示的 rune 字元,例如 containsrune=asong excludes:不包含參數子字串,例如 excludes=email 範圍約束 範圍約束的欄位型別分為三種: 對於數值,我們可以約束其值 對於切片、陣列和 map,我們可以約束其長度 對於字串,我們可以約束其長度 常用 tag 介紹: ne:不等於參數值,例如 ne=5 gt:大於參數值,例如 gt=5 gte:大於等於參數值,例如 gte=50 lt:小於參數值,例如 lt=50 lte:小於等於參數值,例如 lte=50 oneof:只能是列舉出的值其中之一,這些值必須是數值或字串,以空格分隔;如果字串中有空格,請用單引號包起來,例如 oneof=male female。 eq:等於參數值,注意與 len 不同。對於字串,eq 約束字串本身的值,而 len 約束字串長度。例如 eq=10 len:等於參數值,例如 len=10 max:小於等於參數值,例如 max=10 min:大於等於參數值,例如 min=10 欄位約束 eqfield:定義欄位間相等約束,用於約束同一結構體中的欄位。例如:eqfield=Password eqcsfield:約束同一結構體中欄位等於另一個欄位(相對),確認密碼時可以使用,例如:eqcsfield=ConfirmPassword nefield:用來約束兩個欄位是否不同,確認兩種顏色是否一致時可以使用,例如:nefield=Color1 necsfield:約束兩個欄位是否不同(相對) 常用約束 unique:指定唯一性約束,不同型別處理不同:

Hero Image
Gin 框架绑定 JSON 参数使用 jsoniter

Gin 框架绑定 JSON 参数使用 jsoniter simple go build -tags=jsoniter ./... custom implement BindingBody interface // github.com/gin-gonic/gin@v1.6.3/binding/binding.go:36 // Binding describes the interface which needs to be implemented for binding the // data present in the request such as JSON request body, query parameters or // the form POST. type Binding interface { Name() string Bind(*http.Request, interface{}) error } // BindingBody adds BindBody method to Binding. BindBody is similar with Bind, // but it reads the body from supplied bytes instead of req.Body. type BindingBody interface { Binding BindBody([]byte, interface{}) error } package custom import ( "bytes" "fmt" "io" "net/http" jsoniter "github.com/json-iterator/go" "github.com/gin-gonic/gin/binding" ) // BindingJSON 替换Gin默认的binding,支持更丰富JSON功能 var BindingJSON = jsonBinding{} // 可以自定义jsoniter配置或者添加插件 var json = jsoniter.ConfigCompatibleWithStandardLibrary type jsonBinding struct{} func (jsonBinding) Name() string { return "json" } func (jsonBinding) Bind(req *http.Request, obj interface{}) error { if req == nil || req.Body == nil { return fmt.Errorf("invalid request") } return decodeJSON(req.Body, obj) } func (jsonBinding) BindBody(body []byte, obj interface{}) error { return decodeJSON(bytes.NewReader(body), obj) } func decodeJSON(r io.Reader, obj interface{}) error { decoder := json.NewDecoder(r) if binding.EnableDecoderUseNumber { decoder.UseNumber() } if binding.EnableDecoderDisallowUnknownFields { decoder.DisallowUnknownFields() } if err := decoder.Decode(obj); err != nil { return err } return validate(obj) } func validate(obj interface{}) error { if binding.Validator == nil { return nil } return binding.Validator.ValidateStruct(obj) } // binding.JSON 替换成自定义的 ctx.ShouldBindWith(ms, binding.JSON) ctx.ShouldBindBodyWith(ms, binding.JSON)

Hero Image
Gin中文文档

自定义路由日志的格式 Gin 运行多个服务 XML、JSON、YAML 和 ProtoBuf 渲染(输出格式) 自定义路由日志的格式 default [GIN-debug] POST /foo --> main.main.func1 (3 handlers) [GIN-debug] GET /bar --> main.main.func2 (3 handlers) [GIN-debug] GET /status --> main.main.func3 (3 handlers) import ( "log" "net/http" "github.com/gin-gonic/gin" ) func main() { r := gin.Default() gin.DebugPrintRouteFunc = func(httpMethod, absolutePath, handlerName string, nuHandlers int) { log.Printf("endpoint %v %v %v %v\n", httpMethod, absolutePath, handlerName, nuHandlers) } r.POST("/foo", func(c *gin.Context) { c.JSON(http.StatusOK, "foo") }) r.GET("/bar", func(c *gin.Context) { c.JSON(http.StatusOK, "bar") }) r.GET("/status", func(c *gin.Context) { c.JSON(http.StatusOK, "ok") }) // Listen and Server in http://0.0.0.0:8080 r.Run() } Gin 运行多个服务 package main import ( "log" "net/http" "time" "github.com/gin-gonic/gin" "golang.org/x/sync/errgroup" ) var ( g errgroup.Group ) func router01() http.Handler { e := gin.New() e.Use(gin.Recovery()) e.GET("/", func(c *gin.Context) { c.JSON( http.StatusOK, gin.H{ "code": http.StatusOK, "error": "Welcome server 01", }, ) }) return e } func router02() http.Handler { e := gin.New() e.Use(gin.Recovery()) e.GET("/", func(c *gin.Context) { c.JSON( http.StatusOK, gin.H{ "code": http.StatusOK, "error": "Welcome server 02", }, ) }) return e } func main() { server01 := &http.Server{ Addr: ":8080", Handler: router01(), ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, } server02 := &http.Server{ Addr: ":8081", Handler: router02(), ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, } g.Go(func() error { return server01.ListenAndServe() }) g.Go(func() error { return server02.ListenAndServe() }) if err := g.Wait(); err != nil { log.Fatal(err) } } XML、JSON、YAML 和 ProtoBuf 渲染(输出格式) SecureJSON 使用 SecureJSON 可以防止 json 劫持,如果返回的数据是数组,则会默认在返回值前加上"while(1)"