Loading [MathJax]/jax/input/TeX/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Golang使用标签表达式校验结构体字段的有效性

Golang使用标签表达式校验结构体字段的有效性

作者头像
henrylee2cn
发布于 2019-04-04 07:08:34
发布于 2019-04-04 07:08:34
1.7K00
代码可运行
举报
文章被收录于专栏:Go实战Go实战
运行总次数:0
代码可运行

一、背景

在服务的API接口层面,我们常常需要验证参数的有效性。 Golang中,大部分参数校验场景实际上是先将数据Bind到结构体,然后校验其字段值。

一般地,校验结构体字段值有如下两种实现方式。

  1. Case-By-Case 针对每个需校验的结构体字段分别写校验代码
    • 优点:自由灵活,适应所有场景
    • 缺点:重复且琐碎的码农工作,易使人厌烦
  2. 规则匹配,在结构体标签中设置预先支持的验证规则,如emailmax:100等形式
    • 优点:使用简单,不需要写琐碎的代码
    • 缺点:强依赖有限的规则,缺乏灵活性,无法满足复杂场景,如多字段关联验证等

思考:有没有一种方式,即简单易用(少写代码),又能满足各种复杂的校验场景?

答案是:有!结构体标签表达式 go-tagexpr 的出现,为我们提供了兼得鱼和熊掌的第三种选择。

二、认识 go-tagexpr

go-tagexpr 允许Gopher们在 struct tag 写表达式代码,并通过高性能的解释器计算其结果。

安装

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
go get -u github.com/bytedance/go-tagexpr

下面使用一个小示例,演示含有枚举、比较、字段关联的较复杂场景。

示例代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import (
	"fmt"

	tagexpr "github.com/bytedance/go-tagexpr"
)

func ExampleTagexpr() {
	vm := tagexpr.New("te")
	type Meteorology struct {
		Season      string `te:"$=='spring'||$=='summer'||$=='autumn'||$=='winter'"`
		Weather     string `te:"$!='snowing' || (Season)$=='winter'"`
		Temperature int    `te:"{range:$>=-10 && $<38}{alarm:sprintf('Uncomfortable temperature: %v',$)}"`
	}
	m := &Meteorology{
		Season:      "summer",
		Weather:     "snowing",
		Temperature: 40,
	}
	r := vm.MustRun(m)
	fmt.Println(r.Eval("Season"))
	fmt.Println(r.Eval("Weather"))
	fmt.Println(r.Eval("Temperature@range"))
	fmt.Println(r.Eval("Temperature@alarm"))
	// Output:
	// true
	// false
	// false
	// Uncomfortable temperature: 40
}

代码诠释:

  • 新建一个标签名称为 te 的解释器 vm := tagexpr.New("te")
  • 定义一个结构体,添加标签表达式,并实例化一个 m 对象。其中 $ 表示当前字段值,(Season)$ 表示 Season 字段的值 type Meteorology struct { Season string `te:"$=='spring'||$=='summer'||$=='autumn'||$=='winter'"` Weather string `te:"$!='snowing' || (Season)$=='winter'"` Temperature int `te:"{range:$>=-10 && $<38}{alarm:sprintf('Uncomfortable temperature: %v',$)}"` } m := &Meteorology{ Season: "summer", Weather: "snowing", Temperature: 40, }
  • 将对象实例 m 放入解释器中运行,返回表达式对象 r r := vm.MustRun(m)
  • 计算 Season 字段匿名表达式($=='spring'||$=='summer'||$=='autumn'||$=='winter')的值。因字段值 summer 在穷举列表中,故表达式结果为“true” r.Eval("Season")
  • 计算 Weather 字段匿名表达式 $!='snowing' || (Season)$=='winter' 的值。因字段值为 snowing 且 Season 为 summer,故表达式结果为“false” r.Eval("Weather")
  • 计算 Temperature 字段的 range 表达式 $>=-10 && $<38 的值。因字段值为 40,超出给出的范围,所以结果为“false” r.Eval("Temperature@range")
  • 计算 Temperature 字段的 alarm 表达式 sprintf('Uncomfortable temperature: %v',$) 的值。这是一个调用内部函数的表达式,它打印并返回字符串,结果为“Uncomfortable temperature: 40” r.Eval("Temperature@alarm")

