validate

package
v1.3.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 16, 2022 License: Apache-2.0 Imports: 12 Imported by: 0

README

validate

validate包核查模块,用于对入参的校验

快速使用

这里举个例子,快速使用

基于isc-gobase的web的项目的示例:

// main.go 文件
package main

import (
  "bytes"
    "encoding/json"
    "github.com/gin-gonic/gin"
    "github.com/isyscore/isc-gobase/http"
    "github.com/isyscore/isc-gobase/isc"
    "github.com/isyscore/isc-gobase/logger"
    "github.com/isyscore/isc-gobase/server"
    "github.com/isyscore/isc-gobase/server/rsp"
    "github.com/isyscore/isc-gobase/validate"
    "io/ioutil"
    "strings"
)

func main() {
    server.Post("test/insert", InsertData)
    server.Run()
}

// InsertData 数据插入
func InsertData(c *gin.Context) {
    insertReq := InsertReq{}

    // 读取body数据,可以采用isc提供的工具
    err := isc.DataToObject(c.Request.Body, &insertReq)
    if err != nil {
        // ... 省略异常日志
        return
    }

    // api示例:核查入参
    if result, msg := validate.Check(insertReq); !result {
        // 参数异常
        rsp.FailedOfStandard(c, 53, msg)
        logger.Error(msg)
        return
    }

    rsp.SuccessOfStandard(c, "ok")
}

type InsertReq struct {
    Name    string `match:"value={zhou, chen}"`
    Profile string `match:"range=[0, 10)"`
}
# application.yml 文件
api-module: app/sample

base:
  api:
    # api前缀
    prefix: /api
  application:
    # 应用名称
    name: sample
  server:
    # 是否启用,默认:true
    enable: true
    # 端口号
    port: 8080
    # web框架gin的配置
    gin:
      # 有三种模式:debug/release/test
      mode: release

请求

curl -X POST \
  http://localhost:8080/api/app/sample/test/insert \
  -H 'Content-Type: application/json' \
  -d '{
	"name": "zhou",
	"profile": "abcde-abcde"
}'

返回异常

{
    "code": 53,
    "data": null,
    "message": "长度不合法"
}

非web的普通类核查示例:

package main

import (
    "github.com/isyscore/isc-gobase/validate"
    "github.com/isyscore/isc-gobase/logger"
)

type DemoInsert struct {
    // 对属性修饰 
    Name string `match:"value=zhou"`
    Age  int
}

func main() {
    var value DemoInsert
    var result bool
    var errMsg string

    value = DemoInsert{Name: "chen"}
    // 核查
    result, errMsg = validate.Check(value)
    if !result {
        // 属性 Name 的值 chen 不在只可用列表 [zhou] 中 
        logger.Error(errMsg)
    }
}

说明:

  1. 这里提供方法Check,用于核查属性,Check方法也支持核查指定属性,默认核查所有属性
  2. 提供标签match,标签内容中提供匹配器:value,该匹配器表示匹配的具体的一些值

更多功能

这里将核查部分分为匹配和处理两部分,匹配可以有多种的匹配器,核查的逻辑是只要有任何一个匹配器匹配上则认为匹配上,处理模块用于对匹配上的结果进行处理,比如返回指定的异常,或者匹配后接受还是拒绝对应的值,或者匹配后将某个值更改掉(待支持)

匹配模块
  • value:匹配指定的值
  • isBlank:值是否为空字符
  • isBlank:值是否为非空字符
  • range:匹配数值的范围(最大值和最小值,用法是数学表达式):数值(整数和浮点数)的大小、字符串的长度、数组的长度、时间的范围、时间的移动
  • model:匹配指定的类型:
    • id_card:身份证
    • phone: 手机号
    • fixed_phone:固定电话
    • mail: 邮件地址
    • ip: ip地址
  • condition:修饰的属性的表达式的匹配,提供#current和#root占位符,用于获取相邻属性的值
  • regex:匹配正则表达式
  • customize:匹配自定义的回调函数
处理模块
  • errMsg: 自定义的异常
  • accept: 匹配后接受还是拒绝
  • disable: 是否启用匹配,默认启用

匹配模块

