c++ 类中结构体 .cpp怎么写_Golang结构体校验

这篇博客介绍了在Golang中如何对结构体进行校验,包括使用if/else语句、go.pkg的validator库以及checker工具。文章通过示例展示了各种方法的优缺点,指出checker能降低代码耦合并提供更灵活的校验规则。
摘要由CSDN通过智能技术生成

ecd902dd168b8e799b6d968b09b9b384.png

在Golang的日常开发中,有时候需要对struct的每个字段(field)进行校验,从而判断结构体的值是否符合条件。

考虑下面的profile结构体:

type profile struct {
    // Info is pointer filed
    Info      *basicInfo
    Companies []company
}

type basicInfo struct {
    // 1 <= len <= 20
    Name string
    // 18 <= age <= 80
    Age int
    // 1<= len <= 64
    Email string
}

type company struct {
    // frontend,backend
    Position string
    // frontend: html,css,javascript
    // backend: C,Cpp,Java,Golang
    // SkillStack 'length is between [1,3]
    Skills []string
}

func getPassedProfile() profile {
    companies := []company{
        {
            Position: "frontend",
            Skills:   []string{"html", "css"},
        },
        {
            Position: "backend",
            Skills:   []string{"C", "Golang"},
        },
    }
    info := basicInfo{Name: "liang", Age: 24, Email: "yaopei.liang@foxmail.com"}
    return profile{
        Info:      &info,
        Companies: companies,
    }
}

对于profile类型的值,有下面的限制:

Info字段

  • Info不为nil
  • Name的长度限制为[1,20]
  • Age 的取值范围是[18,80]
  • Email 的长度限制为[1,64], 并且符合邮箱的格式

Companies字段

  • Position只能是frontend或者backend
  • 如果Position是frontend, 里面的元素取值只能是 html,css,javascript.
  • 如果Position是backend, 里面的元素取值只能是 C,Cpp,Java,Golang. - Skills的长度限制为[1,3]

下面分别讲述使用if/else, gin的校验器,和checker,三个方法对结构体参数进行校验。

使用if/else

使用if/else判断‘结构体参数是否合法。

func isValidProfile(pro profile) bool {
  if pro.Info == nil {
      return false
   }
   if len(pro.Info.Name) > 20 {
      return false
   }
   if pro.Info.Age < 18 && pro.Info.Age > 80 {
      return false
   }
   if len(pro.Info.Email) > 64 {
      return false
   }
   re := regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")

   if !re.MatchString(pro.Info.Email){
      return false
   }
   for _, comp := range pro.Companies {
      if len(comp.Skills) > 3 {
         return false
      }
      if comp.Position != "frontend" && comp.Position != "backend" {
         return false
      }
      if comp.Position == "frontend" {
         for _, skill := range comp.Skills {
            if skill != "html" && skill != "css" && skill != "javascript" {
               return false
            }
         }
      } else if comp.Position == "backend" {
         for _, skill := range comp.Skills {
            if skill != "C" && skill != "Cpp" && skill != "Java" && skill != "Golang" {
               return false
            }
         }
      }
   }
   return true
}

可以看到,对于上述的校验规则,可能需要写大段的if/else判断语句,当语句太长时,不适合阅读,并且与结构体强耦合。

使用go.pkg的validatior

go.pkg的validator,它是通过在结构体的字段添加标签(tag),来校验结构体。

profile结构体要改造成:

type profile struct {
    Info      *basicInfo 
    Companies []company  `validate:"dive,min=1,max=3"`
}

type basicInfo struct {
    Name  string `validate:"min=1,max=20"`
    Age   int    `validate:"min=18,max=80"`
    Email string `validate:"min=1,max=64,email"`
}

type company struct {
    // frontend,backend
    Position string
    // frontend: html,css,javascript
    // backend: C,Cpp,Java,Golang
    Skills []string `validate:"min=1,max=3"`
}

校验函数改为:

import  "gopkg.in/go-playground/validator.v10"

func TestValidator(t *testing.T) {
    pro := getPassedProfile()
    validate := validator.New()
    err := validate.Struct(pro)
    if err != nil {
        t.Errorf("%s", err.Error())
        return
    }
    for _, comp := range pro.Companies {
        if comp.Position != "frontend" && comp.Position != "backend" {
            t.Error("failed")
        }
        if comp.Position == "frontend" {
            for _, skill := range comp.Skills {
                if skill != "html" && skill != "css" && skill != "javascript" {
                    t.Error("failed")
                }
            }
        } else if comp.Position == "backend" {
            for _, skill := range comp.Skills {
                if skill != "C" && skill != "Cpp" && skill != "Java" && skill != "Golang" {
                    t.Error("failed")
                }
            }
        }
    }
    t.Log("passed")
}

可以看到,gopkg.in/go-playground/validator.v10虽然减少了部分代码,但是校验逻辑需要写在结构体的的标签上面,增加了代码耦合。另外,validator还不支持枚举的校验。

使用checker

本文介绍的checker由RuleChecker组成,在外部对结构体的每一个字段添加规则,降低代码耦合性,并且提供组合规则,枚举等规则,可以轻松实现不同规则的自由组合。

func getProfileChecker() checker.Checker {
    profileChecker := checker.NewChecker()

    infoNameRule := checker.NewLengthRule("Info.Name", 1, 20)
    profileChecker.Add(infoNameRule, "invalid info name")

    infoAgeRule := checker.NewRangeRuleInt("Info.Age", 18, 80)
    profileChecker.Add(infoAgeRule, "invalid info age")

    infoEmailRule := checker.NewAndRule([]checker.Rule{
        checker.NewLengthRule("Info.Email", 1, 64),
        checker.NewEmailRule("Info.Email"),
    })
    profileChecker.Add(infoEmailRule, "invalid info email")

    companyLenRule := checker.NewLengthRule("Companies", 1, 3)
    profileChecker.Add(companyLenRule, "invalid companies len")

    frontendRule := checker.NewAndRule([]checker.Rule{
        checker.NewEqRuleString("Position", "frontend"),
        checker.NewSliceRule("Skills",
            checker.NewEnumRuleString("", []string{"html", "css", "javascript"}),
        ),
    })
    backendRule := checker.NewAndRule([]checker.Rule{
        checker.NewEqRuleString("Position", "backend"),
        checker.NewSliceRule("Skills",
            checker.NewEnumRuleString("", []string{"C", "CPP", "Java", "Golang"}),
        ),
    })
    companiesSliceRule := checker.NewSliceRule("Companies",
        checker.NewAndRule([]checker.Rule{
            checker.NewLengthRule("Skills", 1, 3),
            checker.NewOrRule([]checker.Rule{
                frontendRule, backendRule,
            }),
        }))
    profileChecker.Add(companiesSliceRule, "invalid skill item")

    return profileChecker
}

func TestProfileCheckerPassed(t *testing.T) {
    profile := getPassedProfile()
    profileChecker := getProfileChecker()
    isValid, prompt, errMsg := profileChecker.Check(profile)
    if !isValid {
        t.Logf("prompt:%s", prompt)
        t.Logf("errMsg:%s", errMsg)
        return
    }
    t.Log("pass check")

通过的checker的自由搭配,TestProfileCheckerPassed函数无需添加额外的代码,即可完成校验,降低了代码耦合性。校验的逻辑都在checker里面,校验逻辑更为清晰。

参考文档

  1. checker
我的公众号:lyp分享的地方
我的知乎专栏: https:// zhuanlan.zhihu.com/c_12 75466546035740672
我的博客: http://www. liangyaopei.com
Github Page: https:// liangyaopei.github.io/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值