获取更多关于 go-expr 结构体标签表达式的语法知识 -> 查看这里

二、使用Validator校验

Validator 是有 go-expr 包提供的一个采用结构体标签表达式的参数校验组件。

主要特性

  • 它要求在每个待校验字段上添加结果为布尔值的匿名表达式
  • 当表达式结果为false时,表示验证不通过,此时组件将返回与该字段相关的错误信息
  • 它支持使用名称为msg且结果为字符串的表达式作为错误信息
  • 允许用户按需求自由修改错误信息的模板
  • 支持各种常见的运算符
  • 支持访问数组,切片,字典成员
  • 支持访问当前结构体中的任何字段
  • 支持访问嵌套字段,非导出字段等
  • 支持注册自定义的验证函数表达式
  • 内置len,sprintf,regexp,email,phone等函数表达式

安装

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
go get -u github.com/bytedance/go-tagexpr

我们基于前面示例稍作修改,来演示如何使用validator校验结构体字段的有效性。

示例代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import (
	"fmt"

	"github.com/bytedance/go-tagexpr/validator"
)

func ExampleValidator() {
	vd := validator.New("vd")
	type Meteorology struct {
		Season      string `vd:"$=='spring'||$=='summer'||$=='autumn'||$=='winter'"`
		Weather     string `vd:"$!='snowing' || (Season)$=='winter'"`
		Temperature int    `vd:"{@:$>=-10 && $<38}{msg:sprintf('Uncomfortable temperature: %v',$)}"`
		Contact     string `vd:"email($)"`
	}
	m := &Meteorology{
		Season:      "summer",
		Weather:     "rain",
		Temperature: 40,
		Contact:     "henrylee2cn@gmail.com",
	}
	err := vd.Validate(m)
	if err != nil {
		fmt.Println(err)
	}
	// Output:
	// Uncomfortable temperature: 40
}

代码诠释:

  • 新建一个标签名称为 vd 的校验器 vd := validator.New("vd")
  • 定义一个结构体,在标签上添加校验表达式,并使用 m 实例进行测试。 type Meteorology struct { Season string `vd:"$=='spring'||$=='summer'||$=='autumn'||$=='winter'"` Weather string `vd:"$!='snowing' || (Season)$=='winter'"` Temperature int `vd:"{@:$>=-10 && $<38}{msg:sprintf('Uncomfortable temperature: %v',$)}"` Contact string `vd:"email($)"` } m := &Meteorology{ Season: "summer", Weather: "rain", Temperature: 40, Contact: "henrylee2cn@gmail.com", }
  • 校验实例 m 的各字段值是否有效,如果无效,则返回error信息 err := vd.Validate(m)

注册自己的校验函数

可能你已注意到 email($) 这个表达式,它是默认注册的一个函数表达式,用于验证邮箱的有效性。其实我们也可以定义自己通用的函数表达式,以便较少标签中的代码量,增加代码复用性。

下面以 email 函数的实现为例,演示如何注册自己的校验函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var pattern = "^([A-Za-z0-9_\\-\\.\u4e00-\u9fa5])+\\@([A-Za-z0-9_\\-\\.])+\\.([A-Za-z]{2,8})$"

emailRegexp := regexp.MustCompile(pattern)

validator.RegValidateFunc("email", func(args ...interface{}) bool {
	if len(args) != 1 {
		return false
	}
	s, ok := args[0].(string)
	if !ok {
		return false
	}
	return emailRegexp.MatchString(s)
}, true)

其中,validator.RegValidateFunc 的定义如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
func RegValidateFunc(funcName string, fn func(args ...interface{}) bool, force ...bool) error

RegValidateFunc的force可选参数,表示是否强制覆盖已经注册了的同名函数。

