gin框架使用系列之五——表单校验

news2025/1/18 17:10:28
  • 系列目录
    • 《gin框架使用系列之一——快速启动和url分组》
    • 《gin框架使用系列之二——uri占位符和占位符变量的获取》
    • 《gin框架使用系列之三——获取表单数据》
    • 《gin框架使用系列之四——json和protobuf的渲染》

一 、表单验证的基本理论

  在第三篇中,我们介绍了如何将form表单和json等数据转成结构体对象中的方法,当时在绑定的结构体中,其tag中就有“binding”的信息,这就是gin中表单验证的基础。为了详细了解表单验证,我们进一步了解以下表单绑定的知识。

1.1、若要将请求体绑定到结构体中,需要使用模型绑定,支持JSON、XML、YAML和标准表单的绑定,设置时需要在绑定的字段上设置tag,其只要有两套绑定方法

  • Must bind

    • 方法: Bind 、BindJSON、BindXML、BindQuery、BindYAML
    • 行为:这些方法底层使用MustBindWith方法,如果存在绑定错误,请求将被终止,响应代码会被设置成400
  • Should bind

    • 方法: ShouldBind、ShouldBindJSON、ShouldBindXML、ShouldBindQuery、ShouldBindYAML
    • 行为:底层使用ShouldBindWith方法,如果存在绑定错误,则返回go语言的错误形式,开发人员可以处理错误,请求不会被终止

1.2、Gin中使用 go-playground/validator来验证表单,详细文档

二、表单验证示例

我们以一个注册的接口的表单验证为例,示例表单验证的写法如下:

type SignUpParam struct {
	//  1<= age <= 130
	Age uint8 `json:"age" binding:"gte=1,lte=130"`
	// name,必须
	Name string `json:"name" binding:"required"`
	// email,必须且满足email格式
	Email string `json:"email" binding:"required,email"`
	// password,必须
	Password string `json:"password" binding:"required"`
	// re_password,必须,且要和password字段相同
	RePassword string `json:"re_password" binding:"required,eqfield=Password"`
}

func main() {
	router := gin.Default()
	router.POST("/sign-up", func(c *gin.Context) {
		var param SignUpParam
		if err := c.ShouldBind(&param); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{
				"msg": err.Error(),
			})
			return
		}
		// TODO 业务逻辑
		c.JSON(http.StatusOK, "success")
	})

	router.Run() // listen and serve on 0.0.0.0:8080
}

在postman中调用接口,报错信息如下:
在这里插入图片描述

三、表单验证信息国际化

上面示例中我们可以看到表单验证信息的报错中,显示的不是很详细,而且暴露了go后台代码的数据,我们可以为其添加国际化信息。
下面是增加翻译器的方法


func InitTrans(locale string) (err error) {
	//修改gin框架中的validator引擎属性, 实现定制
	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		//注册一个获取json的tag的自定义方法
		v.RegisterTagNameFunc(func(fld reflect.StructField) string {
			name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
			if name == "-" {
				return ""
			}
			return name
		})

		zhT := zh.New() //中文翻译器
		enT := en.New() //英文翻译器
		//第一个参数是备用的语言环境,后面的参数是应该支持的语言环境
		uni := ut.New(enT, zhT, enT)
		trans, ok = uni.GetTranslator(locale)
		if !ok {
			return fmt.Errorf("uni.GetTranslator(%s)", locale)
		}

		switch locale {
		case "en":
			en_translations.RegisterDefaultTranslations(v, trans)
		case "zh":
			zh_translations.RegisterDefaultTranslations(v, trans)
		default:
			en_translations.RegisterDefaultTranslations(v, trans)
		}
		return
	}

	return
}

整体代码如下:

package main

import (
	"fmt"
	"net/http"
	"reflect"
	"strings"
	
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/locales/en"
	"github.com/go-playground/locales/zh"
	ut "github.com/go-playground/universal-translator"
	"github.com/go-playground/validator/v10"
	en_translations "github.com/go-playground/validator/v10/translations/en"
	zh_translations "github.com/go-playground/validator/v10/translations/zh"
)

type SignUpParam struct {
	//  1<= age <= 130
	Age uint8 `json:"age" binding:"gte=1,lte=130"`
	// name,必须
	Name string `json:"name" binding:"required"`
	// email,必须且满足email格式
	Email string `json:"email" binding:"required,email"`
	// password,必须
	Password string `json:"password" binding:"required"`
	// re_password,必须,且要和password字段相同
	RePassword string `json:"re_password" binding:"required,eqfield=Password"`
}

// 定义一个全局的翻译器
var trans ut.Translator

