概述

上篇文章分享了 Gin 框架使用 Logrus 进行日志记录,这篇文章分享 Gin 框架的数据绑定与验证。

有读者咨询我一个问题,如何让框架的运行日志不输出控制台?

解决方案:

engine := gin.Default() //修改成如下
engine := gin.New()

我是怎么知道的?看框架代码。

Default()

func Default() *Engine {
debugPrintWARNINGDefault()
engine := New()
engine.Use(Logger(), Recovery())
return engine
}

New() 代码我就不贴了。

我们看到 Default() 使用了两个中间件 Logger(), Recovery(),如果不想使用,那就直接使用 New() 就可以了。

开始今天的文章。

比如,请求 v1/member/add 新增会员方法,nameage 为必填,同时 name 不能等于 admin 字符串,10 <= age <= 120。

直接看代码吧。

首先,先定义一个结构体。

entity/member.go

package entity

// 定义 Member 结构体
type Member struct {
Name string `form:"name" json:"name" binding:"required,NameValid"`
Age int `form:"age" json:"age" binding:"required,gt=10,lt=120"`
}

binding 中 required,这个是框架自带的,NameValid,这个是自己定义的。

问题一:框架自带的 binding 参数还有哪些?

问题二:自定义验证方法,怎么写?

接下来要说的就是问题二,写一个验证方法。

validator/member/member.go

package member

import (
"gopkg.in/go-playground/validator.v8"
"reflect"
) func NameValid(
v *validator.Validate, topStruct reflect.Value, currentStructOrField reflect.Value,
field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string,
) bool {
if s, ok := field.Interface().(string); ok {
if s == "admin" {
return false
}
}
return true
}

接下来,在路由中绑定:

router/router.go

package router

import (
"ginDemo/middleware/logger"
"ginDemo/middleware/sign"
"ginDemo/router/v1"
"ginDemo/router/v2"
"ginDemo/validator/member"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"gopkg.in/go-playground/validator.v8"
) func InitRouter(r *gin.Engine) { r.Use(logger.LoggerToFile()) // v1 版本
GroupV1 := r.Group("/v1")
{
GroupV1.Any("/product/add", v1.AddProduct)
GroupV1.Any("/member/add", v1.AddMember)
} // v2 版本
GroupV2 := r.Group("/v2").Use(sign.Sign())
{
GroupV2.Any("/product/add", v2.AddProduct)
GroupV2.Any("/member/add", v2.AddMember)
} // 绑定验证器
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
v.RegisterValidation("NameValid", member.NameValid)
}
}

最后,看一下调用的代码。

router/v1/member.go

package v1

import (
"ginDemo/entity"
"github.com/gin-gonic/gin"
"net/http"
) func AddMember(c *gin.Context) { res := entity.Result{}
mem := entity.Member{} if err := c.ShouldBind(&mem); err != nil {
res.SetCode(entity.CODE_ERROR)
res.SetMessage(err.Error())
c.JSON(http.StatusForbidden, res)
c.Abort()
return
} // 处理业务(下次再分享) data := map[string]interface{}{
"name" : mem.Name,
"age" : mem.Age,
}
res.SetCode(entity.CODE_ERROR)
res.SetData(data)
c.JSON(http.StatusOK, res)
}

访问看看效果吧。

访问:http://localhost:8080/v1/member/add

{
"code": -1,
"msg": "Key: 'Member.Name' Error:Field validation for 'Name' failed on the 'required' tag\nKey: 'Member.Age' Error:Field validation for 'Age' failed on the 'required' tag",
"data": null
}

访问:http://localhost:8080/v1/member/add?name=1

{
"code": -1,
"msg": "Key: 'Member.Age' Error:Field validation for 'Age' failed on the 'required' tag",
"data": null
}

访问:http://localhost:8080/v1/member/add?age=1

{
"code": -1,
"msg": "Key: 'Member.Age' Error:Field validation for 'Age' failed on the 'required' tag",
"data": null
}

