返回顶部
首页 > 资讯 > 精选 >Go高效率开发Web参数校验的方式有哪些
  • 595
分享到

Go高效率开发Web参数校验的方式有哪些

2023-07-04 15:07:13 595人浏览 薄情痞子
摘要

本篇内容介绍了“Go高效率开发WEB参数校验的方式有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!web开发中,你肯定见到过各种各样的表

本篇内容介绍了“Go高效率开发WEB参数校验的方式有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

web开发中,你肯定见到过各种各样的表单或接口数据校验:

  • 客户端参数校验:在数据提交到服务器之前,发生在浏览器端或者app应用端,相比服务器端校验,用户体验更好,能实时反馈用户的输入校验结果。

  • 服务器端参数校验:发生在客户端提交数据并被服务器端程序接收之后,通常服务器端校验都是发生在将数据写入数据库之前,如果数据没通过校验,则会直接从服务器端返回错误消息,并且告诉客户端发生错误的具体位置和原因,服务器端校验不像客户端校验那样有好的用户体验,因为它直到整个表单都提交后才能返回错误信息。但是服务器端校验是应用对抗错误,恶意数据的最后防线,在这之后,数据将被持久化至数据库。当今所有的服务端框架都提供了数据校验与过滤功能(让数据更安全)。

本文主要讨论服务器端参数校验

确保用户以正确格式输入数据,提交的数据能使后端应用程序正常工作,同时在一切用户的输入都是不可信的前提下(比如xss跨域脚本攻击,sql注入),参数验证是不可或缺的一环,也是很繁琐效率不高的一环,在对接表单提交或者api接口数据提交,程序里充斥着大量重复验证逻辑和if else语句,本文分析参数校验的三种方式,找出最优解,从而提高参数验证程序代码的开发效率。

需求场景:

常见的网站登陆场景

业务需求

接口一:场景:输入手机号,获取短信验证码校验需求:判断手机号非空,手机号格式是否正确接口二:场景:手机收到短信验证码,输入验证码,点击登陆校验需求:1、判断手机号非空,手机号格式是否正确;2、验证码非空,验证码格式是否正确

技术选型:web框架gin

第一种实现方式:自定义实现校验逻辑

