0x00 About

接口开发中, 比较常用的操作就是对输入的参数Bean进行字段属性值校验.

在Java中, 有Annotation(注解)可以让我们方便的在的类上面添加校验信息,

那么在Go中应该如何做到这一点呢?

0x01 structTag

StructType = "struct" "{" { FieldDecl ";" } "}" .

FieldDecl = (IdentifierList Type | EmbeddedField) [ Tag ] .

EmbeddedField = [ "*" ] TypeName .

Tag = string_lit .

也就是说, struct字段的后面, 可以添加一个字符串, 称之为Tag

然后, 通过go语言中的reflect反射机制, 就可以读取相应字段的Tag信息了.

接下来, 找一段代码直观感受一下:

type InParam struct {

StudentName string `json:"name" cc:"str,min=5,max=15"`

Score int `json:"score" cc:"num,min=0,max=100"`

}

package main

import (

"fmt"

"reflect"

)

func main() {

t := reflect.TypeOf(&InParam{"18", 25})

field := t.Elem().Field(0)

jsonName := field.Tag.Get("json")

cc := field.Tag.Get("cc")

fmt.Printf("%s(%s): %s", field.Name, jsonName, cc)

}

输出结果:

StudentName(name): str,min=5,max=15

需要注意的地方: reflect.TypeOf 的参数只能接入对象.

0x02 Validator

那么接下来的工作, 就是根据Tag内容开发一个公共函数.

这里提供了两种验证器:string和number, 分别验证其长度范围和取值范围.

package main

import (

"fmt"

"reflect"

"strings"

)

// 定义接口

type CCValid interface {

Validate(interface{}) (bool, error)

}

// 定义三个验证器

type CCDefaultValid struct {

}

type CCNumberValid struct {

Min int

Max int

}

type CCStringValid struct {

Min int

Max int

}

// 三个验证器实现接口方法

func (c CCNumberValid) Validate(obj interface{}) (bool, error) {

v := obj.(int)

if v < c.Min || v > c.Max {

return false, fmt.Errorf(":int value should in range (%d, %d)", c.Min, c.Max)

}

return true, nil

}

func (c CCStringValid) Validate(obj interface{}) (bool, error) {

l := len(obj.(string))

if l < c.Min || l > c.Max {

return false, fmt.Errorf(":string length should in range (%d, %d)", c.Min, c.Max)

}

return true, nil

}

func (c CCDefaultValid) Validate(obj interface{}) (bool, error) {

return true, nil

}

var tagName = "cc"

// 公共方法,对外提供检验处理

func CCValidate(s interface{}) []error {

var errs []error

v := reflect.ValueOf(s)

for i := 0; i < v.NumField(); i++ {

tag := v.Type().Field(i).Tag.Get(tagName)

if tag == "" || tag == "-" {

continue

}

validator := parseValidatorFromTag(tag)

valid, err := validator.Validate(v.Field(i).Interface())

if !valid && err != nil {

errs = append(errs, fmt.Errorf("%s%s", v.Type().Field(i).Name, err.Error()))

}

}

return errs

}

// 从Tag字符串里分析出使用哪个验证器,并赋值

func parseValidatorFromTag(tag string) CCValid {

args := strings.Split(tag, ",")

switch args[0] {

case "num":

v := CCNumberValid{}

fmt.Sscanf(strings.Join(args[1:], ","), "min=%d,max=%d", &v.Min, &v.Max)

return v

case "str":

v := CCStringValid{}

fmt.Sscanf(strings.Join(args[1:], ","), "min=%d,max=%d", &v.Min, &v.Max)

return v

}

return CCDefaultValid{}

}

0x03 在gin中的使用

我们开发校验器的目的, 当然, 是为了能在gin中使用啦

直接上代码, 理解起来不难:

type Result struct {

code int

msg string

data interface{}

}

func OK(msg string) Result {

return Result{0, msg, nil}

}

func NG(msg string) Result {

return Result{1, msg, nil}

}

// 解析JSON请求

r.POST("/login", func(c *gin.Context) {

var header MyHeader

var param Login

// 解析Header

if c.BindHeader(&header) != nil {

c.JSON(200, NG("invalid header"))

return

}

// 解析JSON

if c.BindJSON(&param) != nil {

c.JSON(200, NG("invalid json param"))

return

}

// 验证

for _, firstErr := range CCValidate(param) {

c.JSON(200, NG(firstErr.Error()))

return

}

c.JSON(200, gin.H{"hello": param.Username, "world": param.Password, "from": header.From})

})

当验证出错时, 会向客户端返回第一个发现的错误.

0x04 TODO

现在的代码来看, 业务功能还没开发呢, 已经写了这么多行代码了. 一点都不优雅.

接下来要考虑, 能不能利用中间件, 完成这些前置操作.

