Go-Gin中优雅的实现参数校验,自定义错误消息提示

问题描述

在参数校验的时候我们一般会基于"github.com/go-playground/validator/v10"这个库给结构体加标签实现校验参数,当参数校验错误的时候,他的提示一般是英文的,怎么自定义参数错误提示呢?跟着我一步步来

注册校验器

package initialize

import (
	"ToDoList/global"
	"ToDoList/model/request"
	"ToDoList/util/validate"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/locales/zh"
	ut "github.com/go-playground/universal-translator"
	v10 "github.com/go-playground/validator/v10"
	zhTranslations "github.com/go-playground/validator/v10/translations/zh"
)

func RegisterValidator[T interface{}](fn v10.StructLevelFunc, model T) {
	translator := zh.New()
	universalTransator := ut.New(translator, translator)
	global.GVA_TRANS, _ = universalTransator.GetTranslator("zh")

	if v, ok := binding.Validator.Engine().(*v10.Validate); ok {
		v.RegisterStructValidation(fn, model)
		_ = zhTranslations.RegisterDefaultTranslations(v, global.GVA_TRANS)
	}

}

type _Validator struct{}

func (receiver _Validator) InitValidator() {
	RegisterValidator[request.Login](validate.UserLoginValidate.Validation, request.Login{})
}

var Validator = new(_Validator)


func (receiver *ginEngine) InitEngine() *gin.Engine {
	r := gin.Default()
	// 这里进行注册校验器!!
	Validator.InitValidator()
	return r
}

定义接口

我们定义一个接口规范,利于代码维护

package validate

import "github.com/go-playground/validator/v10"

type Validation interface {
	CreateValidate() validator.StructLevelFunc
}

定义具体参数校验的方法

ReportError函数第四个参数Param将作为错误时消息提示。

package validate

import (
	"ToDoList/model/request"
	"github.com/go-playground/validator/v10"
	"reflect"
)

type userLoginValidate struct{}

func (receiver userLoginValidate) CreateValidate() validator.StructLevelFunc {
	return func(sl validator.StructLevel) {
		user := sl.Current().Interface().(request.Login)
		StructNotEmpty(sl, user, []string{})
		if !VerifyPassword(8, 16, user.Password) {
			sl.ReportError(reflect.ValueOf(user.Password), "", "", "", "密码要出现数字、大写字母、小写字母、特殊字符(.@$!%*#_~?&^),至少包含其中2种且长度在8-16之间")
		}
	}
}

var UserLoginValidate = new(userLoginValidate)


定义公共函数

1.定义一个翻译的函数

func CustomErrMessage(err error) string {
	var result string
	errors := err.(validator.ValidationErrors)
	for _, err := range errors {
		if err.Param() != "" {
			result += err.Param() + ","
		}
	}
	return result
}

2.判断结构体字段是否为空

// 结构体字段判断是否为空
func StructNotEmpty(sl validator.StructLevel, st interface{}, omitKeys []string) {
	t := reflect.TypeOf(st)
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	if t.Kind() != reflect.Struct {
		fmt.Println("必须传入结构体才能判断是否字段为空")
		return
	}
	v := reflect.ValueOf(st) //获取reflect.Type类
	for i := 0; i < v.NumField(); i++ {
		if !slices.Contains(omitKeys, t.Field(i).Name) {
			label := t.Field(i).Tag.Get("label")
			value := v.Field(i).Interface().(string)
			if len(value) == 0 {
				sl.ReportError(t.Field(i), "", "", "", fmt.Sprintf("%s不能为空", label))
			}
		}
	}
}

结构体标签

label标签是结构体字段的中文翻译,以便于当结构体字段为空时StructNotEmpty函数可以进行翻译。

type Login struct {
	Username  string `json:"username" binding:"required,min=4,max=16" label:"用户名"` // 用户名
	Password  string `json:"password" binding:"required,min=8,max=16" label:"密码"`  // 密码
	Captcha   string `json:"captcha" binding:"required" label:"验证码"`               // 验证码
	CaptchaId string `json:"captchaId" binding:"required" label:"验证码ID"`           // 验证码ID
}