package mainfunc main() {   engine := gin.New()    engine := gin.New()    ctrUser := controller.NewUser()    engine.POST("/user/login", ctrUser.Login)    ctrCaptcha := controller.NewCaptcha()    engine.POST("/captcha/send", ctrCaptcha.Send)    engine.Run()}--------------------------------------------------------------------------------package controllertype Captcha struct {}func (ctr *Captcha) Send(c *gin.Context) {   mobile := c.PostFORM("mobile")   // 校验手机号逻辑   if mobile == "" {      c.JSON(Http.StatusBadRequest, gin.H{"error": "手机号不能为空"})      return   }   matched, _ := regexp.MatchString(`^(1[3-9][0-9]\d{8})$`, mobile)   if !matched {      c.jsON(http.StatusBadRequest, gin.H{"error": "手机号格式不正确"})      return   }    c.JSON(http.StatusBadRequest, gin.H{"mobile": mobile})}type User struct {}func (ctr *User) Login(c *gin.Context) {   mobile := c.PostForm("mobile")   code := c.PostForm("code")   // 校验手机号逻辑   if mobile == "" {      c.JSON(http.StatusBadRequest, gin.H{"error": "手机号不能为空"})      return   }   matched, _ := regexp.MatchString(`^(1[3-9][0-9]\d{8})$`, mobile)   if !matched {      c.JSON(http.StatusBadRequest, gin.H{"error": "手机号格式不正确"})      return   }   // 校验手机号逻辑   if code == "" {      c.JSON(http.StatusBadRequest, gin.H{"error": "验证码不能为空"})      return   }   if len(code) != 4 {      c.JSON(http.StatusBadRequest, gin.H{"error": "验证码为4位"})      return   }   c.JSON(http.StatusBadRequest, gin.H{"mobile": mobile, "code": code})}

源码链接

代码分析:
参数验证函数放在Controller层;
这是一种比较初级也是最朴素的实现方式,在现实代码review中经常遇到,这样实现会有什么问题?
1、手机号码验证逻辑重复;
2、违背了controller层的职责,controller层充斥着大量的验证函数(Controller层职责:从HTTP请求中获得信息,提取参数,并分发给不同的处理服务);

重复代码是软件质量下降的重大来源!!!

重复代码会造成维护成本的成倍增加;
2、需求的变动导致需要修改重复代码,如果遗漏某处重复的逻辑,就会产生bug(例如手机号码增加12开头的验证规则);
3、重复代码会导致项目代码体积变得臃肿;

聪明的开发者肯定第一时间想到一个解决办法:提取出验证逻辑,工具包util实现IsMobile函数

package utilfunc IsMobile(mobile string) bool {   matched, _ := regexp.MatchString(`^(1[3-9][0-9]\d{8})$`, mobile)   return matched}代码分析:问题:代码会大量出现util.IsMobile、util.IsEmail等校验代码

思考:从面向对象的思想出发,IsMobile属于util的动作或行为吗?

第二种实现方式:模型绑定校验

技术选型:web框架gin自带的模型验证器中文提示不是很好用,这里使用govalidator 模型绑定校验是目前参数校验最主流的验证方式,每个编程语言的web框架基本都支持这种模式,模型绑定时将Http请求中的数据映射到模型对应的参数,参数可以是简单类型,如整形,字符串等,也可以是复杂类型,如Json,Json数组,对各种数据类型进行验证,然后抛出相应的错误信息。

源码链接

package requestfunc init() {   validator.TagMap["IsMobile"] = func(value string) bool {      return IsMobile(value)   }}func IsMobile(value string) bool {    matched, _ := regexp.MatchString(`^(1[1-9][0-9]\d{8})$`, value)    return matched}type Captcha struct {   Mobile string `form:"mobile" valid:"required~手机号不能为空,numeric~手机号码应该为数字型,IsMobile~手机号码格式错误"`}type User struct {   Mobile string `form:"mobile" valid:"required~手机号不能为空,numeric~手机号码应该为数字型,IsMobile~手机号码格式错误"`   Code string `form:"code" valid:"required~验证码不能为空,numeric~验证码应该为数字型"`}-------------------------------------------------------------------------------package controllertype Captcha struct {}func (ctr *Captcha) Send(c *gin.Context) {   request := new(request.Captcha)   if err := c.ShouldBind(request); err != nil {      c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})      return   }   if _, err := validator.ValidateStruct(request); err != nil {      c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})      return   }   c.JSON(http.StatusBadRequest, gin.H{"data": request})}type User struct {}func (ctr *User) Login(c *gin.Context) {   request := new(request.User)   if err := c.ShouldBind(request); err != nil {      c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})      return   }   if _, err := validator.ValidateStruct(request); err != nil {      c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})      return   }   c.JSON(http.StatusBadRequest, gin.H{"data": request})}

代码分析:
1、mobile校验逻辑同样重复(注释实现校验的逻辑重复,如错误提示"手机号不能为空"修改为"请填写手机号",需要修改两个地方)
2、validator.ValidateStruct函数会验证结构体所有属性

对于2问题不太好理解,举例解释业务场景:用户注册功能,需要校验手机号、短信验证码、密码、昵称、生日type User struct {   Mobile string `form:"mobile" valid:"required~手机号不能为空,numeric~手机号码应该为数字型,IsMobile~手机号码格式错误"`   Code string `form:"code" valid:"required~验证码不能为空,numeric~验证码应该为数字型"`   PassWord string `form:"password" valid:"required~密码不能为空,stringlength(6|18)~密码6-18个字符"`   Nickname string `form:"nickname" valid:"required~昵称不能为空,stringlength(2|10)~昵称2-10个字符"`   Birthday time.Time `form:"birthday" valid:"required~生日不能为空" time_format:"2006-01-02"`}

代码分析:
登陆功能需要校验Mobile、Code属性;
注册功能需要校验Mobile、Code、Password、Nickname、Birthday属性;

如果代码校验共用User结构体,就产生了一个矛盾点,有两种方法可以解决这一问题:

  • 修改validator.ValidateStruct函数,增加校验白名单或黑名单,实现可以设置部分属性校验或者忽略校验部分属性;

// 只做Mobile、Code属性校验或者忽略Mobile、Code属性校验validator.ValidateStruct(user, "Mobile", "Code") 这种也是一种不错的解决方式,但是在项目实践中会遇到点小问题:1、一个校验结构体有20个属性,只需要校验其中10个字段,不管用白名单还是黑名单都需要传10个字段;2、手写字段名容易出错;
  • 新建不同的结构体,对应相应的接口绑定校验

