项目目录:examples/demo/custom_metrics
本例展示了如何使用结构体验证 (struct validation)和处理函数钩子(handler hooks)
运行
服务器开起来
sh
go run main.go
服务监听了 3251 端口,使用命令行跑一下基本功能
有一点需要注意一下,之前的 demo 都设置了 component.WithNameFunc(strings.ToLower)
,所以之前的 router 都是全小写,这个示例程序并没有设置,访问的时候是大小写敏感的,否则会找不到服务器或 handler:
sh
> pitaya-cli
Pitaya REPL Client
>>> request metagameDemo.metagamehandler.createplayercheat {"name":"test", "email":"[email protected]", "softCurrency":10, "hardCurrency":20}
>>> sv->{"code":"PIT-404","msg":"pitaya/handler: metagameDemo.metagamehandler.createplayercheat not found"}
>>> request metagameDemo.metagameHandler.CreatePlayerCheat {"name":"test", "email":"[email protected]", "softCurrency":10, "hardCurrency":20}
>>> sv->{"msg":"ok"}
服务器端输出日志如下:
sh
time="2023-10-16T16:13:59+08:00" level=info msg="Simple Before exec" requestId=BJIiSpBZ1Pue7CIr9md6iz route=metagameDemo.metagameHandler.CreatePlayerCheat source=pitaya userId=
time="2023-10-16T16:13:59+08:00" level=info msg="Name: test" requestId=BJIiSpBZ1Pue7CIr9md6iz route=metagameDemo.metagameHandler.CreatePlayerCheat source=pitaya userId=
time="2023-10-16T16:13:59+08:00" level=info msg="Email: [email protected]" requestId=BJIiSpBZ1Pue7CIr9md6iz route=metagameDemo.metagameHandler.CreatePlayerCheat source=pitaya userId=
time="2023-10-16T16:13:59+08:00" level=info msg="SoftCurrency: 10" requestId=BJIiSpBZ1Pue7CIr9md6iz route=metagameDemo.metagameHandler.CreatePlayerCheat source=pitaya userId=
time="2023-10-16T16:13:59+08:00" level=info msg="HardCurrency: 20" requestId=BJIiSpBZ1Pue7CIr9md6iz route=metagameDemo.metagameHandler.CreatePlayerCheat source=pitaya userId=
time="2023-10-16T16:13:59+08:00" level=debug msg="SID=4, Data={"name":"test","email":"[email protected]","softCurrency":10,"hardCurrency":20}" requestId=BJIiSpBZ1Pue7CIr9md6iz route=metagameDemo.metagameHandler.CreatePlayerCheat source=pitaya userId=
time="2023-10-16T16:13:59+08:00" level=info msg="CreatePlayerChest called" requestId=BJIiSpBZ1Pue7CIr9md6iz route=metagameDemo.metagameHandler.CreatePlayerCheat source=pitaya userId=
time="2023-10-16T16:13:59+08:00" level=info msg="Simple After exec - response: &{ok} , error: <nil>" requestId=BJIiSpBZ1Pue7CIr9md6iz route=metagameDemo.metagameHandler.CreatePlayerCheat source=pitaya userId=
钩子方法输出了 Simple Before 和 Simple After...
如果我们给一个不满足验证的值,hardCurrency = 2000,服务器会验证输入,返回错误信息:
sh
>>> request metagameDemo.metagameHandler.CreatePlayerCheat {"name":"test", "email":"[email protected]", "softCurrency":10, "hardCurrency":2000}
>>> sv->{"code":"PIT-000","msg":"Key: 'CreatePlayerCheatArgs.HardCurrency' Error:Field validation for 'HardCurrency' failed on the 'lte' tag"}
代码分析
结构体验证
开启结构体验证:
go
// main.go:101
config.DefaultPipelines.StructValidation.Enabled = true
使用 tag
validate 设置验证规则:
go
type CreatePlayerCheatArgs struct {
Name string `json:"name"`
Email string `json:"email" validate:"email"`
SoftCurrency int `json:"softCurrency" validate:"gte=0,lte=1000"`
HardCurrency int `json:"hardCurrency" validate:"gte=0,lte=200"`
}
Pitaya 默认的验证器是这个包:github.com/go-playgrou...
这个功能很强大啊,在游戏开发里,默认客户端是不可信的,需要对大部分上行参数做数据验证,自己手动写就很麻烦,在 pitaya 框架里,我们可以使用 StructValidation
来实现,优雅!
结构体验证的实现用到了我们接下来要说的钩子方法,开启验证,就是在 BeforeHandler
添加了验证方法:
go
// builder.go: 314
func configureDefaultPipelines(handlerHooks *pipeline.HandlerHooks) {
handlerHooks.BeforeHandler.PushBack(defaultpipelines.StructValidatorInstance.Validate)
}
钩子方法 BeforeHandler AfterHandler
pitaya 提供了钩子方法集:
- BeforeHandler 在消息处理方法前执行,结构体验证就是在这里调用的
- AfterHandler 在消息处理方法后执行
这两种钩子方法集在 ProcessHandlerMessage
里被调用:
go
// handler_pool.go:43
func (h *HandlerPool) ProcessHandlerMessage(
ctx context.Context,
rt *route.Route,
serializer serialize.Serializer,
handlerHooks *pipeline.HandlerHooks,
session session.Session,
data []byte,
msgTypeIface interface{},
remote bool,
) ([]byte, error) {
...
ctx, arg, err = handlerHooks.BeforeHandler.ExecuteBeforePipeline(ctx, arg)
if err != nil {
return nil, err
}
...
resp, err := util.Pcall(handler.Method, args)
...
resp, err = handlerHooks.AfterHandler.ExecuteAfterPipeline(ctx, resp, err)
...
}
ExecuteBeforePipeline
和 ExecuteAfterPipeline
就是遍历切片,依次调用注册的方法