匹配器可以有多个一起修饰,只要匹配上一个,则认为匹配上

1. 值匹配器:value

匹配指定的一些值,可以修饰一个,也可以修饰多个值,可以修饰字符,也可修饰整数(int、int8、int16、int32、int64)、无符号整数(uint、uint8、uint16、uint32、uint64)、浮点数(float32、float64)、bool类型和string类型。

提示:

  • 中间逗号也可以为中文,为了防止某些手误写错为中文字符
// 修饰一个值
type ValueBaseEntityOne struct {
    Name string `match:"value=zhou"`
    Age  int    `match:"value=12"`
}

// 修饰一个值
type ValueBaseEntity struct {
    Name string `match:"value={zhou, 宋江}"`
    Age  int    `match:"value={12, 13}"`
}

如果有自定义类型嵌套,则可以使用标签check,用于解析复杂结构

type ValueInnerEntity struct {
    InnerName string `match:"value={inner_zhou, inner_宋江}"`
    InnerAge  int    `match:"value={2212, 2213}"`
}

type ValueStructEntity struct {
    Name string `match:"value={zhou, 宋江}"`
    Age  int    `match:"value={12, 13}"`

    Inner ValueInnerEntity `match:"check"`
}

修饰的结构可以有如下

  • 自定义结构
  • 数组/分片:对应类型只有为复杂结构才会核查
  • map:其中的key和value类型只有是复杂结构才会核查

2. 空值匹配器:isBlank

匹配string类型的值是否为空字符,false:字符不为空则匹配上,true:字符为空则匹配上

// 默认为true
type IsBlankEntity2 struct {
    Name string `match:"isBlank"`
    Age  int
}

// 同上
type IsBlankEntity3 struct {
    Name string `match:"isBlank=true"`
    Age  int
}

type IsBlankEntity1 struct {
    Name string `match:"isBlank=false"`
    Age  int
}

3. 非空匹配器:isUnBlank

匹配string类型的值是否为非空字符,true:字符非空则匹配上,false:字符为空则匹配上

// 默认为true
type IsBlankEntity2 struct {
    Name string `match:"isBlank"`
    Age  int
}

// 同上
type IsBlankEntity3 struct {
    Name string `match:"isBlank=true"`
    Age  int
}

type IsBlankEntity1 struct {
    Name string `match:"isBlank=false"`
    Age  int
}

4. 范围匹配器:range

匹配类型的指定范围,方式使用数学表达式"["、"]"、"("、")",使用数学表达式的开闭符号

  • [:表示大于等于
  • ]:表示小于等于
  • (:表示大于
  • ):表示小于

比如:

  • [1, 10):表示大于等于1而且小于10
  • (1, 10):表示大于1而且小于10
  • (1,):表示大于1
  • (,10]:表示小于等于10,也可以[,10]

修饰的类型有

  • 整数:比较大小,int、int8、int16、int32、int64
  • 无符号整数:比较大小,uint、uint8、uint16、uint32、uint64
  • 浮点数:比较大小,float32、float64
  • 分片:匹配分片的长度
  • 时间类型(time.Time):时间的范围,时间格式支持如下
    • yyyy
    • yyyy-MM
    • yyyy-MM-dd
    • yyyy-MM-dd HH
    • yyyy-MM-dd HH:mm
    • yyyy-MM-dd HH:mm:ss
    • yyyy-MM-dd HH:mm:ss.SSS
    • now:表示当前时间
  • 变量时间:除了使用数学表达式外,还支持past、future这两个关键字,用于表示过去和未来的时间
  • 时间计算:用于计算当前的时间向前或者向后推几个小时或者几分钟这种;(-1M, ):表示最近一个月的时间;(-1M3d, ):表示最近一个月零3天的时间,表示大于时间向前推一个月零三天的时间
    • -/+:表示往前推还是往后推
    • y:年
    • M:月
    • d:日
    • h:小时
    • m:分钟
    • s:秒
// 整数类型1
type RangeIntEntity1 struct {
    Age  int `match:"range=[1, 2]"`
}

// 整数类型2
type RangeIntEntity2 struct {
    Age  int `match:"range=[3,]"`
}

