<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          echo 源碼分析之校驗(yàn)器:Validator

          共 10019字,需瀏覽 21分鐘

           ·

          2021-05-10 17:57

          echo 默認(rèn)沒有自己的validator,只提供了接口,需要自己實(shí)現(xiàn)

          Echo struct {    Validator        Validator}

          validator需要實(shí)現(xiàn)Validate接口

          Validator interface {    Validate(i interface{}) error}

          所以我們可以包裝一下go-playground/validator來實(shí)現(xiàn)echo的validator

          由于go-playground/validator并沒有實(shí)現(xiàn)Validate方法,所以不能直接賦值

          e.Validator := validator.New()

          如何實(shí)現(xiàn)呢,可以自定義一個(gè)接口,中間調(diào)用validate.Struct(i)方法

          package main
          import ( "sync"
          "github.com/go-playground/validator/v10")
          type CustomValidator struct { once sync.Once validate *validator.Validate}
          func (c *CustomValidator) Validate(i interface{}) error { c.lazyInit() return c.validate.Struct(i)}
          func (c *CustomValidator) lazyInit() { c.once.Do(func() { c.validate = validator.New() })}
          func NewCustomValidator() *CustomValidator { return &CustomValidator{}}

          接著就可以賦值給echo的validator了

          package main
          import ( "fmt" "net/http"
          "github.com/labstack/echo/v4")
          type User struct { Name string `json:"name" param:"name" query:"name" form:"name" xml:"name" validate:"required"` // //curl -XGET http://localhost:1323/users/Joe\?email\=joe_email Email string `json:"email" form:"email" query:"email"`}
          func main() { e := echo.New() e.Validator = NewCustomValidator() e.GET("/users/:name", func(c echo.Context) error { u := new(User) u.Name = c.Param("name") if err := c.Bind(u); err != nil { return c.JSON(http.StatusBadRequest, nil) }
          if err := c.Validate(u); err != nil { return c.JSON(http.StatusBadRequest, nil) } return c.JSON(http.StatusOK, u) }) fmt.Println(e.Start(":1336"))}

          我們看下go-playground/validator包含哪些文件

          % lsLICENSEMakefileREADME.md_examplesnon-standardtestdatatranslations

          baked_in.gobenchmarks_test.gocache.gocountry_codes.godoc.goerrors.gofield_level.gogo.modgo.sumlogo.pngregexes.gostruct_level.gotranslations.goutil.govalidator.govalidator_instance.govalidator_test.go

          % ls non-standard/validators/notblank.gonotblank_test.go

          主要是下面幾個(gè)部分:

          baked_in.go :定義默認(rèn)【標(biāo)簽校驗(yàn)器】和【別名校驗(yàn)器】,程序初始的時(shí)候直接賦值了默認(rèn)的校驗(yàn)器,相當(dāng)于你買了個(gè)機(jī)器人送幾根電池的行為。當(dāng)然這邊你的校驗(yàn)器可以手動(dòng)添加自定義,后面會(huì)說到

          cache.go:定義結(jié)構(gòu)體校驗(yàn)器緩存、字段校驗(yàn)器緩存和獲取的方法,一個(gè)validator對象如果一直存活,他會(huì)把之前處理過的結(jié)構(gòu)體或者字段校驗(yàn)器進(jìn)行緩存.

          regexes.go:【標(biāo)簽校驗(yàn)器】里面有一些使用到正則進(jìn)行校驗(yàn)的,這邊存儲(chǔ)的就是靜態(tài)的正則表達(dá)式

          util.go:工具類,一般是用在【標(biāo)簽校驗(yàn)器】里面進(jìn)行處理

          validator.go:校驗(yàn)類主體,提供四個(gè)主要的校驗(yàn)方法


          validator一共提供了四種校驗(yàn)器:

          validationFuncs     map[string]Func  //規(guī)則類型的校驗(yàn) 【tag標(biāo)簽】->        校驗(yàn)規(guī)則

          structLevelFuncs    map[reflect.Type]StructLevelFunc  //規(guī)則結(jié)構(gòu)體的校驗(yàn)       【結(jié)構(gòu)體類型】->        校驗(yàn)規(guī)則

          customTypeFuncs     map[reflect.Type]CustomTypeFunc  //類型校驗(yàn)器                   【數(shù)據(jù)類型】->         校驗(yàn)規(guī)則

          aliasValidators     map[string]string                                     //別名校驗(yàn)器                   【別名匹配規(guī)則組合】->   校驗(yàn)規(guī)則


          其中比較重要的就是

          validator.govalidator_instance.go

          這兩個(gè)文件,在validator_instance.go中的方法是公有的

          func New() *Validate {
          tc := new(tagCache) tc.m.Store(make(map[string]*cTag))
          sc := new(structCache) sc.m.Store(make(map[reflect.Type]*cStruct))
          v := &Validate{ tagName: defaultTagName, aliases: make(map[string]string, len(bakedInAliases)), validations: make(map[string]internalValidationFuncWrapper, len(bakedInValidators)), tagCache: tc, structCache: sc, }
          // must copy alias validators for separate validations to be used in each validator instance for k, val := range bakedInAliases { v.RegisterAlias(k, val) }
          // must copy validators for separate validations to be used in each instance for k, val := range bakedInValidators {
          switch k { // these require that even if the value is nil that the validation should run, omitempty still overrides this behaviour case requiredIfTag, requiredUnlessTag, requiredWithTag, requiredWithAllTag, requiredWithoutTag, requiredWithoutAllTag: _ = v.registerValidation(k, wrapFunc(val), true, true) default: // no need to error check here, baked in will always be valid _ = v.registerValidation(k, wrapFunc(val), true, false) } }
          v.pool = &sync.Pool{ New: func() interface{} { return &validate{ v: v, ns: make([]byte, 0, 64), actualNs: make([]byte, 0, 64), misc: make([]byte, 32), } }, }
          return v}

          1,先創(chuàng)建了兩個(gè)校驗(yàn)器緩存(緩存思想)

          2,設(shè)置validator的標(biāo)簽、緩存信息等

          3,注冊默認(rèn)校驗(yàn)器

          4,注冊默認(rèn)tag校驗(yàn)器

          5,返回validator

          由于validate是每一個(gè)請求都需要的高頻操作,所以非常關(guān)注性能,盡量使用緩存。

          校驗(yàn)器結(jié)構(gòu)體

          Ⅰ.cTag(tag規(guī)則)

                 cTag是一個(gè)鏈表,存儲(chǔ)一連串的相關(guān)聯(lián)tag的校驗(yàn)器,比如說這邊是作為存儲(chǔ)一個(gè)Field的相關(guān)所有標(biāo)簽,看一下cTag的結(jié)構(gòu):

          type cTag struct {  tag            string  //標(biāo)簽  aliasTag       string  //  actualAliasTag string  //  param          string  //如果是比較類型的標(biāo)簽,這里存放的是比較的值,比如說 min=10,這里存放的是【10】這個(gè)值  hasAlias       bool    //是否有別名校驗(yàn)器標(biāo)簽  typeof         tagType //對應(yīng)的tagType  hasTag         bool    //是否存在tag標(biāo)簽  fn             Func    //當(dāng)前cTag對應(yīng)的【tag標(biāo)簽校驗(yàn)器】  next           *cTag   //下一個(gè)cTag標(biāo)簽}

          Ⅱ.cFeild(字段規(guī)則)

                cField代表一個(gè)結(jié)構(gòu)體字段對應(yīng)的規(guī)則,他會(huì)包含這個(gè)結(jié)構(gòu)體字段對應(yīng)的所有tag規(guī)則,也就是一組cTag鏈表存儲(chǔ)的規(guī)則:

          type cField struct {  Idx     int    //字段下標(biāo)  Name    string //字段名  AltName string //  cTags   *cTag  //Field對應(yīng)的cTag規(guī)則,是一個(gè)鏈表(一串的規(guī)則).}

          Ⅲ.cStruct(結(jié)構(gòu)體規(guī)則)

               同理,cStruct對應(yīng)的是這個(gè)結(jié)構(gòu)體所有字段(包含tag規(guī)則),以及這個(gè)結(jié)構(gòu)體自己對應(yīng)的【StructLevelFunc】的校驗(yàn)方法:

          type cStruct struct {  Name   string          //結(jié)構(gòu)體名稱  fields map[int]*cField //結(jié)構(gòu)體對應(yīng)的字段map  fn     StructLevelFunc //結(jié)構(gòu)體校驗(yàn)器 (結(jié)構(gòu)體類型->結(jié)構(gòu)體校驗(yàn)器)}

          可以看下調(diào)用Struct來進(jìn)行驗(yàn)證的過程

          1,獲取結(jié)構(gòu)體的value

          2,通過value,進(jìn)行結(jié)構(gòu)體校驗(yàn)

          3,獲取err池錯(cuò)誤信息返回

          func (v *Validate) Struct(s interface{}) error {  return v.StructCtx(context.Background(), s)}


          func (v *Validate) StructCtx(ctx context.Context, s interface{}) (err error) {  vd := v.pool.Get().(*validate)  vd.validateStruct(ctx, top, val, val.Type(), vd.ns[0:0], vd.actualNs[0:0], nil)}

          其中 validate是校驗(yàn)類的主體,所有的注冊和緩存數(shù)據(jù)、錯(cuò)誤信息數(shù)據(jù)都是存儲(chǔ)在validate中的,看一下具體的數(shù)據(jù)結(jié)構(gòu):

          // Validate contains the validator settings passed in using the Config structtype Validate struct {  tagName             string                           //校驗(yàn)起作用的tag名  fieldNameTag        string                           //  validationFuncs     map[string]Func                  //規(guī)則類型的校驗(yàn)      【tag標(biāo)簽】->      校驗(yàn)規(guī)則  structLevelFuncs    map[reflect.Type]StructLevelFunc //規(guī)則結(jié)構(gòu)體的校驗(yàn)        【結(jié)構(gòu)體類型】->      校驗(yàn)規(guī)則  customTypeFuncs     map[reflect.Type]CustomTypeFunc  //類型校驗(yàn)器          【數(shù)據(jù)類型】->      校驗(yàn)規(guī)則  aliasValidators     map[string]string                //別名校驗(yàn)器           【別名匹配規(guī)則組合】->   校驗(yàn)規(guī)則  hasCustomFuncs      bool                             //是否存在類型校驗(yàn)器  hasAliasValidators  bool                             //是否有別名校驗(yàn)器  hasStructLevelFuncs bool                             //是否有結(jié)構(gòu)體校驗(yàn)器  tagCache            *tagCache                        //tag對應(yīng)的【校驗(yàn)規(guī)則方法】的緩存  structCache         *structCache                     //結(jié)構(gòu)體對應(yīng)的【校驗(yàn)規(guī)則方法】的緩存  errsPool            *sync.Pool                       //校驗(yàn)錯(cuò)誤獎(jiǎng)池}

          定義在validator_instance.go

          type validate struct {  v              *Validate  top            reflect.Value  ns             []byte  actualNs       []byte  errs           ValidationErrors  includeExclude map[string]struct{} // reset only if StructPartial or StructExcept are called, no need otherwise  ffn            FilterFunc  slflParent     reflect.Value // StructLevel & FieldLevel  slCurrent      reflect.Value // StructLevel & FieldLevel  flField        reflect.Value // StructLevel & FieldLevel  cf             *cField       // StructLevel & FieldLevel  ct             *cTag         // StructLevel & FieldLevel  misc           []byte        // misc reusable  str1           string        // misc reusable  str2           string        // misc reusable  fldIsPointer   bool          // StructLevel & FieldLevel  isPartial      bool  hasExcludes    bool}

          定義在validator.go

          在validator.go中還有兩個(gè)方法

          1,獲取結(jié)構(gòu)體的數(shù)據(jù)

          2,判斷是否為結(jié)構(gòu)體類型或者接口類型,不是的話直接進(jìn)行報(bào)錯(cuò)處理

          3,傳入結(jié)構(gòu)體數(shù)據(jù)進(jìn)行處理

          func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, current reflect.Value, typ reflect.Type, ns []byte, structNs []byte, ct *cTag) {}

          func (v *validatetraverseField(ctx context.Contextparent reflect.Valuecurrent reflect.Valuens []bytestructNs []bytecf *cFieldct *cTag) { }

          1,獲取結(jié)構(gòu)體校驗(yàn)器緩存,如果沒有獲取到,則對該結(jié)構(gòu)體進(jìn)行解析,然后返回對應(yīng)的校驗(yàn)器,否則往下

          2,判斷是否存在校驗(yàn)器,否則忽略該字段校驗(yàn)(這邊有一個(gè)判斷是first的判斷,如果是第一層,也就是傳進(jìn)來的機(jī)構(gòu)體一定會(huì)對它的Field進(jìn)行校驗(yàn))

          2,循環(huán)所有的Field,判斷Field是否包含在不校驗(yàn)的集合中,如果不包含則進(jìn)行校驗(yàn),包含則不校驗(yàn)(這邊通過傳入一個(gè)【includeExclude】的map結(jié)構(gòu)可以指定對哪些字段不進(jìn)行校驗(yàn).這個(gè)在【StructExcept】方法中會(huì)用到)

          3,判斷是否存在對應(yīng)的結(jié)構(gòu)體類型校驗(yàn)方法,如果存在則調(diào)用該方法進(jìn)行校驗(yàn)


          整個(gè)驗(yàn)證的過程就是利用反射和struct tag中定義的一些語法擴(kuò)展,對參數(shù)的值進(jìn)行校驗(yàn)。

          在很多工具類里面對于可能多次出現(xiàn)的東西都會(huì)進(jìn)行相應(yīng)的緩存處理,這邊也不例外,對于一個(gè)Validator。它可能會(huì)進(jìn)行多次校驗(yàn),那么可能會(huì)有重復(fù)的結(jié)構(gòu)體或者字段數(shù)據(jù),可以進(jìn)行緩存不需要下次再提取,所以這邊提供了兩個(gè)對應(yīng)的緩存。

          1.structCache(結(jié)構(gòu)體緩存)

                這個(gè)緩存存儲(chǔ)的就是 【結(jié)構(gòu)體類型】->【cStruct】之間的對應(yīng)關(guān)系,考慮并發(fā)的問題,這邊是進(jìn)行加鎖存儲(chǔ):

          type structCache struct {  lock sync.Mutex  m    atomic.Value // map[reflect.Type]*cStruct}

          對應(yīng)的緩存方法包含 Get、Set:

          func (sc *structCache) Get(key reflect.Type) (c *cStruct, found bool) {  c, found = sc.m.Load().(map[reflect.Type]*cStruct)[key]  return  }
          func (sc *structCache) Set(key reflect.Type, value *cStruct) {   m := sc.m.Load().(map[reflect.Type]*cStruct)   nm := make(map[reflect.Type]*cStruct, len(m)+1)  for k, v := range m {    nm[k] = v  }  nm[key] = value  sc.m.Store(nm)}

          2.tagCache(標(biāo)簽規(guī)則緩存)

                這個(gè)緩存存儲(chǔ)的就是 【tag】 ->【cTag】之間的對應(yīng)關(guān)系,考慮并發(fā)的問題,這邊是進(jìn)行加鎖存儲(chǔ):

          type tagCache struct {  lock sync.Mutex  m    atomic.Value // map[string]*cTag}

          對應(yīng)的緩存方法包含 Get、Set:

          func (tc *tagCache) Get(key string) (c *cTag, found bool) {  c, found = tc.m.Load().(map[string]*cTag)[key]  return}
          func (tc *tagCache) Set(key string, value *cTag) {   m := tc.m.Load().(map[string]*cTag)   nm := make(map[string]*cTag, len(m)+1)  for k, v := range m {    nm[k] = v  }  nm[key] = value  tc.m.Store(nm)}



          推薦閱讀


          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù) ebook 獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬 Gopher 交流學(xué)習(xí)。


          瀏覽 34
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色综合高清在线观看视频 | 成年人黄色网址 | 国产成人av 高清在线 | 久久精品无码一区二区无码性色 | 亚洲成人影片在线免费看 |