func main() {

	//代码侵入性很强 中间件
	if err := InitTrans("zh"); err != nil {
		fmt.Println("初始化翻译器错误")
		return
	}

	router := gin.Default()
	router.POST("/sign-up", func(c *gin.Context) {
		var param SignUpParam
		if err := c.Bind(&param); err != nil {
			errs, ok := err.(validator.ValidationErrors)
			if !ok {
				c.JSON(http.StatusBadRequest, gin.H{
					"msg": err.Error(),
				})
				return
			}
			// 检查使用自定义的转换器
			c.JSON(http.StatusBadRequest, gin.H{
				"msg": errs.Translate(trans),
			})
			return
		}
		// TODO 业务逻辑
		c.JSON(http.StatusOK, "success")
	})

	router.Run() // listen and serve on 0.0.0.0:8080
}
func InitTrans(locale string) (err error) {
	//修改gin框架中的validator引擎属性, 实现定制
	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
		//注册一个获取json的tag的自定义方法
		v.RegisterTagNameFunc(func(fld reflect.StructField) string {
			name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0]
			if name == "-" {
				return ""
			}
			return name
		})

		zhT := zh.New() //中文翻译器
		enT := en.New() //英文翻译器
		//第一个参数是备用的语言环境,后面的参数是应该支持的语言环境
		uni := ut.New(enT, zhT, enT)
		trans, ok = uni.GetTranslator(locale)
		if !ok {
			return fmt.Errorf("uni.GetTranslator(%s)", locale)
		}

		switch locale {
		case "en":
			en_translations.RegisterDefaultTranslations(v, trans)
		case "zh":
			zh_translations.RegisterDefaultTranslations(v, trans)
		default:
			en_translations.RegisterDefaultTranslations(v, trans)
		}
		return
	}

	return
}


用postman输入错误信息如下:
在这里插入图片描述

输入全部正确信息如下:

在这里插入图片描述


后记
  个人总结,欢迎转载、评论、批评指正

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

3DV 2024 Oral | SlimmeRF:可动态压缩辐射场,实现模型大小和建模精度的灵活权衡

目前大多数NeRF模型要么通过使用大型模型来实现高精度&#xff0c;要么通过牺牲精度来节省内存资源。这使得任何单一模型的适用范围受到局限&#xff0c;因为高精度模型可能无法适应低内存设备&#xff0c;而内存高效模型可能无法满足高质量要求。为此&#xff0c;本文研究者提…

【奇技淫巧】OpenCV中Blob分析的两个关键技巧

Blob分析 BLOB是图像中灰度块的一种专业称呼&#xff0c;更加变通一点的可以说它跟我们前面二值图像分析的联通组件类似&#xff0c;通过特征提取实现常见的各种灰度BLOB对象组件检测与分离。使用该检测器的时候&#xff0c;可以根据需要输入不同参数&#xff0c;得到的结果跟…

微信聊天记录会被监控吗?

微信聊天记录是否会被监控是一个备受关注的话题。随着微信的普及和人们对隐私保护意识的提高&#xff0c;越来越多的人开始关注自己的微信聊天记录是否会被他人监控。 首先&#xff0c;我们先正面回答一下这个问题&#xff1a; 微信聊天记录是会被监控的&#xff01;但前提是你…

南京观海微电子--GIP Introduction

What is GIP&#xff1f; GIP&#xff08;Gate in panel&#xff09;&#xff1a;将IC Gate电路中的shift register circuit 移至 panel端。 提供控制信号给panel即可达到gate function Why use GIP&#xff1f; GIP技术就是把扫描芯片集成在显示面板上&#xff0c;达到节省…

关于“Python”的核心知识点整理大全45

目录 15.4.6 绘制直方图 die_visual.py 注意 15.4.7 同时掷两个骰子 dice_visual.py 15.4.8 同时掷两个面数不同的骰子 different_dice.py 15.5 小结 第 16 章 16.1 CSV 文件格式 16.1.1 分析 CSV 文件头 highs_lows.py 注意 16.1.2 打印文件头及其位置 highs_l…

k8s 网络

还没有部署网络。 k8s的网络类型&#xff1a; k8s中的通信模式&#xff1a; 1&#xff0c;pod内部之间容器和容器之间的通信。 在同一个pod中的容器共享资源和网络&#xff0c;使用同一个网络命名空间。可以直接通信的。 2&#xff0c;同一个node节点之内不同pod之间的通信。…

docker 搭建gitlab 恢复和备份

最近一直在折腾gitlab 代码管理系统 采用docker搭建 镜像网址 https://hub.docker.com/ 技术交流 http://idea.coderyj.com/ 1.因为我要恢复的版本是12.0.9的所有我就下载了docker-ce的12.0.9的镜像 1.下载镜像 docker pull gitlab/gitlab-ce:12.0.9-ce.02.安装 docker run …

VUE2入门教程

VUE2 1.vue环境搭建 1.1.什么是vue ​ vue是一套用于构建用户界面的渐进式JavaScript框架。中文官网&#xff1a;https://cn.vuejs.org/ 渐进式&#xff1a;vue可以自底向上逐层的应用。&#xff08;引入轻量的核心库逐渐递进到各种各样的复杂库&#xff09; ​ vue由当时…

