首页 > 其他分享 >go gin web应用-通过中间件形式实现通用的参数检验

go gin web应用-通过中间件形式实现通用的参数检验

时间:2023-03-25 12:55:58浏览次数:49  
标签:web return err ctx 中间件 校验 参数检验 func gin

都知道 gin 在web开发方面应用广泛,但在参数校验上,之前写一堆 POST 接口的时候,每个接口的业务代码里都要去实现 validate 校验逻辑,感觉代码复用糟糕。

为解决这问题,想到通过 reflect 包是不是可以实现通用的校验处理呢。如果可以实现,业务逻辑就只需要专注与业务实现,进一步实现高内聚。

main.go

package main

import (
	"errors"
	"fmt"
	"net/http"
	"reflect"
	
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/go-playground/validator/v10"
)

func main()  {
	r := gin.New()
	r.Use(PrintRequestURL(), ValidateMiddleware())
	r.POST("/hello", func(ctx *gin.Context) {
		ctx.JSON(http.StatusOK, gin.H{
			"msg": fmt.Sprintf("api: %s", ctx.Request.URL),
		})
	})
	r.POST("/ping", func(ctx *gin.Context) {
		ctx.JSON(http.StatusOK, gin.H{
			"msg": fmt.Sprintf("api: %s", ctx.Request.URL),
		})
	})

	_ = r.Run(":8080")
}

// 定义请求处理前后的进出打印中间件
func PrintRequestURL() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		fmt.Printf("Request URL: %s start.\n", ctx.Request.URL)
		ctx.Next()
		fmt.Printf("Request URL: %s end.\n", ctx.Request.URL)
	}
}

// 定义参数自动校验中间件,根据url自动关联对应的结构体进行校验
func ValidateMiddleware() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		// 校验出错,需要及时终止请求下传
		if err := validateParams(ctx); err != nil {
			// 实际项目,不把具体出错传给前台,只需要响应比如 {“msg”: "req params validate error"},后台应该记录具体出错,
			// 细节在后台记录,前台只要收到出错即可
			ctx.JSON(http.StatusOK, gin.H{
				"msg": fmt.Sprintf("params validate error: %s", err),
			})
			ctx.Abort()
		}

		ctx.Next()
	}
}

// 校验函数
func validateParams(ctx *gin.Context) error {
	return validateOrNot(ctx)
}

// 校验函数,通过反射获取原始类型进行 bind和validate
func validateOrNot(ctx *gin.Context) error {
	var err error
	// 定义请求体接收变量
	var params interface{}
	// 获取 url 对应的结构体类型,interface{}
	val, ok := url2struct[ctx.Request.URL.String()]
	if !ok {
		return errors.New("Bad url.")
	}

	// 先从interface{}还原,提取原值类型,再运行时,获取校验体原始类型,
	reqTyp := reflect.Indirect(reflect.ValueOf(val)).Type()
	if reqTyp == nil {
		return errors.New("reqStruct is nil")
	}
	// 原始类型
	params = reflect.New(reqTyp).Interface()
	err = ctx.ShouldBindBodyWith(&params, binding.JSON)
	if err != nil {
		return err
	}
	// 拿到实际请求结构体进行校验
	valid := validator.New()
	err = valid.Struct(params)
	if err != nil {
		return err
	}

	return nil
}

// /hello 对应的请求参数结构体
type HelloParams struct {
	Param1 string `json:"param1" validate:"required,min=5,max=10"`
}

// 实现reqStruct接口方法
func (t HelloParams) do()  {}

// 定义 /ping 的请求结构体
type PingParams struct {
	Param2 string `json:"param2" validate:"required,min=6,max=10"`
}

// 实现reqStruct接口方法
func (t PingParams) do()  {

}

// 初始化 url2struct 变量
func init()  {
	initURL2Struct()
}

type reqStruct interface {
	do()
}

var url2struct map[string]reqStruct