访问:http://localhost:8080/v1/member/add?name=admin&age=1

{
"code": -1,
"msg": "Key: 'Member.Name' Error:Field validation for 'Name' failed on the 'NameValid' tag",
"data": null
}

访问:http://localhost:8080/v1/member/add?name=1&age=1

{
"code": -1,
"msg": "Key: 'Member.Age' Error:Field validation for 'Age' failed on the 'gt' tag",
"data": null
}

访问:http://localhost:8080/v1/member/add?name=1&age=121

{
"code": -1,
"msg": "Key: 'Member.Age' Error:Field validation for 'Age' failed on the 'lt' tag",
"data": null
}

访问:http://localhost:8080/v1/member/add?name=Tom&age=30

{
"code": 1,
"msg": "",
"data": {
"age": 30,
"name": "Tom"
}
}

未避免返回信息过多,错误提示咱们也可以统一。

if err := c.ShouldBind(&mem); err != nil {
res.SetCode(entity.CODE_ERROR)
res.SetMessage("参数验证错误")
c.JSON(http.StatusForbidden, res)
c.Abort()
return
}

这一次目录结构调整了一些,在这里说一下:

├─ ginDemo
│ ├─ common //公共方法
│ ├── common.go
│ ├─ config //配置文件
│ ├── config.go
│ ├─ entity //实体
│ ├── ...
│ ├─ middleware //中间件
│ ├── logger
│ ├── ...
│ ├── sign
│ ├── ...
│ ├─ router //路由
│ ├── ...
│ ├─ validator //验证器
│ ├── ...
│ ├─ vendor //扩展包
│ ├── github.com
│ ├── ...
│ ├── golang.org
│ ├── ...
│ ├── gopkg.in
│ ├── ...
│ ├─ Gopkg.toml
│ ├─ Gopkg.lock
│ ├─ main.go

signlogger 调整为中间件,并放到 middleware 中间件 目录。

新增了 common 公共方法目录。

新增了 validator 验证器目录。

新增了 entity 实体目录。

具体代码我会放到 GitHub,有感兴趣的可以去看:https://github.com/xinliangnote/Go。

上面还遗漏了问题一没解决,框架自带的 binding 参数还有哪些?

从框架源码了解到验证使用的是:

gopkg.in/go-playground/validator.v8

文档地址为:

https://godoc.org/gopkg.in/go-playground/validator.v8

去探索文档吧,里面有很多验证规则。

推荐阅读

Gin 框架

基础篇

本文欢迎转发,转发请注明作者和出处,谢谢!

