从 PHP 到 AI + Golang,程序员自救转型手记(四十三):前后端数据验证

这是一个系列 Blog,作者将以一个 PHP 全栈工程师的身份,利用 AI 工具(claude code、codex、deepseek、豆包等):从零开始学习 golang 语言,并最终完成 ai-go-admin(github | gitee)开源项目的制作,全程记录分享。

在上一期,我们进行了 "前端表格组件",本期将完成:前后端数据验证

前后端数据验证

服务端数据验证

服务端一共有三种数据验证方案:

方案一

模型层写 binding tag,然后在控制器层使用 ginShouldBindJSON,在绑定 JSON 数据同时完成效验(基控制器默认的 CreateUpdate 方法都已经调过 ShouldBindJSON 了,模型定义写好验证规则即可)示例如下:

go 复制代码
Username string `gorm:"comment:用户名;type:varchar(64);not null" binding:"required"`

required 规则意为 必填,完整可用规则有很多,可以参考:go-playground/validator | gin 模型绑定和验证章节

go-playground/validator 包已随 gin 框架内置,可直接使用。

方案二

模型定义写 validate tag,如:

go 复制代码
Username string `gorm:"comment:用户名;type:varchar(64)" validate:"required"`

可用规则和 方案一binding 一致,validate 的写法是 go-playground/validator 原生提供的。

然后,服务层或控制器层重写 CreateUpdate 等方法手动完成效验,示例如下:

go 复制代码
type User struct {
    FirstName string `validate:"required"`
    Age       uint8  `validate:"gte=0,lte=130"`
    Email     string `validate:"required,email"`
}

validate := validator.New()

user := &User{
    Age:   135,
    Email: "Badger.Smith@gmail.com",
}

err := validate.Struct(user)

方案三

增加 DTO,并定义以上的 binding / validate tag,然后重写控制器/服务,AdminUpdateRequest 就是一个自定义 DTO,不理解的可以全局搜索参考。

何时在控制器效验,何时在服务层效验?

服务层:涉及数据库、父子关系、唯一性、字段联动的校验。

控制器层:只校验字段自身独立规则,如必填、长度、有效值,不依赖数据库、不跨字段判断。

例如 AdminRule 表title、name、type 的必填就应该放在控制器层,name 的重复、pid 不能是自身,就应该放在服务层;注意仓储层不做任何效验,仅使用 not null、唯一索引等兜底即可。

前端数据验证

前端主要是表单数据的验证,我们准备了一个验证工具包 src\utils\validate.ts,里边定义了各种验证规则,另外主要是导出了一个验证规则构建器:buildValidatorRule 函数,它可以非常方便的生成 el-form 表单的常用验证规则,示例如下:

element plus 的表单验证,是基于 async-validator

vue 复制代码
<template>
    <div>
        <el-form ref="formRef" :model="formItems" :rules="rules"></el-form>
    </div>
</template>

<script setup lang="ts">
import { regularPassword, buildValidatorRule } from '/@/utils/validate'
import type { FormItemRule } from 'element-plus'

const rules: Partial<Record<string, FormItemRule[]>> = reactive({
    username: [buildValidatorRule({ name: 'required', title: '用户名' }), buildValidatorRule({ name: 'account' })],
    nickname: [buildValidatorRule({ name: 'required', title: '昵称' })],
    email: [buildValidatorRule({ name: 'email', message: '邮箱错误' })],
    mobile: [buildValidatorRule({ name: 'mobile', message: '手机号错误' })],
    password: [
        {
            validator: (rule: any, val: string, callback: Function) => {
                if (props.manager.form.operate == 'create') {
                    if (!val) {
                        return callback(new Error('密码必填'))
                    }
                } else {
                    if (!val) {
                        return callback()
                    }
                }
                if (!regularPassword(val)) {
                    return callback(new Error('密码需要 6-32 位,禁止使用特殊符号'))
                }
                return callback()
            },
            trigger: 'blur',
        },
    ],
})
</script>
相关推荐
YuePeng1 小时前
别再让 AI 直接写 SQL 了:一个注解搞定十亿行数据的语义层
后端·github
半夜里咳嗽的狼1 小时前
Go 1.25 的 WaitGroup.Go 省了两行代码,也补不上这三个并发边界
后端·go
Lihua奏1 小时前
身份验证:登录之后,服务器怎么一直认得你?
后端
XuCoder1 小时前
Redis 缓存和 MySQL 数据不一致怎么办?双写一致性一次讲透
后端
程序员天天困1 小时前
Arthas ognl 表达式从入门到实战:掌握在线调试最强的表达式引擎
java·jvm·后端
ServBay2 小时前
全栈开发者的 AI 模型选择指南:Kimi K3、Claude、GPT 分别适合什么场景
开源·aigc·ai编程
Escape2 小时前
为什么你的 AI 越聊越傻?从 Token 到 Agent,彻底搞懂 AI Agent的秘密㊙️
前端·人工智能·后端
windliang2 小时前
Claude Code 源码分析(二):CC 的心脏 queryLoop Agent主循环
前端·ai编程·claude