前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >gin 接口开发 - 用户输入自动 TrimSpace

gin 接口开发 - 用户输入自动 TrimSpace

原创
作者头像
仁扬
发布2023-07-15 19:24:33
1620
发布2023-07-15 19:24:33
举报
文章被收录于专栏:仁扬笔记

最近在思考一个问题,针对用户的输入,能不能快速校验?

比方说下面的 struct,大家用过 gin 的就知道,支持指定某个字段为 required,用户如果不输入,就检验不通过。

代码语言:go
复制
type LoginForm struct {
	Username string `json:"username" form:"username"`
	Key      string `json:"key" form:"key"`
	Sign     string `json:"sign" form:"sign"`
}

然而,我们不能指望用户输入了,这个字段就是可以用的!例如他提交了这个表单:

代码语言:json
复制
{
	"username": " ",
	"key": "value",
	"sign": "     "
}

也就是说,用户输入了空格,就成功躲避我们的校验,所以我们不得不再校验一次:

代码语言:go
复制
form.Username = strings.TrimSpace(form.Username)
form.Key = strings.TrimSpace(form.Key)
form.Sign = strings.TrimSpace(form.Sign)

……显然,非常不优雅!如果字段多的话,满屏都是这样子的代码,相信屏幕前的你也是受不了的!

那有没有好办法?

因为公司存在 PHP 业务,为了兼容,JSON 的解析使用了 jsoniter 这个第三方包。如果你也是使用 gin 框架,别忘了编译指定 -tags=jsoniter 构建标签:

代码语言:shell
复制
go build -tags=jsoniter -o ./${PROJECT_NAME} ./cmd/server

而 jsoniter 有个非常强大的功能,支持在解析类型的时候,执行你给定的钩子!

于是只要我们在项目启动的时候注册以下钩子函数:

当解析到 string 的时候,自动帮我们 TrimSpace !!!

代码语言:go
复制
func init() {
	jsonAutoTrimSpace()
}

func jsonAutoTrimSpace() {
	jsoniter.RegisterTypeDecoderFunc("string", func(ptr unsafe.Pointer, iter *jsoniter.Iterator) {
		s := strings.TrimSpace(iter.ReadString())
		*((*string)(ptr)) = s
	})
}

至此,JSON 数据解析的时候,就支持自动去掉空白字符了。但是有时候参数不是 JSON 传递的,是通过 URL 的参数形式、或者是 POST 表单,该怎么办?

这个大家可以看下 http.RequestParseFormParseMultipartForm 方法,它只会解析一次。

而 gin 底层的 Bind 也是调用了这两个方法,我们可以在用户 Bind 之前,调用 ParseForm 和 ParseMultipartForm 方法,对里面的 Value 遍历并 TrimSpace 就好了!后面用户第二次调用,拿到的也是我们 TrimSpace 后的内容了。

代码语言:go
复制
func shouldBindForm(req *http.Request) (err error) {
	if err = req.ParseForm(); err != nil {
		return
	}
	const defaultMemory = 32 << 20
	if err = req.ParseMultipartForm(defaultMemory); err != nil {
		if err == http.ErrNotMultipart {
			err = nil
		} else {
			return
		}
	}
	for k, v := range req.Form {
		for i, s := range v {
			req.Form[k][i] = strings.TrimSpace(s)
		}
	}
	for k, v := range req.PostForm {
		for i, s := range v {
			req.PostForm[k][i] = strings.TrimSpace(s)
		}
	}
	if req.MultipartForm != nil {
		for k, v := range req.MultipartForm.Value {
			for i, s := range v {
				req.MultipartForm.Value[k][i] = strings.TrimSpace(s)
			}
		}
	}
	return
}

shouldBindForm 的调用时机,需要检查一下 ContentType,如果是表单,或者是 GET 请求,就执行一下,具体可以参考 gin 的实现。


文章来源于本人博客,发布于 2019-09-08,原文链接:https://imlht.com/archives/193/

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档