type UserLogin struct {   Mobile string `form:"mobile" valid:"required~手机号不能为空,numeric~手机号码应该为数字型,IsMobile~手机号码格式错误"`   Code string `form:"code" valid:"required~验证码不能为空,numeric~验证码应该为数字型"`}type UserReGISter struct {   Mobile string `form:"mobile" valid:"required~手机号不能为空,numeric~手机号码应该为数字型,IsMobile~手机号码格式错误"`   Code string `form:"code" valid:"required~验证码不能为空,numeric~验证码应该为数字型"`   Password string `form:"password" valid:"required~密码不能为空,stringlength(6|18)~密码6-18个字符"`   Nickname string `form:"nickname" valid:"required~昵称不能为空,stringlength(2|10)~昵称2-10个字符"`   Birthday time.Time `form:"birthday" valid:"required~生日不能为空" time_format:"2006-01-02"`}代码解析:用户登陆接口对应:UserLogin结构体用户注册接口对应:UserRegister结构体

同样问题再次出现,Mobile、Code属性校验逻辑重复。

再介绍第三种参数校验方式之前,先审视一下刚才的一段代码:

if err := c.ShouldBind(&request); err != nil {  c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})  return}if _, err := validator.ValidateStruct(request); err != nil {  c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})  return}

参数绑定校验的地方都需要出现这几行代码,我们可以修改gin源码,把govalidator库集成在gin中;
如何修改第三方库源代码参照项目 源码链接

在gin根目录增加context_validator.go文件,代码如下:package ginimport (   "GitHub.com/asaskevich/govalidator")type Validator interface {   Validate() error}func (c *Context) ShouldB(data interface{}) error {   if err := c.ShouldBind(data); err != nil {      return err   }   if _, err := govalidator.ValidateStruct(data); err != nil {      return err   }   var v Validator   var ok bool   if v, ok = data.(Validator); !ok {      return nil   }   return v.Validate()}

controller层的参数绑定校验代码如下:

type User struct {}func (ctr *User) Register(c *gin.Context) {   request := new(request.UserRegister)   if err := c.ShouldB(request); err != nil {      c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})      return   }   c.JSON(http.StatusBadRequest, gin.H{"data": request})}

代码分析:
增加了Validator接口,校验模型实现Validator接口,可以完成更为复杂的多参数联合校验检查逻辑,如检查密码和重复密码是否相等

type UserRegister struct {   Mobile string `form:"mobile" valid:"required~手机号不能为空,numeric~手机号码应该为数字型,IsMobile~手机号码格式错误"`   Code string `form:"code" valid:"required~验证码不能为空,numeric~验证码应该为数字型"`   Password string `form:"password" valid:"required~密码不能为空,stringlength(6|18)~密码6-18个字符"`   RePassword string `form:"rePassword" valid:"required~重复密码不能为空,stringlength(6|18)~重复密码6-18个字符"`   Nickname string `form:"nickname" valid:"required~昵称不能为空,stringlength(2|10)~昵称2-10个字符"`   Birthday time.Time `form:"birthday" valid:"required~生日不能为空" time_format:"2006-01-02"`}func (req *UserRegister) Validate() error {   if req.Password != req.RePassword {      return errors.New("两次密码不一致")   }   return nil}

模型校验是通过反射机制来实现,众所周知反射的效率都不高,现在gin框架集成govalidator,gin原有的校验功能就显得多余,小伙伴们可以从ShouldBind函数从下追,把自带的校验功能屏蔽,提高框架效率。

第三种实现方式:拆解模型字段,组合结构体

解决字段校验逻辑重复的最终方法就是拆解字段为独立结构体,通过多个字段结构体的不同组合为所需的校验结构体,代码如下:
源码链接