// 整数类型3
type RangeIntEntity3 struct {
    Age  int `match:"range=[3,)"`
}

// 浮点数类型
type RangeFloatEntity struct {
    Money float32 `match:"range=[10.37, 20.31]"`
}

// 字符类型
type RangeStringEntity struct {
    Name string `match:"range=[2, 12]"`
}

// 分片类型
type RangeSliceEntity struct {
    Age  []int `match:"range=[2, 6]"`
}

// 时间类型1
type RangeTimeEntity1 struct {
    CreateTime time.Time `match:"range=[2019-07-13 12:00:23.321, 2019-08-23 12:00:23.321]"`
}

// 时间类型2
type RangeTimeEntity2 struct {
    CreateTime time.Time `match:"range=[2019-07-13 12:00:23.321, ]"`
}

// 时间类型3
type RangeTimeEntity3 struct {
    CreateTime time.Time `match:"range=(, 2019-07-23 12:00:23.321]"`
}

// 时间类型4
type RangeTimeEntity4 struct {
    CreateTime time.Time `match:"range=[2019-07-23 12:00:23.321, now)"`
}

// 时间类型4
type RangeTimeEntity5 struct {
    CreateTime time.Time `match:"range=past"`
}

// 时间类型4
type RangeTimeEntity6 struct {
    CreateTime time.Time `match:"range=future"`
}

// 时间计算:年
type RangeTimeCalEntity1 struct {
    CreateTime time.Time `match:"range=(-1y, )"`
}

// 时间计算:月
type RangeTimeCalEntity2 struct {
    CreateTime time.Time `match:"range=(-1M, )"`
}

// 时间计算:月日
// 顺序不能乱:yMdhms,中间可以为空,比如:-1y3d
type RangeTimeCalEntity2And1 struct {
    CreateTime time.Time `match:"range=(-1M3d, )"`
}

// 时间计算:日
type RangeTimeCalEntity3 struct {
    CreateTime time.Time `match:"range=(-3d, )"`
}

// 时间计算:时
type RangeTimeCalEntity4 struct {
    CreateTime time.Time `match:"range=(-4h, )"`
}

// 时间计算:分
type RangeTimeCalEntity5 struct {
    CreateTime time.Time `match:"range=(-12m, )"`
}

// 时间计算:秒
type RangeTimeCalEntity6 struct {
    CreateTime time.Time `match:"range=(-120s, )"`
}

// 时间计算:正负号
type RangeTimeCalEntity7 struct {
    CreateTime time.Time `match:"range=(2h, )"`
}

5. 类型匹配器:model

类型匹配器:指定的几种内置类型进行匹配

  • id_card:身份证
  • phone: 手机号
  • fixed_phone:固定电话
  • mail: 邮件地址
  • ip: ip地址
type ValueModelIdCardEntity struct {
    Data string `match:"model=id_card"`
}

type ValueModelPhone struct {
    Data string `match:"model=phone"`
}

type ValueModelFixedPhoneEntity struct {
    Data string `match:"model=fixed_phone"`
}

type ValueModelEmailEntity struct {
    Data string `match:"model=mail"`
}

type ValueModelIpAddressEntity struct {
    Data string `match:"model=ip"`
}

6. 表达式匹配器:condition

表达式匹配器:用于数学计算表达式进行计算,表达式是返回bool类型的表达式。提供两个占位符

  • #current:当前修饰的值
  • #root:当前属性所在的对象,比如:#root.Age,表示当前对象中的其他属性Age的值
// 测试基本表达式
type ValueConditionEntity1 struct {
    Data1 int `match:"condition=#current + #root.Data2 > 100"`
    Data2 int `match:"condition=#current < 20"`
    Data3 int `match:"condition=(++#current) >31"`
}

// 测试表达式
type ValueConditionEntity2 struct {
    Age   int `match:"condition=#root.Judge"`
    Judge bool
}

7. 正则表达式匹配器:regex

正则表达式匹配器:用于匹配自定义的正则表达式

type ValueRegexEntity struct {
    Name string `match:"regex=^zhou.*zhen$"`
    Age  int    `match:"regex=^\\d+$"`
}

8. 自定义回调匹配器:customize

