MST

星途 面试题库

面试题:Go反射实现通用数据校验框架

要求使用Go的反射机制设计并实现一个通用的数据校验框架。这个框架能够对任意结构体类型的数据进行校验,校验规则通过结构体标签来定义(例如:`validate:"required,max=10"`),涉及到必填校验、长度上限校验等常见校验规则。请详细阐述设计思路,并给出关键代码实现。
27.4万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 解析结构体标签:利用Go的反射机制获取结构体字段及其标签信息,提取出校验规则。
  2. 实现校验逻辑:针对不同的校验规则,如必填校验、长度上限校验等,分别实现对应的校验函数。
  3. 构建通用校验框架:将解析标签和校验逻辑整合,形成一个通用的校验函数,能够对任意结构体类型的数据进行校验。

关键代码实现

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")
    }
}