package captchatype CodeS struct {   Code string `form:"code" valid:"required~验证码不能为空,numeric~验证码应该为数字型"`}package usertype PasswordS struct {   Password string `form:"password" valid:"required~密码不能为空,stringlength(6|18)~密码6-18个字符"`}type RePasswordS struct {   RePassword string `form:"rePassword" valid:"required~重复密码不能为空,stringlength(6|18)~重复密码6-18个字符"`}type NicknameS struct {   Nickname string `form:"nickname" valid:"required~昵称不能为空,stringlength(2|10)~昵称2-10个字符"`}type BirthdayS struct {   Birthday time.Time `form:"birthday" valid:"required~生日不能为空" time_format:"2006-01-02"`}type UserLogin struct {   MobileS   captcha.CodeS}type UserRegister struct {   MobileS   captcha.CodeS   user.PasswordS   user.RePasswordS   user.NicknameS   user.BirthdayS}func (req *UserRegister) Validate() error {   if req.Password() != req.RePassword() {      return errors.New("两次密码不一致")   }   return nil}代码解析:为什么字段结构体都加了S?1、结构体包含匿名结构体不能调用匿名结构体同名属性,匿名结构体加S标识为结构体示例代码不能很好的展示项目结构,可以查看源代码

代码分析:

  • 独立的字段结构体通常以表名为包名定义范围,比如商品名称和分类名称字段名都为Name,但是所需定义的校验逻辑(字符长度等)很有可能不同;

  • 每一个接口建立对应的验证结构体:

接口user/login:    对应请求结构体UserLogin接口user/register: 对应请求结构体UserRegister接口captcha/send:  对应请求结构体CaptchaSend
  • 公用的字段结构体例如ID、Mobile建立单独的文件;

总结
一、验证逻辑封装在各自的实体中,由request层实体负责验证逻辑,验证逻辑不会散落在项目代码的各个地方,当验证逻辑改变时,找到对应的实体修改就可以了,这就是代码的高内聚;

二、通过不同实体的嵌套组合就可以实现多样的验证需求,使得代码的可重用性大大增强,这就是代码的低耦合

独立字段结构体组合成不同的校验结构体,这种方式在实际项目开发中有很大的灵活性,可以满足参数校验比较多变复杂的需求场景,小伙伴可以在项目开发中慢慢体会。

参数绑定校验在项目中遇到的几个问题

源码链接1、需要提交参数为json或json数组如何校验绑定?

type ColumnCreateArticle struct {   IDS   article.TitleS}type ColumnCreate struct {   column.TitleS   Article *ColumnCreateArticle `form:"article"`   Articles []ColumnCreateArticle `form:"articles"`}

严格遵循一个接口对应一个校验结构体

func (ctr *Column) Detail(c *gin.Context) {   request := new(request.IDS)   if err := c.ShouldB(request); err != nil {      c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})      return   }   c.JSON(http.StatusBadRequest, gin.H{"data": request})}

示例代码获取文章专栏详情的接口,参数为专栏id,因为只有一个id参数,如果刚开始图省事,没有建立对应独立的ColumnDetail校验结构体,后期接口增加参数(例如来源等),还是要改动这一块代码,增加代码的不确定性

布尔参数的三种状态

