gRPC之proto数据验证
1、proto数据验证
本篇将介绍grpc_validator,它可以对gRPC数据的输入和输出进行验证。
这里复用上一篇文章的代码。
1.1 创建proto文件,添加验证规则
这里使用第三方插件go-proto-validators 自动生成验证规则。
地址:https://github.com/mwitkow/go-proto-validators
$ go get github.com/mwitkow/go-proto-validators
1.1.1 新建simple.proto文件
syntax = "proto3";
package proto;
option go_package = "./proto;proto";// validator.proto是github.com/mwitkow/go-proto-validators/validator.proto
import "validator.proto";message InnerMessage {// some_integer can only be in range (1, 100).int32 some_integer = 1 [(validator.field) = {int_gt: 0, int_lt: 100}];// some_float can only be in range (0;1).double some_float = 2 [(validator.field) = {float_gte: 0, float_lte: 1}];
}message OuterMessage {// important_string must be a lowercase alpha-numeric of 5 to 30 characters (RE2 syntax).string important_string = 1 [(validator.field) = {regex: "^[a-z]{2,5}$"}];// proto3 doesn't have `required`, the `msg_exist` enforces presence of InnerMessage.InnerMessage inner = 2 [(validator.field) = {msg_exists: true}];
}service Simple {rpc Route (InnerMessage) returns (OuterMessage) {};
}
validator.proto文件内容:
// Copyright 2016 Michal Witkowski. All Rights Reserved.
// See LICENSE for licensing terms.// Protocol Buffers extensions for defining auto-generateable validators for messages.// TODO(mwitkow): Add example.syntax = "proto2";
package validator;import "google/protobuf/descriptor.proto";option go_package = "github.com/mwitkow/go-proto-validators;validator";// TODO(mwitkow): Email protobuf-global-extension-registry@google.com to get an extension ID.extend google.protobuf.FieldOptions {optional FieldValidator field = 65020;
}extend google.protobuf.OneofOptions {optional OneofValidator oneof = 65021;
}message FieldValidator {// Uses a Golang RE2-syntax regex to match the field contents.optional string regex = 1;// Field value of integer strictly greater than this value.optional int64 int_gt = 2;// Field value of integer strictly smaller than this value.optional int64 int_lt = 3;// Used for nested message types, requires that the message type exists.optional bool msg_exists = 4;// Human error specifies a user-customizable error that is visible to the user.optional string human_error = 5;// Field value of double strictly greater than this value.// Note that this value can only take on a valid floating point// value. Use together with float_epsilon if you need something more specific.optional double float_gt = 6;// Field value of double strictly smaller than this value.// Note that this value can only take on a valid floating point// value. Use together with float_epsilon if you need something more specific.optional double float_lt = 7;// Field value of double describing the epsilon within which// any comparison should be considered to be true. For example,// when using float_gt = 0.35, using a float_epsilon of 0.05// would mean that any value above 0.30 is acceptable. It can be// thought of as a {float_value_condition} +- {float_epsilon}.// If unset, no correction for floating point inaccuracies in// comparisons will be attempted.optional double float_epsilon = 8;// Floating-point value compared to which the field content should be greater or equal.optional double float_gte = 9;// Floating-point value compared to which the field content should be smaller or equal.optional double float_lte = 10;// Used for string fields, requires the string to be not empty (i.e different from "").optional bool string_not_empty = 11;// Repeated field with at least this number of elements.optional int64 repeated_count_min = 12;// Repeated field with at most this number of elements.optional int64 repeated_count_max = 13;// Field value of length greater than this value.optional int64 length_gt = 14;// Field value of length smaller than this value.optional int64 length_lt = 15;// Field value of length strictly equal to this value.optional int64 length_eq = 16;// Requires that the value is in the enum.optional bool is_in_enum = 17;// Ensures that a string value is in UUID format.// uuid_ver specifies the valid UUID versions. Valid values are: 0-5.// If uuid_ver is 0 all UUID versions are accepted.optional int32 uuid_ver = 18;
}message OneofValidator {// Require that one of the oneof fields is set.optional bool required = 1;
}
1.1.2 编译simple.proto文件
$ go get github.com/mwitkow/go-proto-validators/protoc-gen-govalidators
$ go install github.com/mwitkow/go-proto-validators/protoc-gen-govalidators
$ protoc --govalidators_out=. --go_out=plugins=grpc:. simple.proto
编译完成后,自动生成simple.pb.go和simple.validator.pb.go文件,simple.pb.go文件不再介绍,我们
看下simple.validator.pb.go文件。
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: simple.protopackage protoimport (fmt "fmt"math "math"proto "github.com/golang/protobuf/proto"_ "github.com/mwitkow/go-proto-validators"regexp "regexp"github_com_mwitkow_go_proto_validators "github.com/mwitkow/go-proto-validators"
)// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inffunc (this *InnerMessage) Validate() error {if !(this.SomeInteger > 0) {return github_com_mwitkow_go_proto_validators.FieldError("SomeInteger", fmt.Errorf(`value '%v' must be greater than '0'`, this.SomeInteger))}if !(this.SomeInteger < 100) {return github_com_mwitkow_go_proto_validators.FieldError("SomeInteger", fmt.Errorf(`value '%v' must be less than '100'`, this.SomeInteger))}if !(this.SomeFloat >= 0) {return github_com_mwitkow_go_proto_validators.FieldError("SomeFloat", fmt.Errorf(`value '%v' must be greater than or equal to '0'`, this.SomeFloat))}if !(this.SomeFloat <= 1) {return github_com_mwitkow_go_proto_validators.FieldError("SomeFloat", fmt.Errorf(`value '%v' must be lower than or equal to '1'`, this.SomeFloat))}return nil
}var _regex_OuterMessage_ImportantString = regexp.MustCompile(`^[a-z]{2,5}$`)func (this *OuterMessage) Validate() error {if !_regex_OuterMessage_ImportantString.MatchString(this.ImportantString) {return github_com_mwitkow_go_proto_validators.FieldError("ImportantString", fmt.Errorf(`value '%v' must be a string conforming to regex "^[a-z]{2,5}$"`, this.ImportantString))}if nil == this.Inner {return github_com_mwitkow_go_proto_validators.FieldError("Inner", fmt.Errorf("message must exist"))}if this.Inner != nil {if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.Inner); err != nil {return github_com_mwitkow_go_proto_validators.FieldError("Inner", err)}}return nil
}
里面自动生成了message中属性的验证规则。
1.2 把grpc_validator验证拦截器添加到服务端
package mainimport ("context"grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"grpc_zap "github.com/grpc-ecosystem/go-grpc-middleware/logging/zap"grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"grpc_validator "github.com/grpc-ecosystem/go-grpc-middleware/validator"pb "middleware/proto""middleware/server/middleware/auth""middleware/server/middleware/cred""middleware/server/middleware/recovery""middleware/server/middleware/zap""google.golang.org/grpc""log""net"
)// SimpleService 定义我们的服务
type SimpleService struct{}// Route 实现Route方法
func (s *SimpleService) Route(ctx context.Context, req *pb.InnerMessage) (*pb.OuterMessage, error) {res := pb.OuterMessage{ImportantString: "hello grpc validator",Inner: req,}return &res, nil
}const (// Address 监听地址Address string = ":8000"// Network 网络通信协议Network string = "tcp"
)func main() {// 监听本地端口listener, err := net.Listen(Network, Address)if err != nil {log.Fatalf("net.Listen err: %v", err)}// 新建gRPC服务器实例grpcServer := grpc.NewServer(// TLS+Token认证cred.TLSInterceptor(),grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(// 参数验证grpc_validator.StreamServerInterceptor(),// grpc_zap日志记录grpc_zap.StreamServerInterceptor(zap.ZapInterceptor()),// grpc_auth认证grpc_auth.StreamServerInterceptor(auth.AuthInterceptor),// grpc_recovery恢复grpc_recovery.StreamServerInterceptor(recovery.RecoveryInterceptor()),)),grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(// 参数验证grpc_validator.UnaryServerInterceptor(),// grpc_zap日志记录grpc_zap.UnaryServerInterceptor(zap.ZapInterceptor()),// grpc_auth认证grpc_auth.UnaryServerInterceptor(auth.AuthInterceptor),// grpc_recovery恢复grpc_recovery.UnaryServerInterceptor(recovery.RecoveryInterceptor()),)),)// 在gRPC服务器注册我们的服务pb.RegisterSimpleServer(grpcServer, &SimpleService{})log.Println(Address + " net.Listing with TLS and token...")//用服务器 Serve() 方法以及我们的端口信息区实现阻塞等待,直到进程被杀死或者 Stop() 被调用err = grpcServer.Serve(listener)if err != nil {log.Fatalf("grpcServer.Serve err: %v", err)}
}
[root@zsx middleware]# go run server.go
2023/02/11 21:42:01 :8000 net.Listing with TLS and token...
1.3 客户端
package mainimport ("context""middleware/token"pb "middleware/proto""google.golang.org/grpc""google.golang.org/grpc/credentials""log"
)// Address 连接地址
const Address string = ":8000"var grpcClient pb.SimpleClientfunc main() {//从输入的证书文件中为客户端构造TLS凭证creds, err := credentials.NewClientTLSFromFile("./cert/server/server.pem", "test.example.com")if err != nil {log.Fatalf("Failed to create TLS credentials %v", err)}//构建Tokentoken := auth.Token{Value: "bearer grpc.auth.token",}// 连接服务器conn, err := grpc.Dial(Address, grpc.WithTransportCredentials(creds), grpc.WithPerRPCCredentials(&token))if err != nil {log.Fatalf("net.Connect err: %v", err)}defer conn.Close()// 建立gRPC连接grpcClient = pb.NewSimpleClient(conn)route()
}// route 调用服务端Route方法
func route() {// 创建发送结构体req := pb.InnerMessage{SomeInteger: 99,SomeFloat: 1,}// 调用我们的服务(Route方法)// 同时传入了一个 context.Context ,在有需要时可以让我们改变RPC的行为,比如超时/取消一个正在运行的RPCres, err := grpcClient.Route(context.Background(), &req)if err != nil {log.Fatalf("Call Route err: %v", err)}// 打印返回值log.Println(res)
}
[root@zsx middleware]# go run client.go
2023/02/11 21:44:11 important_string:"hello grpc validator" inner:{some_integer:99 some_float:1}
运行后,当输入不符合要求的数据:
// 创建发送结构体
req := pb.InnerMessage{SomeInteger: 199,SomeFloat: 1,
}
验证失败后,会有以下错误返回:
[root@zsx middleware]# go run client.go
2023/02/11 21:45:25 Call Route err: rpc error: code = InvalidArgument desc = invalid field SomeInteger: value '199' must be less than '100'
exit status 1
# 项目结构
$ tree middleware/
middleware/
├── cert
│ ├── ca.crt
│ ├── ca.csr
│ ├── ca.key
│ ├── ca.srl
│ ├── client
│ │ ├── client.csr
│ │ ├── client.key
│ │ └── client.pem
│ ├── openssl.cnf
│ └── server
│ ├── server.csr
│ ├── server.key
│ └── server.pem
├── client.go
├── go.mod
├── go.sum
├── log
│ └── debug.log
├── proto
│ ├── simple.pb.go
│ └── simple.validator.pb.go
├── server
│ └── middleware
│ ├── auth
│ │ └── auth.go
│ ├── cred
│ │ └── cred.go
│ ├── recovery
│ │ └── recovery.go
│ └── zap
│ └── zap.go
├── server.go
├── simple.proto
├── token
│ └── token.go
└── validator.proto12 directories, 25 files
1.3 其他类型验证规则设置
enum验证:
syntax = "proto3";
package proto;
option go_package = "./proto;enum";import "validator.proto";message SomeMsg {Action do = 1 [(validator.field) = {is_in_enum: true}];
}
enum Action {ALLOW = 0;DENY = 1;CHILL = 2;
}
UUID验证:
syntax = "proto3";
package proto;
option go_package = "./proto;uuid";import "validator.proto";message UUIDMsg {// user_id must be a valid version 4 UUID.string user_id = 1 [(validator.field) = {uuid_ver: 4, string_not_empty: true}];
}
1.4 总结
go-grpc-middleware中grpc_validator集成go-proto-validators,我们只需要在编写proto时设好验证规
则,并把grpc_validator添加到gRPC服务端,就能完成gRPC的数据验证,很简单也很方便。
相关文章:
gRPC之proto数据验证
1、proto数据验证 本篇将介绍grpc_validator,它可以对gRPC数据的输入和输出进行验证。 这里复用上一篇文章的代码。 1.1 创建proto文件,添加验证规则 这里使用第三方插件go-proto-validators 自动生成验证规则。 地址:https://github.co…...
计算机竞赛 题目: 基于深度学习的疲劳驾驶检测 深度学习
文章目录 0 前言1 课题背景2 实现目标3 当前市面上疲劳驾驶检测的方法4 相关数据集5 基于头部姿态的驾驶疲劳检测5.1 如何确定疲劳状态5.2 算法步骤5.3 打瞌睡判断 6 基于CNN与SVM的疲劳检测方法6.1 网络结构6.2 疲劳图像分类训练6.3 训练结果 7 最后 0 前言 🔥 优…...
css--踩坑
1. 子元素的宽高不生效问题 设置flex布局后,子元素的宽高不生效问题。 如果希望子元素的宽高生效,解决方法,给子元素添加如下属性: flex-shrink: 0; flex-shrink: 0;2. 横向滚动(子元素宽度不固定) /* tab…...
C超市商品信息查询系统
一、系统界面介绍 1. 超市商品信息查询系统 1、显示商品信息,包括:商品名称、商品种类(休闲食品、奶品水饮、生鲜水果)、商品价格、商品保质期、商品生产日期; 2、从文件中导入数据、显示、排序、查询的功能。&…...
黑马JVM总结(二十七)
(1)synchronized代码块 synchronized代码块的底层原理,它是给一个对象进行一个加锁操作,它是如何保证如果你出现了synchronized代码块中出现了问题,它需要给这个对象有一个正确的解锁操作呢,加锁解锁是成对…...
软件测试/测试开发丨Python异常处理 学习笔记
点此获取更多相关资料 本文为霍格沃兹测试开发学社学员学习笔记分享 原文链接:https://ceshiren.com/t/topic/27722 异常处理 编写程序时,即使语句或表达式使用了正确的语法,执行时仍可能触发错误。执行时检测到的错误称为异常,大…...
026 - STM32学习笔记 - 液晶屏控制(三) - DMA2D快速绘制矩形、直线
026- STM32学习笔记 - 液晶屏控制(三) - DMA2D快速绘制矩形、直线等 上节直接操作LTDC在先视频上直接显示,我们直接操作显存地址空间中的内容,用来显示图形,但是相对来说,这种方法费时费力,这节…...
【牛客网】OR59 字符串中找出连续最长的数字串
题目 思路 创建两个字符串 temp 和 ret 创建指针i用来遍历字符串通过i遍历字符串,如果遇到数字则将这个数组加到字符串temp中 i,如果遇到字母,则判断temp字符串的长度和ret字符串的长度,如果temp<ret则说明这个字符串不是要的字符串,如果temp>ret则说明此时temp字符串是…...
云原生监控系统Prometheus:基于Prometheus构建智能化监控告警系统
目录 一、理论 1.Promethues简介 2.监控告警系统设计思路 3.Prometheus监控体系 4.Prometheus时间序列数据 5.Prometheus的生态组件 6.Prometheus工作原理 7.Prometheus监控内容 8.部署Prometheus 9.部署Exporters 10.部署Grafana进行展示 二、实验 1.部署Prometh…...
C++ 学习系列 -- std::list
一 std::list 介绍 list 是 c 中的序列式容器,其实现是双向链表,每个元素都有两个指针,分别指向前一个节点与后一个节点 链表与数组都是计算机常用的内存数据结构,与数组连续内存空间不一样的地方在于,链表的空间是不…...
YOLOv8血细胞检测(6):多维协作注意模块MCA | 原创独家创新首发
💡💡💡本文改进:多维协作注意模块MCA,效果秒杀ECA、SRM、CBAM,创新性十足,可直接作为创新点使用。 MCA | 亲测在血细胞检测项目中涨点,map@0.5 从原始0.895提升至0.910 收录专栏: 💡💡💡YOLO医学影像检测:http://t.csdnimg.cn/N4zBP ✨✨✨实战医学影…...
FFmpeg横竖版视频互换背景模糊一键生成
视频处理是现代多媒体应用中常见的需求。其中横竖版视频互换和背景模糊是视频编辑中常见的操作。FFmpeg是一个功能强大的工具,适用于这些任务。 本文将详细介绍如何使用FFmpeg进行横竖版视频互换和背景模糊。 文章目录 操作命令与命令说明横版转竖版竖版转横版背景模糊处理横…...
Java 华为真题-小朋友分班
需求: 题目描述 幼儿园两个班的小朋友在排队时混在了一起,每位小朋友都知道自己是否与前面一位小朋友同班,请你帮忙把同班的小朋友找出来小朋友的编号是整数,与前一位小朋友同班用Y表示,不同班用N表示学生序号范围(0&…...
机器学习必修课 - 编码分类变量 encoding categorical variables
1. 数据预处理和数据集分割 import pandas as pd from sklearn.model_selection import train_test_split导入所需的Python库 !git clone https://github.com/JeffereyWu/Housing-prices-data.git下载数据集 # Read the data X pd.read_csv(/content/Housing-prices-data/t…...
ClickHouse进阶(二十二):clickhouse管理与运维-服务监控
进入正文前,感谢宝子们订阅专题、点赞、评论、收藏!关注IT贫道,获取高质量博客内容! 🏡个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Kerberos安全认证-CSDN博客 📌订阅:拥抱独家专题,你的订阅将点燃我的创作热情! 👍点赞:赞同优秀创作,你的点赞是对我创…...
Hadoop使用hdfs指令查看hdfs目录的根目录显示被拒
背景 分布式部署hadoop,服务机只有namenode节点,主机包含其他所有节点 主机关机后,没有停止所有节点,导致服务机namenode继续保存 再次开启主机hadoop,使用hdfs查看hdfs根目录的时候显示访问被拒 解决方案 1.主机再次开启hadoop并继续执行关闭 2.服务器再次开启hadoop并继…...
[Mac] 安装paddle-pipelines出现 ERROR: Failed building wheel for lmdb
今天在mac换了新系统,然后重新安装paddle-piplines的时候出现了下面的问题: xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrunerror: comma…...
LED灯亮灭
.text .global _start _start: 设置GPIO寄存器的时钟使能 RCC_MP_AHB4ENSETR[4]->1 0x50000a28LDR R0,0x50000A28LDR R1,[R0] 从R0为起始地址的4个字节数据取出放入R1中ORR R1,R1,#(0x1<<4) 第四位设置为1STR R1,[R0] 写回LDR R0,0x5000…...
Acwing.143 最大异或对(trie树)
题目 在给定的N个整数A1,A2 . …Ax中选出两个进行xor(异或)运算,得到的结果最大是多少? 输入格式 第一行输入一个整数N。 第二行输入N个整数A1~AN。 输出格式 输出一个整数表示答案。 数据范围 1 ≤N ≤105,0≤A<231 输入样例: 3 1 2 3输出样…...
day10.8ubentu流水灯
流水灯 .text .global _start _start: 1.设置GPIOE寄存器的时钟使能 RCC_MP_AHB4ENSETR[4]->1 0x50000a28LDR R0,0X50000A28LDR R1,[R0] 从r0为起始地址的4字节数据取出放在R1ORR R1,R1,#(0x1<<4) 第4位设置为1STR R1,[R0] 写回2.设置PE10管脚为输出模式 G…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
CVPR2025重磅突破:AnomalyAny框架实现单样本生成逼真异常数据,破解视觉检测瓶颈!
本文介绍了一种名为AnomalyAny的创新框架,该方法利用Stable Diffusion的强大生成能力,仅需单个正常样本和文本描述,即可生成逼真且多样化的异常样本,有效解决了视觉异常检测中异常样本稀缺的难题,为工业质检、医疗影像…...
HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散
前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说,在叠衣服的过程中,我会带着团队对比各种模型、方法、策略,毕竟针对各个场景始终寻找更优的解决方案,是我个人和我司「七月在线」的职责之一 且个人认为,…...