**结论:**validator的使用方法非常简单、灵活且具有良好的扩展性,能够轻松满足各种复杂的验证场景。

获取更多关于 validator 校验器的语法知识 -> 查看这里

(adsbygoogle = window.adsbygoogle || []).push({});

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java14新特性:Switch表达式
Java 14的switch表达式使用箭头表达时,不需要我们在每一个case后都加上break,减少我们出错的机会。
一觉睡到小时候
2020/04/01
1.7K0
快收藏!最全GO语言实现设计模式
导语| 设计模式是针对软件设计中常见问题的工具箱,其中的工具就是各种经过实践验证的解决方案。即使你从未遇到过这些问题,了解模式仍然非常有用,因为它能指导你如何使用面向对象的设计原则来解决各种问题,提高开发效率,降低开发成本;本文囊括了GO语言实现的经典设计模式示例,每个示例都精心设计,力求符合模式结构,可作为日常编码参考,同时一些常用的设计模式融入了开发实践经验总结,帮助大家在平时工作中灵活运用。
腾讯云开发者
2023/02/13
8910
快收藏!最全GO语言实现设计模式
echo 源码分析(validator)
所以我们可以包装一下go-playground/validator来实现echo的validator
golangLeetcode
2022/08/02
9510
echo 源码分析(validator)
Go结构体标签
通过 reflect.Type 获取结构体成员信息 reflect.StructField 结构中的 Tag 被称为结构体标签(Struct Tag)。结构体标签是对结构体字段的额外信息标签。 Tag是结构体在编译阶段关联到成员的元信息字符串,在运行的时候通过反射的机制读取出来。 结构体标签由一个或多个键值对组成。键与值使用冒号分隔,值用双引号括起来。键值对之间使用一个空格分隔,具体的格式如下:
IT工作者
2023/06/02
1.4K0
数据类型和表达式
需要注意的是,Go语言中支持隐式类型转换,但是不同类型之间的转换需要满足特定的规则。另外,Go还提供了一种复合类型complex,用于表示复数。complex由实部和虚部两个float32或float64类型组成,可以用于数学运算。
用户1413827
2023/11/28
3690
Go 编程 | 连载 27 - 正则表达式
很多语言都是支持正则表达式的,Go 也不例外。正则表达式的用途之一就是从字符中查找出与指定正则表达式匹配的字符串子串。
RiemannHypothesis
2022/09/28
3990
Go程序例子(54):正则表达式
Go 提供了对正则表达式的内置支持。以下是一些在 Go 中常见的正则表达式相关任务示例。
用户11078756
2025/01/16
1160
Go程序例子(54):正则表达式
Go 编程 | 连载 09 - 条件和循环表达式
需要注意的是 Go 中 if 控制语句的 { 不可以换行,必须要跟 if 关键字在同一行,否则会报错。
RiemannHypothesis
2022/09/26
2390
Go 编程 | 连载 09 - 条件和循环表达式
C++正则表达式校验某个字符串是否是合格的email
C++正则表达式校验某个字符串是否是合格的email 可以借助正则表达式校验某个字符串是否是合规的电子邮箱。对于邮箱的正则表达式有严格的模式,如:^[a-zA-Z0-9_+&*-]+(?:\\.[a-
ccf19881030
2023/07/24
4230
C++正则表达式校验某个字符串是否是合格的email
正则表达式大全
正则表达式中的特殊字符 字符 含意 \ 做为转意,即通常在"\"后面的字符不按原来意义解释,如/b/匹配字符"b",当b前面加了反斜杆后/\b/,转意为匹配一个单词的边界。 -或- 对正则表达式功能字符的还原,如"*"匹配它前面元字符0次或多次,/a*/将匹配a,aa,aaa,加了"\"后,/a\*/将只匹配"a*"。 ^ 匹配一个输入或一行的开头,/^a/匹配"an A",而不匹配"An a" $ 匹配一个输入或一行的结尾,/a$/匹配"An a",而不匹配"an A" * 匹配前面元字符0次或多次,/b
Hongten
2018/09/18
4.7K0
gin中validator模块的源码分析
众所周知,在api层需要使用gin.Context中的ShouldBindJSON方法来对request中的json字段进行校验,例子如下:
编程黑洞
2023/03/06
4290
Golang结构体入门
1.结构体是值类型:在Go语言中,结构体是一种值类型,与数组和基本数据类型一样。当结构体被赋值给一个变量或传递给一个函数时,它的值会被复制一份。因此,对复制的结构体进行修改不会影响原来的结构体。
周小末天天开心
2023/10/16
1900
Golang(四)正则表达式使用
0. 前言 最近用到了 regexp 包,下面整理下正则表达式相关用法 参考 基础知识 - Golang 中的正则表达式 和 Golang regexp包中的函数和方法 做了汇总 1. 正则表达式 1.1 单一字符 . 匹配任意一个字符,如果设置 s = true,则可以匹配换行符 [字符类] 匹配“字符类”中的一个字符,“字符类”见后面的说明 [^字符类] 匹配“字符类”外的一个
西凉风雷
2022/11/23
3.5K0
go语言正则表达式
我们前两节课爬取珍爱网的时候,用到了很多正则表达式去匹配城市列表、城市、用户信息,其实除了正则表达式去匹配,还可以利用goquery和xpath第三方库匹配有用信息。而我利用了更优雅的正则表达式匹配。下来大概介绍下正则表达式。
我的小碗汤
2018/08/22
9350
go语言正则表达式
【愚公系列】2022年08月 Go教学课程 033-结构体方法重写、方法值、方法表达式
方法重写又称方法覆盖。方法重写概念方法的重写是指两个方法的返回值、方法名、参数的类型和个数相同(子类重写父类的方法)。方法的重写,不能发生在同类中,只能发生在子类中。若子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法。
愚公搬代码
2022/09/21
1710
【愚公系列】2022年08月 Go教学课程 033-结构体方法重写、方法值、方法表达式
Golang 正则表达式(regexp)
Go内置了(regexp包)对正则表达式的支持,这里是一般的正则表达式常规用法的例子。
孤烟
2020/09/27
10K0
一文带你入门仓颉编程语言(下)
2024年6月21日下午,华为终端BG软件部总裁龚体先生在华为开发者大会主题演讲《鸿蒙原生应用,全新出发!》中向全球开发者介绍了华为自研仓颉编程语言,并发布了HarmonyOS NEXT仓颉语言开发者预览版。这是华为首次公开发布仓颉编程语言。
倔强的石头_
2025/01/02
3320
一文带你入门仓颉编程语言(下)
Go语言之匿名函数和C++的lambda表达式
匿名函数通常比较短小,不希望在这个函数外部使用,这点类似与C++中的lamdba表达式。
灰子学技术
2023/10/30
4420
Go语言之匿名函数和C++的lambda表达式
javaee的OA项目(八)EL表达式和JSTL表达式 学习,是什么,为什么,如何使用的角度进行学习
利用estl语句进行计算一个班的男生和女生的人数。思路是,在jsp页面定义一个变量,之后判断遍历出来的学生的性别,如果是男,变量加一
一写代码就开心
2021/06/22
6440
javaee的OA项目(八)EL表达式和JSTL表达式 学习,是什么,为什么,如何使用的角度进行学习
第6章 | 表达式 | 优先级,块与分号,生命,if match
本章将介绍 Rust 表达式,它是函数体的组成部分,因而也是大部分 Rust 代码的组成部分。Rust 中的大多数内容是表达式。本章将探索表达式的强大功能以及如何克服它的局限性。我们将介绍在 Rust 中完全面向表达式的控制流,以及 Rust 的基础运算符如何独立工作和组合工作。
草帽lufei
2024/05/08
2010
第6章 | 表达式 | 优先级,块与分号,生命,if match
推荐阅读
相关推荐
Java14新特性:Switch表达式
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验