cloudwego/hertz-examples

参数验证功能咨询

ccpwcn opened this issue · 7 comments

Describe the bug
我参照hertz文档写了一段代码,一个小型服务,但是参数验证始终存在问题。下面是我的代码:

package main

import (
	"context"
	"github.com/cloudwego/hertz/pkg/app/server/binding"
	"github.com/cloudwego/hertz/pkg/common/hlog"

	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/server"
	"github.com/cloudwego/hertz/pkg/common/utils"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
)

type BindError struct {
	ErrType, FailField, Msg string
}

// Error implements error interface.
func (e *BindError) Error() string {
	if e.Msg != "" {
		return e.ErrType + ": expr_path=" + e.FailField + ", cause=" + e.Msg
	}
	return e.ErrType + ": expr_path=" + e.FailField + ", cause=invalid"
}

type ValidateError struct {
	ErrType, FailField, Msg string
}

// Error implements error interface.
func (e *ValidateError) Error() string {
	if e.Msg != "" {
		return e.ErrType + ": expr_path=" + e.FailField + ", cause=" + e.Msg
	}
	return e.ErrType + ": expr_path=" + e.FailField + ", cause=invalid"
}

func init() {
	CustomBindErrFunc := func(failField, msg string) error {
		err := BindError{
			ErrType:   "bindErr",
			FailField: "[bindFailField]: " + failField,
			Msg:       "[参数接收失败]: " + msg,
		}

		return &err
	}

	CustomValidateErrFunc := func(failField, msg string) error {
		err := ValidateError{
			ErrType:   "validateErr",
			FailField: "[validateFailField]: " + failField,
			Msg:       "[参数验证失败]: " + msg,
		}

		return &err
	}

	binding.SetErrorFactory(CustomBindErrFunc, CustomValidateErrFunc)
}

type LoginInput struct {
	UserName   string `json:"userName,required" vd:"regexp('[a-zA-Z0-9_-]{4,50}$'); msg:'账号格式错误'"`
	Password   string `json:"password,required"`
	Captcha    string `json:"captcha,required" vd:"len($)==4; msg:'验证码字符数量不正确'"`
	ClientType int    `json:"clientType,required" vd:"$==1||$==2||$==3||$==4; msg:'客户端类型无效'"`
}

func main() {
	h := server.Default()

	h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
		ctx.JSON(consts.StatusOK, utils.H{"msg": "pong"})
	})

	h.POST("/login", func(c context.Context, ctx *app.RequestContext) {
		var req LoginInput
		if err := ctx.BindAndValidate(&req); err != nil {
			hlog.CtxErrorf(c, "登录参数验证失败:%+v", err)
			ctx.JSON(consts.StatusBadRequest, utils.H{"msg": err.Error()})
			return
		}
		hlog.CtxInfof(c, "登录,参数:%+v", req)
		ctx.JSON(consts.StatusOK, utils.H{"msg": "login success", "data": req})
	})

	h.Spin()
}

To Reproduce

  1. 请求curl -X POST -d '{"userName":"z","password":"123456","captcha":"1234","clientType":-1}' -H 'Content-Type:application/json' localhost:8888/login,返回{"msg":"validateErr: expr_path=[validateFailField]: UserName, cause=[参数验证失败]: 账号格式错误"}这个是符合预期的
  2. 请求$ curl -X POST -d '{"userName":"z","password":"123456","captcha":"1234"}' -H 'Content-Type:application/json' localhost:8888/login,返回{"msg":"bindErr: expr_path=[bindFailField]: clientType, cause=[参数接收失败]: missing required parameter"}

Expected behavior

当我在struct中定义了json的tag和vd之后,vd的校验没问题了,可是json的tag中的required对应的提示消息上哪里去自定义呢?

Hertz version:

v0.4.2

我的gomod

module hertz_demo

go 1.19

require github.com/cloudwego/hertz v0.4.2