func initURL2Struct()  {
	if url2struct == nil {
		url2struct = make(map[string]reqStruct)
	}
	url2struct = M
}

// 定义 URL: struct{},在新增 接口 需要校验的时候,需要维护此对应关系
var (
	M = map[string]reqStruct{
		"/hello": HelloParams{},
		"/ping": PingParams{},
	}
)

代码注释也比较详细,主要通过两个 POST 接口来测试,以下是 Postman 测试情况:
测试正常情况

测试异常请求参数
参数长度过长

参数长度过短

可以看到,我们通过 reflect + validate 即实现了中间件的形式校验参数,让业务专注业务实现,不同的业务接口通过 map 实现的 url: struct{} 来完成映射,我们只需要每次在新增 POST 接口的时候,添加对应关系即可,扩展性较好,当然反射也会一定程度影响性能,就看取舍了,未做基本测试,具体情况具体分析吧。

参考文章:

标签:web,return,err,ctx,中间件,校验,参数检验,func,gin
From: https://www.cnblogs.com/davis12/p/17254477.html

相关文章

  • 基于Web的图书管理系统运行教程
    @文章目录目录1、前期必备1.1、所需软件版本说明1.2、下载源码1.3、下载开发工具1.4、下载Tomcat1.5、下载JDK并配置环境变量1.6、安装数据库和数据库管理工具2、将SQL文件......
  • 如何提高 Website 的 organic traffic?
    Lighthouse中的SEOMetric和网站在Google搜索引擎结果列表中的排名之间存在一定的关系,但它们并不是直接相关的指标。Lighthouse中的SEOMetric包括一些与搜索引擎......
  • 【小沐学C#】WPF中嵌入web网页控件(WebBrowser、WebView2、CefSharp)
    1、简介使用WindowsPresentationFoundation(WPF),你可以创建适用于Windows且具有非凡视觉效果的桌面客户端应用程序。1.1WPF简介<fontcolor=blue>WPF的核心是......
  • scrapy-中间件
    Scrapy中间件学习目标:应用scrapy中使用中间件使用随机UA的方法了解scrapy中使用代理ip的的方法1、scrapy中间件的分类和作用1.1scrapy中间件的分类根据scrapy......
  • WEB服务器的基本介绍
    (WEB服务器的基本介绍)一、Web服务器介绍HTTP服务器本质上也是一种应用程序——它通常运行在服务器上,绑定服务器的IP地址并监听某个TCP端口,接收并处理HTTP请求,这样客户端(......
  • .net core利用中间件获取IHttpContextAccessor
    1、建立一个静态类publicclassServiceProviderInstance{publicstaticIServiceProviderInstance{get;set;}}2、在应用初始化过程中,WebHo......
  • JAVAWEB-北京地铁查询系统(Servlet+JSP+CSS+SQL 实现)部分代码
    #这是我与伙伴合作完成的练习项目@小彭先森页面展示请见我的上一篇博客:https://www.cnblogs.com/rsy-bxf150/p/17253623.html完整代码请看我的GitHub:https://github.co......
  • 地铁最终javaweb
    关于在编写地铁查询系统主界面chaxun-01.jsp中出现了methodget与post中所产生的问题(get能传值,post不能传值) 在编写中我发现了method中用get可以把用户输入的......
  • JAVAWEB-北京地铁查询系统(Servlet+JSP+CSS+SQL 实现)
    Servlet+JSP+CSS+SQL实现完善的地铁系统页面#这是我和伙伴合作完成的练习项目#代码我将放在下一篇博客功能介绍:1.地铁线路查询:选择线路,输出线路上的站点名。2.地铁站......
  • 使用 PC 端浏览器开发者工具对移动端真机环境 Web 页面进行远程调试
    Mac/Windows浏览器开发者工具远程调试iPhone/Android页面在移动端Web开发中,有时候只通过模拟器进行调试是不够的,需要在真机环境下进行调试才能发现并解决一些问题。......