grpc集成proto-validator进行参数校验
proto-validator的文档
在proto文件中定义go结构体中的参数校验规则,用于详细见于文档:
https://github.com/bufbuild/protoc-gen-validate
项目目录结构
hello.proto
syntax = "proto3";
import "validate.proto";
option go_package="./;proto";
service Greeter {
rpc SayHello (Person) returns (Person);
}
message Person {
uint64 id = 1 [(validate.rules).uint64.gt = 999];
string email = 2 [(validate.rules).string.email = true];
string mobile = 3 [(validate.rules).string = {
pattern: "^1[3456789]\\d{9}$"}];
}
validator.proto
copy代码到项目目录下即可
validator.proto
client.go
package main
import (
"Go_Bible/grpc_validate_test/proto"
"context"
"fmt"
"google.golang.org/grpc"
)
func main() {
var opts []grpc.DialOption
opts = append(opts, grpc.WithInsecure())
conn, err := grpc.Dial("localhost:15001", opts...)
if err != nil {
panic("客户端拨号失败-->" + err.Error())
}
defer conn.Close()
client := proto.NewGreeterClient(conn)
res, err := client.SayHello(context.Background(), &proto.Person{
Id: 1000,
Email: "kevin@qq.com",
Mobile: "18888888888",
})
if err != nil {
panic("客户端调用SayHello接口失败-->" + err.Error() + "\n")
}
fmt.Println("客户端调用接口成功:" + res.Email)
}
server.go
package main
import (
"Go_Bible/grpc_validate_test/proto"
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"net"
)
type Server struct {
proto.UnimplementedGreeterServer
}
func (s *Server) SayHello(ctx context.Context, req *proto.Person) (*proto.Person, error) {
return &proto.Person{
Email: "12345677",
}, nil
}
// 自定义接口类型,里面有Validate()方法,用于返回校验是否通过,proto中定义的类型如果定义了校验规则,则会在go代码中实现了Validate方法
// 因此在服务端定义这个校验类型结构体,用于接口退化判断,增强代码功能,不用在拦截器校验每种类型的时候,都断言一次对应类型
type Validator interface {
Validate() error
}
func main() {
// 创建验证拦截器
interceptor := func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {
fmt.Println("服务端开始对接口拦截")
// 把客户端传过来的结构体类型 退化成 实现了校验方法的类型
if r, ok := req.(Validator); ok {
if err = r.Validate(); err != nil {
fmt.Println("服务端拦截器中的验证器校验错误-->" + err.Error())
return nil, status.Error(codes.InvalidArgument, err.Error())
}
}
fmt.Println("服务端接口验证器校验通过")
// 校验通过,继续调用接口
resp, err = handler(ctx, req)
fmt.Println("服务端接口拦截结束")
return resp, err
}
// 注册拦截器
opt := grpc.UnaryInterceptor(interceptor)
var opts []grpc.ServerOption
opts = append(opts, opt)
// 创建服务
server := grpc.NewServer(opts...)
// 注册服务
proto.RegisterGreeterServer(server, &Server{})
lis, err := net.Listen("tcp", "0.0.0.0:15001")
if err != nil {
panic("服务端监听失败-->" + err.Error())
}
err = server.Serve(lis)
if err != nil {
panic("服务端开启服务失败-->" + err.Error())
}
}