uniapp发送formdata表单请求(全网最简单方法)

因为uniapp不支持直接传输formdata&#xff0c;只提供了uploadFile方法上传文件&#xff0c;但是利用该方法就可以传输formdata了。 第一种&#xff1a; 可以直接使用filePath属性进行空文件传输&#xff0c;只需要设置filePath为空就行了。 function $uploadForm(url, data…

Python(九十二)函数的参数定义-个数可变的位置参数和个数可变的关键字形参

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

骨传导耳机和开放式耳机的区别是什么?使用开放式耳机会损伤听力吗?

其实很多人对开放式耳机不了解&#xff0c;开放式耳机是指不用塞入耳道&#xff0c;开放双耳佩戴的耳机&#xff0c;而骨传导耳机也算开放式耳机的一种&#xff0c;在开放式耳机中&#xff0c;分为骨传导耳机和气传导耳机&#xff0c;想要知道开放式耳机是否会损伤听力&#xf…

《数据库开发实践》之存储过程【知识点罗列+例题演练】

一、什么是存储过程&#xff1f; 1.概念理解&#xff1a; 存储过程是一组为了完成特定功能的SQL语句集。通过组成SQL语句和控制语句&#xff0c;提供一种封装任务的方法。因此在创建编译好某个存储过程后&#xff0c;因为存储过程中有可执行操作的sql语句&#xff0c;用户可以…

会议室占用时间段 - 华为OD统一考试

OD统一考试 题解: Java / Python / C++ 题目描述 现有若干个会议,所有会议共享一个会议室,用数组表示各个会议的开始时间和结束时间, 格式为: [[会议1开始时间,会议1结束时间],[会议2开始时间,会议2结束时间]] 请计算会议室占用时间段。 输入描述 [[会议1开始时间,…

Deep Learning(wu--108-118)CNN详解(一)

文章目录 4 卷积神经网络计算机视觉CV边缘检测示例CNNPaddingStride三维卷积参数量计算单层卷积网络小结池化为什么使用卷积 4 卷积神经网络 计算机视觉CV 边缘检测示例 CNN Padding Stride 三维卷积 参数量计算 单层卷积网络小结 池化 为什么使用卷积 参数共享和稀疏计算

传感器基础:传感器使用与编程使用(三)

目录 常用传感器讲解九--雨滴传感器具体讲解电路连接代码实现 常用传感器讲解十--光传感器根据亮度安排灯具体讲解电路连接代码实现 常用传感器讲解七--light cup&#xff08;KY-008&#xff09;具体讲解电路连接代码实现 常用传感器讲解十二--倾斜开关传感器&#xff08;KY-02…

Qt学习:Qt的意义安装Qt

Qt 的简介 QT 是一个跨平台的 C图形用户界面应用程序框架。它为程序开发者提供图形界面所需的所有功能。它是完全面向对象的&#xff0c;很容易扩展&#xff0c;并且允许真正地组件编程。 支持平台 xP 、 Vista、Win7、win8、win2008、win10Windows . Unix/Linux: Ubuntu 等…

ERROR: No matching distribution found for torch==1.12.0+cu113

原因 pip install torch1.12.0cu113用pip安装torch时&#xff0c;出现&#xff1a; ERROR: No matching distribution found for torch1.12.0cu113好像不少用清华源的会出现这个问题 解决办法 pytorch官网&#xff1a;https://pytorch.org/get-started/previous-versions/ …

Flink1.17实战教程(第七篇:Flink SQL)

系列文章目录 Flink1.17实战教程&#xff08;第一篇&#xff1a;概念、部署、架构&#xff09; Flink1.17实战教程&#xff08;第二篇&#xff1a;DataStream API&#xff09; Flink1.17实战教程&#xff08;第三篇&#xff1a;时间和窗口&#xff09; Flink1.17实战教程&…

网络编程『简易TCP网络程序』

&#x1f52d;个人主页&#xff1a; 北 海 &#x1f6dc;所属专栏&#xff1a; Linux学习之旅、神奇的网络世界 &#x1f4bb;操作环境&#xff1a; CentOS 7.6 阿里云远程服务器 文章目录 &#x1f324;️前言&#x1f326;️正文TCP网络程序1.字符串回响1.1.核心功能1.2.程序…

数据结构学习 jz13衣橱整理

关键词&#xff1a;搜索算法 dfs bfs 回溯 题目&#xff1a; 各数位之和&#xff1a; 求法代码&#xff1a; int sums(int x){int s0;while(x!0){sx%10;xx/10;}return s;} 总的思路&#xff1a; 这道题是求可以到达的格子数&#xff0c;想到可以用搜索算法来做&#xff0c;…