[系列] Gin框架 - 数据绑定和验证的更多相关文章

  1. Gin框架 - 数据绑定和验证

    概述 上篇文章分享了 Gin 框架使用 Logrus 进行日志记录,这篇文章分享 Gin 框架的数据绑定与验证. 有读者咨询我一个问题,如何让框架的运行日志不输出控制台? 解决方案: engine : ...

  2. Gin框架 - 自定义错误处理

    目录 概述 错误处理 自定义错误处理 panic 和 recover 推荐阅读 概述 很多读者在后台向我要 Gin 框架实战系列的 Demo 源码,在这里再说明一下,源码我都更新到 GitHub 上, ...

  3. gin框架中的参数验证

    结构体验证 用gin框架的数据验证,可以不用解析数据,减少if else,会简洁许多. 处理请求方法 func structValidator(context *gin.Context) { var ...

  4. 基于gin框架和jwt-go中间件实现小程序用户登陆和token验证

    本文核心内容是利用jwt-go中间件来开发golang webapi用户登陆模块的token下发和验证,小程序登陆功能只是一个切入点,这套逻辑同样适用于其他客户端的登陆处理. 小程序登陆逻辑 小程序的 ...

  5. Gin框架系列03:换个姿势理解中间件

    什么是中间件 中间件,英译middleware,顾名思义,放在中间的物件,那么放在谁中间呢?本来,客户端可以直接请求到服务端接口. 现在,中间件横插一脚,它能在请求到达接口之前拦截请求,做一些特殊处理 ...

  6. gin框架教程:代码系列demo地址

    gin框架教程代码地址: https://github.com/jiujuan/gin-tutorial demo目录: 01quickstart 02parameter 03route 04midd ...

  7. Gin框架系列02:路由与参数

    回顾 上一节我们用Gin框架快速搭建了一个GET请求的接口,今天来学习路由和参数的获取. 请求动词 熟悉RESTful的同学应该知道,RESTful是网络应用程序的一种设计风格和开发方式,每一个URI ...

  8. gin框架学习手册

    前言 gin框架是go语言的一个框架,框架的github地址是:https://github.com/gin-gonic/gin 转载本文,请标注原文地址:https://www.cnblogs.co ...

  9. Golang语言系列-17-Gin框架

    Gin框架 Gin框架简介 package main import ( "github.com/gin-gonic/gin" "io" "net/ht ...

随机推荐

  1. spring boot单元测试之MockMvc

    spring单元测试之MockMvc,这个只是模拟,并不是真正的servlet,所以session.servletContext是没法用的. @RunWith(SpringRunner.class) ...

  2. hgoi#20190519

    更好的阅读体验 来我的博客观看 T1-求余问题 Abu Tahun很喜欢回文. 一个数组若是回文的,那么它从前往后读和从后往前读都是一样的,比如数组{1},{1,1,1},{1,2,1},{1,3,2 ...

  3. C++按格式接收输入字符(京东,滴滴,360笔试必用)

    头一次起这种标题,为了对得起这个标题,我尽量多写点~ 最近还是一边实习一遍投简历--笔试--面试,然而发现了自己的好多问题. 在答了京东笔试(滴滴,360也是这样的)的题后,发现与腾讯,阿里等公司的不 ...

  4. python Trojan 模块(我忘记几了)—— 通信隧道建立

    0X01 SSH的建立  我想,第一步先实现简单的ssh通信再说,类似其他那种高端的C&C将逐步研究 对于ssh,python有个module叫paramiko,对没错看起来像日语单词 The ...

  5. Spring Bean 生命周期之“我从哪里来?” 懂得这个很重要

    Spring bean 的生命周期很容易理解.实例化 bean 时,可能需要执行一些初始化以使其进入可用 (Ready for Use)状态.类似地,当不再需要 bean 并将其从容器中移除时,可能需 ...

  6. 二进制mariadb多实例

    实验环境: centos7.6 :IP: 192.168.99.110 1.首先下载二进制的压缩包,解压到一个指定的目录/hx/下 [root@centos7 hx]#tar xf mariadb-1 ...

  7. 高性能微服务网关.NETCore客户端Kong.Net开源发布

    前言 项目地址:https://github.com/lianggx/Kong.Net 你的支持使我们更加强大,请单击 star 让更多的 .NETCore 认识它. 拥抱开源的脚步,我们从来都是一直 ...

  8. Python中的函数及函数参数的使用

    函数:一个工具,随调随用 降级代码冗余 增加代码的复用性,提高开发效率,为了不成为cv战士 提高程序扩展性 函数有两个阶段:定义阶段,调用阶段. 定义时:只检查函数体内代码语法,不执行函数体内代码. ...

  9. mysql双机热备实现方案

    一.概念 1.热备份和备份的区别 热备份指的是:High Available(HA)即高可用,而备份指的是Backup,数据备份的一种.这是两种不同的概念,应对的产品也是两种功能上完全不同的产品.热备 ...

  10. Mybatis 一对多分页踩坑 对collection的分析

    背景描述: 产品和结算对象(结算名和结算金额)是一对多的关系,使用 collection 做一对多配置.但是出现一对多时,数据没有整合至一起,导致一个产品重复出现. class ResponseVo{ ...