require (
	github.com/bytedance/go-tagexpr/v2 v2.9.2 // indirect
	github.com/bytedance/gopkg v0.0.0-20220413063733-65bf48ffb3a7 // indirect
	github.com/bytedance/sonic v1.5.0 // indirect
	github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06 // indirect
	github.com/cloudwego/netpoll v0.3.1 // indirect
	github.com/fsnotify/fsnotify v1.5.4 // indirect
	github.com/golang/protobuf v1.5.0 // indirect
	github.com/henrylee2cn/ameda v1.4.10 // indirect
	github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8 // indirect
	github.com/klauspost/cpuid/v2 v2.0.9 // indirect
	github.com/nyaruka/phonenumbers v1.0.55 // indirect
	github.com/tidwall/gjson v1.13.0 // indirect
	github.com/tidwall/match v1.1.1 // indirect
	github.com/tidwall/pretty v1.2.0 // indirect
	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
	golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
	golang.org/x/sys v0.0.0-20220412211240-33da011f77ad // indirect
	google.golang.org/protobuf v1.27.1 // indirect
)

Environment:

set GO111MODULE=on
set GOARCH=amd64
set GOBIN=
set GOCACHE=C:\Users\lidawei\AppData\Local\go-build
set GOENV=C:\Users\lidawei\AppData\Roaming\go\env
set GOEXE=.exe
set GOEXPERIMENT=
set GOFLAGS=
set GOHOSTARCH=amd64
set GOHOSTOS=windows
set GOINSECURE=
set GOMODCACHE=C:\Users\lidawei\go\pkg\mod
set GONOPROXY=
set GONOSUMDB=
set GOOS=windows
set GOPATH=C:\Users\lidawei\go
set GOPRIVATE=
set GOPROXY=https://goproxy.cn
set GOROOT=C:\Program Files\Go
set GOSUMDB=sum.golang.org
set GOTMPDIR=
set GOTOOLDIR=C:\Program Files\Go\pkg\tool\windows_amd64
set GOVCS=
set GOVERSION=go1.19.2
set GCCGO=gccgo
set GOAMD64=v1
set AR=ar
set CC=gcc
set CXX=g++
set CGO_ENABLED=1
set GOMOD=E:\code\GoglandProjects\hertz_demo\go.mod
set GOWORK=
set CGO_CFLAGS=-g -O2
set CGO_CPPFLAGS=
set CGO_CXXFLAGS=-g -O2
set CGO_FFLAGS=-g -O2
set CGO_LDFLAGS=-g -O2
set PKG_CONFIG=pkg-config
set GOGCCFLAGS=-m64 -mthreads -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=C:\Users\lidawei\AppData\Local\Temp\go-build1957160079=/tmp/go-build -gno-record-gcc-switches
PS E:\code\GoglandProjects\hertz_demo>

Additional context

我重点想验证hertz与go-tagexpr整合在一起的时候的强大的参数验证功能,但是认真地讲,文档和资料不太齐全,示例也不够生动,费了好大劲,终于弄明白了基本用法,但是对于required的提示消息,仍然不知道上哪里去自定义。

我看了下 go-tagexpr 的实现,这里其实 required 的校验他在这里直接返回来了一个error。 这应该算是设计的不合理,不过本质上绑定失败的error自定义主要指在反射绑定参数过程发生的。

struct ChangePasswordRequest {
1:required i32 ID (go.tag = 'json:"id"' api.form="id", api.query="id")
2:required string password (api.vd="regexp('^(?=.[a-z])(?=.[A-Z])(?=.\d)(?=.[!@#$%^&*()_-+=]).{8,16}$');msg:'密码至少6个字符,一个小写字母、一个大写字母、一个数字和一个特殊字符'" api.form="password")
}
校验失效,什么原因呀?

@ccpwcn 新提一个 issue 吧,不是一个问题

ccpwcn commented

@ccpwcn 新提一个 issue 吧,不是一个问题

对 go-tagexpr 我已经放弃,不好用。换go-playgroud-v10了

@ccpwcn 新提一个 issue 吧,不是一个问题

对 go-tagexpr 我已经放弃,不好用。换go-playgroud-v10了

可以的,我也不喜欢用 go-tagexpr

cc @FGYFFFF 民意