• Stars
    star
    8
  • Rank 2,030,533 (Top 42 %)
  • Language
    Go
  • License
    MIT License
  • Created almost 2 years ago
  • Updated almost 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Bind data to any Go value. Can use built-in and custom expression binding capabilities; supports data validation logic for Go values. // 将数据绑定到任何 Go 值。可使用内置和自定义表达式绑定能力;支持对Go值的数据校验逻辑.

English | 🇨🇳中文

Go Report Card

gbind

Encapsulate general parameter parsing and parameter verification logic, 
minimize repetitive code in daily development, and solve parameter binding 
and verification in a few lines of code

Features

  • Bind data to the specified structure based on tag information
    • Built-in HTTP request path, query, form, header, cookie binding ability
      • Binding for http uri parameters gbind:"http.path"
      • Binding for http query parameters gbind:"http.query.varname"
      • Binding for http header parameters gbind:"http.header.varname"
      • Binding for http form parameters gbind:"http.form.varname"
      • Binding for http cookie parameters gbind:"http.cookie.varname"
    • Built-in json binding capability, implemented with encoding/json
      • For HTTP body in json format, follow golang json parsing format uniformly json:"varname"
    • Support for setting default values of bound fields
      • Supports setting default values of bound fields when no data is passed in gbind:"http.query.varname,default=123"
    • Support custom binding parsing logic (not limited to HTTP requests, using gbind can do bindings similar to database tags and other scenarios)
      • You can register custom binding logic by calling the RegisterBindFunc function, such as implementing a binding of the form gbind:"simple.key"
  • Validate the field value according to the tag information, parameter validation logic refer to the validate package
    • Data validation of bound fields is performed according to the defined validatetag, which depends on github.com/go-playground/validator implementation, validate="required,lt=100"
    • Support custom validation logic, you can customize the data validation logic by calling the RegisterCustomValidation function
    • Support custom error message for validation failure
    • By defining the tag of err_msg, it supports custom error message when parameter validation fails, demo gbind:"http.cookie.Token" validate="required,lt=100" err_msg="Please complete the login"

Usage example

  • Use gbind's web API request parameters for binding and verification
package gbind

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
)

type Params struct {
	API    string `gbind:"http.path,default=/api/test"`
	Appkey string `gbind:"http.query.appkey,default=appkey-default"`
	Page   int    `gbind:"http.query.page,default=1"`
	Size   int    `gbind:"http.query.size,default=10"`
	Token  string `gbind:"http.cookie.Token" validate:"required" err_msg:"please login"`
	Host   string `gbind:"http.header.host,default=www.baidu.com"`
	Uids   []int  `gbind:"http.form.uids"`
}

func Controller(w http.ResponseWriter, r *http.Request) {
	var requestParams = &Params{}
	if _, err := BindWithValidate(context.Background(), requestParams, r); err != nil {
		w.WriteHeader(http.StatusBadRequest)
		return
	}
	bs, _ := json.MarshalIndent(requestParams, "", "\t")
	w.Write(bs)
}

func ExampleGbind() {
	w := httptest.NewRecorder()
	u, _ := url.Parse("http://gbind.baidu.com/api/test?appkey=abc&page=2")
	r := &http.Request{
		Method: http.MethodPost,
		Header: map[string][]string{
			"Host": {"gbind.baidu.com"},
		},
		PostForm: url.Values{
			"uids": {"1", "2", "3"},
		},
		URL: u,
	}
	r.AddCookie(&http.Cookie{
		Name:  "Token",
		Value: "foo-bar-andsoon",
	})

	Controller(w, r)

	fmt.Println(w.Result().Status)
	fmt.Println(w.Body.String())

	// Output:
	// 200 OK
	//{
	//	"API": "/api/test",
	//	"Appkey": "abc",
	//	"Page": 2,
	//	"Size": 10,
	//	"Token": "foo-bar-andsoon",
	//	"Host": "gbind.baidu.com",
	//	"Uids": [
	//		1,
	//		2,
	//		3
	//	]
	//}
}
  • Customize the binding logic, you can realize the binding of different scenarios, demo gbind:"simple.key"
package gbind

func TestRegisterBindFunc(t *testing.T) {
	g := NewGbind()
	g.RegisterBindFunc("simple", NewSimpleExecer)

	type Foo struct {
		Key string `gbind:"simple.key"`
	}
	f := &Foo{}
	_, err := g.Bind(context.WithValue(context.Background(), exprKey{}, "simple-k-d"), f, nil)
	assert.Nil(t, err)
	assert.Equal(t, "simple-k-d", f.Key)	
}

type exprKey struct{}

func NewSimpleExecer(values [][]byte) (Execer, error) {
	n := len(values)
	if n != 2 {
		return nil, errors.New("syntax error: simple error")
	}
	switch {
	case bytes.Equal(values[1], []byte("key")):
		return &simpleKeyExecer{}, nil
	}
	return nil, fmt.Errorf("syntax error: not support simple %s", values[1])
}

type simpleKeyExecer struct{}

// Exec
func (s *simpleKeyExecer) Exec(ctx context.Context, value reflect.Value, data interface{}, opt *DefaultOption) (context.Context, error) {
	err := TrySet(value, []string{ctx.Value(exprKey{}).(string)}, opt)
	return ctx, err
}

// Name
func (s *simpleKeyExecer) Name() string {
	return "simple.key"
}
  • Custom data verification logic, can realize verification in different scenarios, demo validate:"is-awesome"
func TestRegisterCustomValidation(t *testing.T) {
	g := NewGbind()

	g.RegisterCustomValidation("is-awesome", func(fl validator.FieldLevel) bool {
		return fl.Field().String() == "awesome"
	})

	{
		type Foo struct {
			Appkey string `gbind:"http.query.appkey" validate:"is-awesome"`
		}
		f := &Foo{}
		req := NewReq().AddQueryParam("appkey", "awesome").R()
		_, err := g.BindWithValidate(context.Background(), f, req)
		assert.Nil(t, err)
	}
}

benchmark

  • Stressed the binding capabilities of the gin framework and the gbind package. The simple binding capabilities of query+form, gbind has a performance improvement of more than 10 times, and the complex binding capabilities of query+form+header, gbind has 30 times or more performance improvement, the specific data are as follows
    • HTTP query+form parameter binding, gin, gbind package comparison
BenchmarkBind/gin-query-form-8         	  612357	      1937 ns/op	     304 B/op	      20 allocs/op
BenchmarkBind/gbind-query-form-8       	 6981271	      171.3 ns/op	     200 B/op	       5 allocs/op
- HTTP query+form+cookie parameter binding, gin, gbind package comparison
BenchmarkBind/gin-query-form-header-8  	  232152	      5143 ns/op	     736 B/op	      53 allocs/op
BenchmarkBind/gbind-query-form-header-8   6673236	      180.0 ns/op	     232 B/op	       5 allocs/op	

Binding supported underlying data types

  • basic data type
    • int、int8、int16、int32、int64
    • uint、uint8、uint16、uint32、uint64
    • float32、float64
    • bool
    • string
  • other data types
    • ptr
      • *int、*uint、*float32、*string
      • **int、**uint、**float32、**string
      • Multilevel pointer to any underlying data type
    • slice
      • []int, []uint, []bool, []string, etc.
      • []*int, []*uint, []*bool, []*string, etc.
      • a slice of any underlying data type (including pointers)
    • array
      • [1]int, [2]uint, [3]bool, [4]string, etc.
      • [5]*int, [6]*uint, [7]*bool, [8]*string, etc.
      • Arrays of any underlying data type (including pointers)
      • time.Duration