Open
Description
What happened?
当我使用 v10.26.0 的时候,验证参数,导致了下面的崩溃:
2025/04/03 08:28:01 [Recovery] 2025/04/03 - 08:28:01 panic recovered:
POST /canteen/orderItem/multiple HTTP/1.1
Host: localhost:8586
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Authorization: *
Connection: close
Content-Length: 54
Content-Type: application/json
Referer: http://localhost:9281/h5/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1 HBuilderX
X-Forwarded-For: 127.0.0.1
X-Forwarded-Host: localhost:9281
X-Forwarded-Port: 9281
X-Forwarded-Proto: http
Bad field type param.OrderItem
E:/dependencies/go/pkg/mod/github.com/go-playground/validator/v10@v10.26.0/baked_in.go:2192 (0x14629d2)
isGte: panic(fmt.Sprintf("Bad field type %T", field.Interface()))
E:/dependencies/go/pkg/mod/github.com/go-playground/validator/v10@v10.26.0/baked_in.go:2285 (0x14635cb)
hasMinOf: return isGte(fl)
E:/dependencies/go/pkg/mod/github.com/go-playground/validator/v10@v10.26.0/baked_in.go:44 (0x1450ce3)
wrapFunc.func1: return fn(fl)
E:/dependencies/go/pkg/mod/github.com/go-playground/validator/v10@v10.26.0/validator.go:473 (0x14765e1)
(*validate).traverseField: if !ct.fn(ctx, v) {
E:/dependencies/go/pkg/mod/github.com/go-playground/validator/v10@v10.26.0/validator.go:315 (0x147882f)
(*validate).traverseField: v.traverseField(ctx, parent, current.Index(i), ns, structNs, reusableCF, ct)
E:/dependencies/go/pkg/mod/github.com/go-playground/validator/v10@v10.26.0/validator.go:78 (0x1473f0e)
(*validate).validateStruct: v.traverseField(ctx, current, current.Field(f.idx), ns, structNs, f, f.cTags)
E:/dependencies/go/pkg/mod/github.com/go-playground/validator/v10@v10.26.0/validator_instance.go:396 (0x147dcc8)
(*Validate).StructCtx: vd.validateStruct(ctx, top, val, val.Type(), vd.ns[0:0], vd.actualNs[0:0], nil)
E:/dependencies/go/pkg/mod/github.com/go-playground/validator/v10@v10.26.0/validator_instance.go:369 (0x147d7d4)
(*Validate).Struct: return v.StructCtx(context.Background(), s)
E:/dependencies/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/binding/default_validator.go:83 (0x158a74b)
(*defaultValidator).validateStruct: return v.validate.Struct(obj)
E:/dependencies/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/binding/default_validator.go:60 (0x158a42a)
(*defaultValidator).ValidateStruct: return v.validateStruct(obj)
E:/dependencies/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/binding/binding.go:121 (0x1589da9)
validate: return Validator.ValidateStruct(obj)
E:/dependencies/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/binding/json.go:55 (0x15902b7)
decodeJSON: return validate(obj)
E:/dependencies/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/binding/json.go:37 (0x15900d5)
jsonBinding.Bind: return decodeJSON(req.Body, obj)
E:/dependencies/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:752 (0x15a4b98)
(*Context).ShouldBindWith: return b.Bind(c.Request, obj)
E:/code/GoglandProjects/realMsgService/ctrl/order_item.go:173 (0x18598f3)
(*OrderItemController).CreateOrderItems: if err := c.ShouldBindWith(&in, binding.JSON); err != nil {
E:/dependencies/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0x159fdb9)
(*Context).Next: c.handlers[c.index](c)
E:/code/GoglandProjects/realMsgService/web/middleware/online.go:18 (0x18936d2)
Online: c.Next()
E:/dependencies/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0x159fdb9)
(*Context).Next: c.handlers[c.index](c)
E:/code/GoglandProjects/realMsgService/web/middleware/permission.go:16 (0x1895c97)
PermissionMiddleware.func1: c.Next()
E:/dependencies/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0x159fdb9)
(*Context).Next: c.handlers[c.index](c)
E:/code/GoglandProjects/realMsgService/web/middleware/auth.go:73 (0x1892804)
Auth: c.Next()
E:/dependencies/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0x159fdb9)
(*Context).Next: c.handlers[c.index](c)
E:/code/GoglandProjects/realMsgService/web/middleware/elapsed.go:14 (0x18929c5)
Elapsed: c.Next()
E:/dependencies/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0x159fdb9)
(*Context).Next: c.handlers[c.index](c)
E:/code/GoglandProjects/realMsgService/web/middleware/safety_trace.go:71 (0x189486b)
SafetyTracer: c.Next()
E:/dependencies/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0x159fdb9)
(*Context).Next: c.handlers[c.index](c)
E:/code/GoglandProjects/realMsgService/web/middleware/repeat_read.go:25 (0x1893a26)
RepeatRead: c.Next()
E:/dependencies/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0x159fdb9)
(*Context).Next: c.handlers[c.index](c)
E:/dependencies/go/pkg/mod/github.com/gin-contrib/zap@v1.1.5/zap.go:76 (0x189057a)
GinzapWithConfig.func1: c.Next()
E:/dependencies/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0x159fdb9)
(*Context).Next: c.handlers[c.index](c)
E:/code/GoglandProjects/realMsgService/web/middleware/request_id.go:19 (0x1893cf8)
RequestIDMiddleware: c.Next()
E:/dependencies/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0x159fdb9)
(*Context).Next: c.handlers[c.index](c)
E:/dependencies/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/recovery.go:102 (0x15b26bc)
CustomRecoveryWithWriter.func1: c.Next()
E:/dependencies/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go:185 (0x159fdb9)
(*Context).Next: c.handlers[c.index](c)
E:/dependencies/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:633 (0x15b00e9)
(*Engine).handleHTTPRequest: c.Next()
E:/dependencies/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/gin.go:589 (0x15afbbb)
(*Engine).ServeHTTP: engine.handleHTTPRequest(c)
C:/Program Files/Go/src/net/http/server.go:3210 (0xbb3f16)
serverHandler.ServeHTTP: handler.ServeHTTP(rw, req)
C:/Program Files/Go/src/net/http/server.go:2092 (0xb9cc74)
(*conn).serve: serverHandler{c.server}.ServeHTTP(w, w.req)
C:/Program Files/Go/src/runtime/asm_amd64.s:1700 (0x6a59c0)
goexit: BYTE $0x90 // NOP
当我退回 v10.14.0 的时候就没有问题了。
realMsgService/ctrl/order_item.go:173 是这样的:
这段代码我已经很久没有改过它,这一次崩溃,就是因为 github.com/go-playground/validator/v10 从 v10.14.0 升到了 v10.26.0 导致的。这给我造成了非常严重的客户信任危机。
代码中使用的结构体 param.OrderItemsCreateInput 是这样的:
package param
type OrderItem struct {
SkuId int64 `json:"skuId,omitempty" form:"skuId" binding:"required,gte=1"`
Subject int8 `json:"subject,omitempty" form:"subject" binding:"required,oneof=1 2"`
Amount int64 `json:"amount,omitempty" form:"amount" binding:"required,gte=1"`
}
type OrderItemsCreateInput struct {
OrderItems []OrderItem `json:"orderItems,omitempty" form:"orderItems" binding:"required,dive,min=1,max=100"`
}
Version
v10.26.0
Example Code
func (ctrl *OrderItemController) CreateOrderItems(c *gin.Context) {
var in param.OrderItemsCreateInput
if err := c.ShouldBindWith(&in, binding.JSON); err != nil {
c.JSON(http.StatusBadRequest, ctrl.ApiCheck(c, gin.H{"msg": ctrl.processError(err)}))
return
}
session, err := concurrency.NewSession(g.EtcdCli())
if err != nil {
c.JSON(http.StatusInternalServerError, ctrl.ApiFail(c, gin.H{"msg": err.Error()}))
return
}
defer func(session *concurrency.Session) {
if err := session.Close(); err != nil {
g.LogWithContext(c.Request.Context()).Error("关闭etcd的session出现错误", zap.Error(err))
}
}(session)
mutex := concurrency.NewMutex(session, fmt.Sprintf("/create-order-item-lock/%d", ctrl.MustLoginUser(c).UserId))
// 竞争锁,最多等待3秒
ctx, cancel := context.WithTimeout(c, time.Second*3)
defer cancel()
err = mutex.TryLock(ctx)
if errors.Is(err, concurrency.ErrLocked) {
c.JSON(http.StatusOK, ctrl.ApiFail(c, gin.H{"msg": "这会儿下单的人太多了"}))
return
}
if err != nil {
c.JSON(http.StatusInternalServerError, ctrl.ApiFail(c, gin.H{"msg": err.Error()}))
return
}
defer func(mutex *concurrency.Mutex, ctx context.Context) {
if err := mutex.Unlock(ctx); err != nil {
g.Log().Error("释放etcd的锁出现错误", zap.Error(err))
}
}(mutex, ctx)
// 创建订单
if orderId, err := ctrl.createOrderItemsAction(c, in); err != nil {
c.JSON(http.StatusInternalServerError, ctrl.ApiFail(c, gin.H{"msg": err.Error()}))
return
} else {
c.JSON(http.StatusOK, ctrl.ApiOk(c, gin.H{
"code": http.StatusOK,
"data": map[string]interface{}{
"orderId": orderId,
},
}))
return
}
}