java 自定义validate_Golang-03 自定义validator,实现java注解功能-Go语言中文社区相关推荐

  1. java调用百度翻译_Java调用百度API实现翻译-Go语言中文社区

    下面是Java调用百度API实现翻译的具体步骤: 一.在写代码之前先在在百度翻译平台中,申请APP_ID 申请地址申请的详见点击打开链接 申请之后,会得到APP_ID和SECURITY_KEY 二.j ...

  2. java黄金连分数_蓝桥杯 | Java B组省赛真题练习——黄金连分数-Go语言中文社区...

    标题: 黄金连分数 黄金分割数0.61803... 是个无理数,这个常数十分重要,在许多工程问题中会出现.有时需要把这个数字求得很精确. 对于某些 精密工程,常数的精度很重要.也许你听说过哈勃太空望远 ...

  3. java jtextfield hint_JTextField 默认文字提示功能-Go语言中文社区

    当JTextField没有输入时,显示提示文字,类似于 通过实现FocusListener来实现. import java.awt.Color; import java.awt.event.Focus ...

  4. java awt run_Java中awt基本组件及其使用方法-Go语言中文社区

    1,按钮 此类创建一个标签按钮.当按下该按钮时,应用程序能执行某项动作.它有两种构造方法: public Button() 构造一个标签字符串为空的按钮. public Button(String l ...

  5. java以太坊源码分析_以太坊区块链Java(EthereumJ)学习笔记:区块链结构-Go语言中文社区...

    本文对EthereumJ的区块链相关的代码做一个简单的介绍. 以太坊区块链 以太坊区块链是在Bitcoin区块链的基础上发展起来的.区块链的数据结构既保留了Bitcoin区块链验证数据的真实性和完整性 ...

  6. grpc java 泛型_关于使用GRPC遇到的BUG-Go语言中文社区

    GRPC获取服务器数据是耗时操作,不能写在UI主线程中,可以写在子线程或使用AsyncTask实现获取数据,但实测,仅仅是获取少量数据,可以写在UI主线程中(虽然不推荐这么做).目前博主在AsyncT ...

  7. Java太密来福_这篇文章就是要让你入门java多线程【多线程入门】-Go语言中文社区...

    就在前几天,有位读者朋友私信宜春,说期待出一篇多线程的文章,我当时内心是小鹿乱撞啊-于是这几天茶不思饭不想,好几天深夜皆是辗转反侧,两目深凝,以至于这几天走起路来格外飘飘然,左摇右晃的,魔鬼般的步伐, ...

  8. Java元数据区的概念_java之元数据(metadata)-Go语言中文社区

    什么是元数据? 元数据是指用来描述数据的数据,更通俗一点,就是描述代码间关系,或者代码与其他资源(例如数据库表)之间内在联系的数据.在一些技术框架,如struts.EJB.hibernate就不知不觉 ...

  9. cotlin java go_Aspectj 在Android中的简单使用(Java + Kotlin)-Go语言中文社区

    OOP&AOP OOP(Object Oriented Programming):面向对象编程.把问题或功能模块化,每个模块处理自己的事. AOP(Aspect Oriented Progra ...

最新文章

  1. Python2 编码问题分析
  2. 情绪管理的快速实践方法---视频学习记录
  3. java 内存溢出 内存泄露_java 内存泄露、内存溢出、内存不足
  4. 抖音ai智能机器人挂机_电销秘诀 电销企业难以拒绝的AI智能电销机器人
  5. 文件操作(stat函数)
  6. mysql error1045 yes_MYSQL ERROR 1045 (28000): Access denied for user (using password: YES)问题的解决...
  7. 关于spark写入文件至文件系统并制定文件名之自定义outputFormat
  8. 协议:Modbus通讯协议详细
  9. C/C++中使用可变参数
  10. NOIP引水入城(dfs)
  11. yolo 深度学习_吴恩达深度学习笔记04.卷积神经网络 W3.目标检测(YOLO)
  12. 访问无偏移的谷歌地图——工具篇
  13. 虚拟文件系统(Virtual File System,VFS)
  14. 天超级计算机,2、天啦,古老的超级计算机!
  15. Matlab求解点到直线距离
  16. 微信商户批量转账到零钱
  17. python爬取豆瓣图书top250_python3 爬虫学习:爬取豆瓣读书Top250(四)
  18. JAVA美食小屋系统(JAVA毕业设计)
  19. oracle获取年初年末,月初月末,季度初季度末
  20. 区块链的前世今生:为什么说区块链是即将到来的数字革命?

热门文章

  1. win7驱动程序未经签名可以使用吗_如何解决高校机房计算机新CPU不支持win7系统的问题...
  2. Ubuntu 卸载 Nvidia 驱动和安装最新驱动
  3. Linux下CMake简明教程(三)同一目录下多个源文件
  4. yolov3 数据预处理
  5. 居民信息管理系统java_基于jsp的社区住户信息管理系统-JavaEE实现社区住户信息管理系统 - java项目源码...
  6. web浏览器录音:web audio api
  7. 强化学习笔记: MDP - Policy iteration
  8. MySQL 四种事务隔离级别详解及对比--转
  9. 机器学习实战读书笔记(2)决策树
  10. hive表信息查询:查看表结构、表操作等--转