返回结果

func (receiver *userApi) Login(c *gin.Context) {
	// 1.绑定用户表单数据
	var loginForm request.Login
	// 2.检验用户登录表单
	if err := c.ShouldBindJSON(&loginForm); err != nil {
		response.Response.FailWithMessage(validate.CustomErrMessage(err), c)
		return
	}
}

结果展示

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结

  1. 先在Gin中注册验证器
  2. 定义校验函数并通过sl validator.StructLevel调用sl.ReportError传入param参数作为自定义错误
  3. 然后绑定结构体 var loginForm request.Login if err := c.ShouldBindJSON(&loginForm);
  4. 当绑定失败的时候把err传给CustomErrMessage函数CustomErrMessage函数,将会把param的值作为错误结果
  5. 然后将CustomErrMessage函数返回结果给客户端即可

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/511243.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

备战蓝桥杯---贪心刷题1

话不多说&#xff0c;直接看题&#xff1a; 本质是一个数学题&#xff1a; 我们令xi<0表示反方向传递&#xff0c;易得我们就是求每一个xi的绝对值之和min,我们令平均值为a爸。 易得约束条件&#xff1a; x1-x2a1-a,x2-x3a2-a..... 解得x1x1-0,x2x1-((n-1)*a-a2-...an)。…

硬件了解 笔记

motherboard的高低端区别在哪里&#xff1f; 核心&#xff1a;从单核变成双核&#xff0c;多核&#xff08;几核就是几个打工人&#xff09; 多线程&#xff1a;6核本来对应6个线程&#xff0c;但是多线程就是说6核对应12个线程 频率 主频&#xff1a;平时打工的速度 睿频&…

达梦数据库 优化

谁进行优化&#xff1f;优化什么&#xff1f; 优化不能仅从数据库方面考虑&#xff0c;比如&#xff0c;在存储达到数据库极限、应用涉及人员设计的代码稀巴烂的情况下&#xff0c;进行调优就是杯水车薪的效果。 涉及到优化人员&#xff1a; 数据库管理员应用程序架构师应用…

Javascript/Node.JS中如何用多种方式避免属性为空(cannot read property of undefined ERROR)

>>>>>>问题 "cannot read property of undefined" 是一个常见的 JavaScript 错误&#xff0c;包含我在内很多人都会遇到&#xff0c;表示你试图访问一个未定义&#xff08;undefined&#xff09;对象的属性。这通常是因为你在访问一个不存在的对象…

制造型企业实施WMS仓储管理系统前后的变化

在科技浪潮的推动下&#xff0c;WMS仓储管理系统逐渐崭露头角&#xff0c;成为制造企业优化运营、提升竞争力的得力助手。本文将从制造企业实施WMS仓储管理系统前后的对比入手&#xff0c;探讨这一变革所带来的深远影响。 一、WMS仓储管理系统实施前的仓储管理挑战 在WMS仓储管…

【LeetCode: 96. 不同的二叉搜索树 + 动态规划】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

从0到1完成UI自动化测试框架搭建之Pytest

上篇文章中&#xff0c;我们学会了如何使用UI Automator2atx编写简单的Android自动化脚本。 但是有个问题&#xff0c;大家可以思考下&#xff0c;光用自动化脚本让它自己动起来&#xff0c;是不是缺了点什么&#xff1f; 我们写测试用例的时候&#xff0c;是不是经常写&…

redis 的StringRedisTemplate

6.3 StringRedisTemplate 尽管JSON的序列化方式可以满足我们的需求&#xff0c;但依然存在一些问题&#xff0c;如图&#xff1a; 为了在反序列化时知道对象的类型&#xff0c;JSON序列化器会将类的class类型写入json结果中&#xff0c;存入Redis&#xff0c;会带来额外的内存…

透彻理解二分查找-算法通关村

透彻理解二分查找-算法通关村 常见的查找算法有顺序查找、二分查找、插值查找&#xff0c;树表查找、分块查找、哈希查找等等。其实二分查找、插值查找以及斐波那契查找都可以归为一类—插值查找。插值查找是在二分查找的基础上的优化查找算法。 二分查找的价值&#xff0c;请…

