Echo.Validate() 默认不校验表单字段,因为其默认 validator 仅对带 validate tag 的结构体字段校验,而 FormValue() 返回的是原始字符串映射,需显式调用 c.Bind(&req) 才触发校验。

为什么 echo.Validate() 默认不校验表单字段?
因为 Echo 的默认 validator(基于 go-playground/validator/v10)只对结构体字段做 tag 校验,而 echo.Context.FormValue() 或 c.Request().PostForm 返回的是原始字符串映射,不会自动绑定到带 validate tag 的 struct 上——你得显式调用 c.Bind() 或手动构造 struct 并校验。
常见错误现象:required 校验始终通过、空字符串不触发 email 格式检查、min=1 对 "0" 字符串无效。
- 必须用
c.Bind(&req)触发 validator,不能只靠c.FormValue("name")后手写 if 判断 - struct 字段类型要匹配:表单值是字符串,但你想校验数字范围?字段得是
int或int64,且需form:"age" validate:"required,gt=0";否则字符串 "0" 会被 validator 跳过类型转换 - 如果 POST 是
application/x-www-form-urlencoded或multipart/form-data,Bind()才能正确解析;若误设为application/json,表单字段全为空
如何注册自定义 validator(比如手机号、中文名)?
直接调用 validator.RegisterValidation() 注册函数,但注意:必须在 Echo 实例创建前完成注册,否则新 validator 不生效——因为 Echo 内部 validator 实例在 echo.New() 时已初始化。
示例:注册中文姓名校验(至少 2 字,纯汉字或含空格、·):
立即学习“go语言免费学习笔记(深入)”;
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
import "github.com/go-playground/validator/v10"
func init() {
v := echo.New().Validator.(*echo.DefaultValidator).Validator
v.RegisterValidation("chinese_name", func(fl validator.FieldLevel) bool {
s := fl.Field().String()
if len(s) < 2 {
return false
}
for _, r := range s {
if !unicode.Is(unicode.Han, r) && r != ' ' && r != '·' {
return false
}
}
return true
})
}
- 注册名(如
"chinese_name")要和 struct tag 里的一致:Name string `form:"name" validate:"required,chinese_name"` - 函数接收
validator.FieldLevel,用fl.Field().String()拿值;别用fl.Param(),那是给带参数的 validator(如len=5)用的 - 如果 validator 依赖外部服务(如查手机号归属地),别在注册函数里做网络调用——它会在每次校验时同步执行,阻塞请求;应改用中间件预处理或业务层异步校验
表单多文件上传 + 文件名/大小校验怎么做?
Echo 的 c.MultipartForm() 返回的 *multipart.Form 中,File 是 map[string][]*multipart.FileHeader,validator 默认不处理这个结构。你得自己提取 FileHeader 字段,再用 validator 校验其 Filename 和 Size。
推荐做法:定义一个轻量 struct 封装上传元信息,手动赋值后校验:
type UploadMeta struct {
Filename string `validate:"required,excludesall=\/:*?"<>|"`
Size int64 `validate:"required,gte=1,lte=10485760"` // 10MB
}
// 使用时:
form, _ := c.MultipartForm()
files := form.File["avatar"]
if len(files) > 0 {
meta := UploadMeta{
Filename: files[0].Filename,
Size: files[0].Size,
}
if err := validator.New().Struct(meta); err != nil {
c.JSON(http.StatusBadRequest, map[string]string{"error": "文件名或大小不合法"})
return
}
}
-
excludesall比regexp更快,适合过滤 Windows 非法文件名字符 -
Size是字节数,不是用户传的字符串,所以不用转 int64 ——FileHeader.Size已是int64 - 不要试图把
*multipart.FileHeader直接丢给Bind():它没有对应 form tag 映射逻辑,会静默失败
Custom Validator 报错提示中文不显示?
因为 go-playground/validator 默认返回英文错误信息,且 Echo 的 HTTPError 处理器不自动翻译。想返回中文,有两个轻量方案:
- 用
ut.Translator+en-utf8.json/zh-utf8.json翻译包,但需提前加载并挂载到 validator 实例——对简单项目太重 - 更实用的做法:在
echo.HTTPErrorHandler里拦截validator.ValidationErrors,用strings.ReplaceAll替换关键词(如"Key: 'User.Email' Error:Field validation for 'Email' failed on the 'email' tag"→"邮箱格式不正确") - 关键点:判断错误类型必须用
errors.As(err, &ve),其中ve是validator.ValidationErrors;直接err.Error()会丢失结构化信息
真正容易被忽略的是 validator 的缓存行为:同一个 struct 类型首次校验后,校验规则会被缓存;如果你在运行时动态修改 tag(比如从 required 改成 required,email),旧缓存仍生效——重启服务才能刷新。

