type ColumnDetail struct {   IDS   // 为真显示重点文章,为否显示非重点文章,为nil都显示   ArticleIsImportant *bool `form:"articleIsImportant"`}column?id=1&articleIsImportant=true    ArticleIsImportant为truecolumn?id=1&articleIsImportant=false   ArticleIsImportant为falsecolumn?id=1                            ArticleIsIm

“Go高效率开发Web参数校验的方式有哪些”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

--结束END--

本文标题: Go高效率开发Web参数校验的方式有哪些

本文链接: https://lsjlt.com/news/346391.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Go高效率开发Web参数校验的方式有哪些
    本篇内容介绍了“Go高效率开发Web参数校验的方式有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!web开发中,你肯定见到过各种各样的表...
    99+
    2023-07-04
  • SpringBoot中参数校验的方法有哪些
    这篇文章给大家分享的是有关SpringBoot中参数校验的方法有哪些的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一、前言在 Web 开发中经常需要对前端传过来的参数进行校验,例如格式校验、非空校验等,基本上每个...
    99+
    2023-06-15
  • 提高开发效率的Vue技巧有哪些
    本篇内容主要讲解“提高开发效率的Vue技巧有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“提高开发效率的Vue技巧有哪些”吧!组件(component)的使...
    99+
    2024-04-02
  • 提高开发效率Vue技巧有哪些
    这篇文章主要讲解了“提高开发效率Vue技巧有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“提高开发效率Vue技巧有哪些”吧!组件(component)的...
    99+
    2024-04-02
  • 提升web前端开发效率的工具有哪些
    本篇内容介绍了“提升web前端开发效率的工具有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1. En...
    99+
    2024-04-02
  • 提高开发效率的Vue3常用插件有哪些
    这篇文章主要介绍“提高开发效率的Vue3常用插件有哪些”,在日常操作中,相信很多人在提高开发效率的Vue3常用插件有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”提高开发效率的Vue3常用插件有哪些”的疑...
    99+
    2023-07-04
  • Go中数组传参的方式有哪些
    这篇文章主要介绍“Go中数组传参的方式有哪些”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Go中数组传参的方式有哪些”文章能帮助大家解决问题。初学Golang,数组传参问题就是把我整不会了,以前我们...
    99+
    2023-07-05
  • 提升Web开发人员效率的工具网站有哪些
    本篇内容介绍了“提升Web开发人员效率的工具网站有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1.C...
    99+
    2024-04-02
  • 前端开发效率提高的代码规范有哪些
    这篇文章主要讲解了“前端开发效率提高的代码规范有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“前端开发效率提高的代码规范有哪些”吧!常见的命名规则1.大...
    99+
    2024-04-02
  • 提高开发效率和性能的Vue技巧有哪些
    本篇内容主要讲解“提高开发效率和性能的Vue技巧有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“提高开发效率和性能的Vue技巧有哪些”吧!1. 巧用$attrs和$listeners$att...
    99+
    2023-07-02
  • 借助 Django 框架,Go 开发者如何提高 Web 应用的开发效率?
    随着互联网的不断发展,Web 应用的开发需求也越来越高。为了提高开发效率,很多开发者选择使用框架。Django 是一款流行的 Python Web 框架,而 Go 语言则是近年来备受关注的高性能编程语言。那么,如何借助 Django 框架...
    99+
    2023-07-24
    开发技术 django 框架
  • 有哪些Web前端开发找错的方式
    这篇文章主要讲解了“有哪些Web前端开发找错的方式”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“有哪些Web前端开发找错的方式”吧!WEB开发主要是两个交互(B/S数据)浏览器:1包含htm...
    99+
    2023-06-08
  • 有哪些最佳实践可以提高ASP开发效率?
    ASP.NET是一种流行的Web开发框架,它提供了一种强大的平台来构建动态和互动的Web应用程序。然而,ASP.NET开发也可能会遇到一些挑战,例如代码复杂度高、调试困难等。本文将介绍一些最佳实践,以提高ASP.NET开发效率。 一、使用M...
    99+
    2023-06-27
    windows numpy leetcode
  • 高效开发Go语言项目的经验与方法
    在当今软件开发的浪潮中,Go语言作为一种强大而高效的编程语言,备受开发者的喜爱。Go语言不仅拥有简洁的语法和强大的并发能力,还具备快速编译、高效执行和丰富的标准库等优势。然而,要想在Go语言项目中实现高效开发,除了熟悉语言本身外,还需要掌握...
    99+
    2023-11-02
    Go语言 高效开发 经验与方法
  • 提升css开发效率的技巧有哪些
    今天小编给大家分享一下提升css开发效率的技巧有哪些的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解...
    99+
    2024-04-02
  • 高效率配置管理的方法有哪些
    这篇文章主要讲解了“高效率配置管理的方法有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“高效率配置管理的方法有哪些”吧!Spring-boot 基于数据...
    99+
    2024-04-02
  • Go语言二维数组的传参方式有哪些
    小编给大家分享一下Go语言二维数组的传参方式有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!go适合做什么go是golang的简称,而golang可以做服务器...
    99+
    2023-06-14
  • 10个提升Web开发效率的JavaScript库分别是哪些
    这期内容当中小编将会给大家带来有关10个提升Web开发效率的JavaScript库分别是哪些,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。JavaScript是最流行的编...
    99+
    2024-04-02
  • Vue 开发中的编码效率神器有哪些?
    ...
    99+
    2024-04-02
  • ASP开发技术面试:你知道哪些IDE有助于提高开发效率?
    在ASP开发中,一个高效的IDE是不可或缺的。一个好的IDE可以帮助开发人员更快、更准确地编写代码,提高开发效率。本文将介绍一些常用的ASP开发IDE,以及它们如何帮助提高开发效率。 Microsoft Visual Studio Mi...
    99+
    2023-08-10
    开发技术 面试 ide
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作