大数据分析与内存计算——Spark安装以及Hadoop操作——注意事项

一、Spark安装 1.相关链接 https://dblab.xmu.edu.cn/blog/4322/ 2.安装Spark&#xff08;Local模式&#xff09; 按照文章中的步骤安装即可 遇到问题&#xff1a;xshell以及xftp不能使用 解决办法&#xff1a; 在linux使用镜像网站进行下载&#xff1a;wget https://mi…

Three.js真实相机模拟

有没有想过如何在 3D Web 应用程序中模拟物理相机&#xff1f; 在这篇博文中&#xff0c;我将向你展示如何使用 Three.js和 OpenCV 来完成此操作。 我们将从模拟针孔相机模型开始&#xff0c;然后添加真实的镜头畸变。 具体来说&#xff0c;我们将仔细研究 OpenCV 的两个失真模…

【Java 集合进阶】单练集合顶层接口collction迭代器

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏 …

适合初学者的Linux的综合项目

大家好&#xff0c;今天给大家介绍适合初学者的Linux的综合项目&#xff0c;文章末尾附有分享大家一个资料包&#xff0c;差不多150多G。里面学习内容、面经、项目都比较新也比较全&#xff01;可进群免费领取。 对于初学者来说&#xff0c;Linux的综合项目应当既具有教育意义又…

element plus 输入框样式模仿Material-UI

获取焦点状态 自定义指令 app.directive(focus, { // 当被绑定的元素插入到 DOM 中时…… mounted(el) { const descendants el.querySelectorAll(.el-input__inner); var cssClass newLable;el.classList.add(cssClass); // 遍历并操作这些子孙节点 descendants.forE…

(24年4月2日更新)Linux安装chrome及chromedriver(Ubuntu20.0416.04)

一、安装Chrome 1&#xff09;先执行命令下载chrome&#xff1a; wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb2&#xff09;安装chrome sudo dpkg -i google-chrome-stable_current_amd64.deb踩坑&#xff1a;这里会提示如下报错&…

安卓主板MT8390(Genio 700)_MTK联发科Linux开发板方案

MediaTek Genio 700 &#xff08;MT8390&#xff09;是一款高性能的边缘 AI 物联网平台&#xff0c;专为智能家居、互动零售、工业与商业应用而设计。提供快速响应的边缘计算能力、先进的多媒体功能、广泛的传感器和连接方式&#xff0c;且支持多任务操作系统。 MT8390安卓核心…

ArrayList扩容原理

ArrayList源码分析 分析ArrayList源码主要从三个方面去翻阅&#xff1a;成员变量&#xff0c;构造函数&#xff0c;关键方法 以下源码都来源于jdk1.8 1 成员变量 DEFAULT_CAPACITY 10; 默认初始的容量**(CAPACITY) EMPTY_ELEMENTDATA {}; 用于空实例的共享空数组实例 DEFAU…

Java项目:85 springboot智能物流管理系统

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 本美发门店管理系统有管理员和用户两个角色。 用户功能有项目预定管理&#xff0c;产品购买管理&#xff0c;会员充值管理&#xff0c;余额查询管理。…

文本自动粘贴编辑器:支持自动粘贴并筛选手机号码,让信息处理更轻松

在信息时代的浪潮中&#xff0c;文本处理已成为我们日常工作与生活的重要组成部分。无论是商务沟通、社交互动还是个人事务处理&#xff0c;手机号码的筛选与粘贴都显得尤为关键。然而&#xff0c;传统的文本处理方式效率低下、易出错&#xff0c;已无法满足现代人的高效需求。…

Linux(05) Debian 系统修改主机名

查看主机名 方法1&#xff1a;hostname hostname 方法2&#xff1a;cat etc/hostname cat /etc/hostname 如果在创建Linux系统的时候忘记修改主机名&#xff0c;可以采用以下的方式来修改主机名称。 修改主机名 注意&#xff0c;在linux中下划线“_”可能是无效的字符&…