设计思路
- 解析结构体标签:利用Go的反射机制获取结构体字段及其标签信息,提取出校验规则。
- 实现校验逻辑:针对不同的校验规则,如必填校验、长度上限校验等,分别实现对应的校验函数。
- 构建通用校验框架:将解析标签和校验逻辑整合,形成一个通用的校验函数,能够对任意结构体类型的数据进行校验。
关键代码实现
package main
import (
"errors"
"fmt"
"reflect"
"strconv"
"strings"
)
// 定义错误类型
var (
ErrRequired = errors.New("field is required")
ErrMaxLen = errors.New("field length exceeds the maximum limit")
)
// 校验必填字段
func checkRequired(field reflect.Value) bool {
switch field.Kind() {
case reflect.String:
return field.Len() > 0
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() != 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return field.Uint() != 0
case reflect.Float32, reflect.Float64:
return field.Float() != 0
case reflect.Bool:
return field.Bool()
default:
return field.IsValid() &&!field.IsNil()
}
}
// 校验长度上限
func checkMaxLen(field reflect.Value, max int) bool {
switch field.Kind() {
case reflect.String:
return field.Len() <= max
default:
return true
}
}
// 解析校验规则
func parseValidateTag(tag string) (map[string]string, error) {
rules := make(map[string]string)
parts := strings.Split(tag, ",")
for _, part := range parts {
if strings.Contains(part, "=") {
keyValue := strings.Split(part, "=")
rules[keyValue[0]] = keyValue[1]
} else {
rules[part] = ""
}
}
return rules, nil
}
// 通用校验函数
func Validate(data interface{}) error {
valueOf := reflect.ValueOf(data)
if valueOf.Kind() == reflect.Ptr {
valueOf = valueOf.Elem()
}
if valueOf.Kind() != reflect.Struct {
return errors.New("input must be a struct")
}
typeOf := valueOf.Type()
for i := 0; i < valueOf.NumField(); i++ {
field := valueOf.Field(i)
tag := typeOf.Field(i).Tag.Get("validate")
if tag == "" {
continue
}
rules, err := parseValidateTag(tag)
if err != nil {
return err
}
if _, ok := rules["required"]; ok &&!checkRequired(field) {
return fmt.Errorf("%s: %w", typeOf.Field(i).Name, ErrRequired)
}
if maxStr, ok := rules["max"]; ok {
max, err := strconv.Atoi(maxStr)
if err != nil {
return fmt.Errorf("invalid max value for %s", typeOf.Field(i).Name)
}
if!checkMaxLen(field, max) {
return fmt.Errorf("%s: %w", typeOf.Field(i).Name, ErrMaxLen)
}
}
}
return nil
}
使用示例
type User struct {
Name string `validate:"required,max=10"`
Age int `validate:"required"`
}
func main() {
user := User{
Name: "John Doe",
Age: 30,
}
err := Validate(user)
if err != nil {
fmt.Println("Validation failed:", err)
} else {
fmt.Println("Validation passed")
}
}