该匹配器可以用于自定义扩展,比如实际业务场景,某个字段在数据库中存在,这种情况就需要用户自定义扩展

比如:

package fun

type CustomizeEntity1 struct {
    // fun.Judge1是对应的函数
    Name string `match:"customize=judge1Name"`
}

func JudgeString1(name string) bool {
    if name == "zhou" || name == "宋江" {
        return true
    }

    return false
}

// 由于go反射功能没那么强,因此需要用户自己先将函数和name进行注册
func init() {
  validate.RegisterCustomize("judge1Name", JudgeString1)
}
说明:

其中自定义的函数有相关的要求,参数可以为一个,也可以为两个,返回
参数:

  • 一个值:为修饰的属性的值
  • 两个值:第一个值为属性所在的对象的值,第二个值为属性的值

返回值:

  • 一个值:则为bool类型(表示是否匹配上)
  • 两个值:第一个为bool类型(表示是否匹配上),第二个为string类型(匹配或者没有匹配上的自定义错误)
package fun

import (
    "fmt"
    "github.com/isyscore/isc-gobase/validate"
)

type CustomizeEntity2 struct {
    Name string `match:"customize=judge2Name"`
}

type CustomizeEntity3 struct {
    Name string `match:"customize=judge3Name"`
    Age  int
}

func JudgeString2(name string) (bool, string) {
    if name == "zhou" || name == "宋江" {
        return true, ""
    }

    return false, "没有命中可用的值'zhou'和'宋江'"
}

func JudgeString3(customize CustomizeEntity3, name string) (bool, string) {
    if name == "zhou" || name == "宋江" {
    if customize.Age > 12 {
        return true, ""
    } else {
        return false, "用户[" + name + "]" + "没有满足年龄age > 12," + "当前年龄为:" + fmt.Sprintf("%v", customize.Age)
    }

    } else {
        return false, "没有命中可用的值'zhou'和'宋江'"
    }
}

func init() {
    validate.RegisterCustomize("judge2Name", JudgeString2)
    validate.RegisterCustomize("judge3Name", JudgeString3)
}

处理模块

当匹配后如何处理,这里分为了如下几种处理

1. 匹配上接受/拒绝:accept

匹配后是接收还是拒绝,目前业内的处理方式都是匹配后接收,在概念上其实叫白名单,对于黑名单的处理,业内是没有,而我们这里用accept实现白名单和黑名单的概念

type AcceptEntity1 struct {
    // 表示只要匹配上name为zhou的,则拒绝
    Name string `match:"value=zhou" accept:"false"`
    Age  int
}

type AcceptEntity2 struct {
    // 表示name为空,则拒绝,表示需要非空才行,以下同`match:"isUnBlank"`
    Name string `match:"isBlank" accept:"false"`
    Age  int
}

// 只要任何一个匹配上,则接受
type AcceptEntity3 struct {
    Name string `match:"isBlank=true value=zhou" accept:"true"`
    Age  int
}

2. 自定义异常:errMsg

匹配后返回自定义的异常,提供了两个占位符#current表示修饰的当前属性的值,#root当前属性所在的结构的值,#root.Age表示当前结构中的属性Age对应的值

type ErrMsgEntity1 struct {
    Name string `match:"value=zhou" errMsg:"对应的值不合法"`
    Age  int
}

type ErrMsgEntity2 struct {
    Name string `match:"value=zhou"`
    Age  int    `match:"condition=#current > 10" errMsg:"当前的值不合法,应该大于10,当前值为#current,对应的名字为#root.Name"`
}

3. 启用:disable

表示是否启用整个核查

type DisableEntity1 struct {
    Name string `match:"value=zhou" disable:"true"`
    Age  int
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Check

func Check(object any, fieldNames ...string) (bool, string)

func RegisterCustomize

func RegisterCustomize(funName string, fun any)

Types

type CheckResult

type CheckResult struct {
	Result bool
	ErrMsg string
}

type CollectorEntity

type CollectorEntity struct {
	// contains filtered or unexported fields
}

type MatchCollector

type MatchCollector func(objectTypeFullName string, fieldKind reflect.Kind, objectFieldName string, tagName string, subCondition string, errMsg string)

Directories

Path Synopsis
fun

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL