grpc的验证器

简介

在使用grpc库时候 ,很多时候我们需要对反序列化的参数进行校验,代码中有很多参数校验的代码,如果手动实现,会非常繁琐,对于grpc来说,在定义proto的时候使用直接定义参数的限制规则是一种更合理、更优雅的方式,插件protoc-gen-validate就是来帮助我们实现这一功能的,使用 proto-gen-validate 生成后的代码进行参数校验,我们可以通过在 proto 中编写参数校验规则,然后生成代码,通过中间件自动的进行校验。

安装

go install github.com/envoyproxy/protoc-gen-validate@latest

规则示例

下面为大家列举几种常用类型的参数校验示例,更多的示例可以在 proto-gen-validate 文档中查看。

数字类型

// 参数必须大于 0
int64 id = 1 [(validate.rules).int64 = {gt: 0}];
// 参数必须在 0 到 120 之间
int32 age = 2 [(validate.rules).int64 = {gt:0, lte: 120}];
// 参数是 1 或 2 或 3
uint32 code = 3 [(validate.rules).uint32 = {in: [1,2,3]}];
// 参数不能是 0 或 99.99
float score = 1 [(validate.rules).float = {not_in: [0, 99.99]}];

布尔类型

// 参数必须为 true
bool state = 5 [(validate.rules).bool.const = true];
// 参数必须为 false
bool state = 5 [(validate.rules).bool.const = false];

文本类型

// 参数必须为 /hello
string path = 6 [(validate.rules).string.const = "/hello"];
// 参数文本长度必须为 11
string phone = 7 [(validate.rules).string.len = 11];
// 参数文本长度不能小于 10 个字符
string explain = 8 [(validate.rules).string.min_len =  10];
// 参数文本长度不能小于 1 个字符并且不能大于 10 个字符
string name = 9 [(validate.rules).string = {min_len: 1, max_len: 10}];
// 参数文本使用正则匹配,匹配必须是非空的不区分大小写的十六进制字符串
string card = 10 [(validate.rules).string.pattern = "(?i)^[0-9a-f]+$"];
// 参数文本必须是 email 格式
string email = 11 [(validate.rules).string.email = true];

消息体

// 参数为必填项
Info info = 11 [(validate.rules).message.required = true];
message Info {string address = 1;
}

定义proto文件

syntax = "proto3";package examplepb;
option go_package = "./example";
import "validate/validate.proto";message Person {uint64 id    = 1 [(validate.rules).uint64.gt = 999];string email = 2 [(validate.rules).string.email = true];string name  = 3 [(validate.rules).string = {pattern:   "^[^[0-9]A-Za-z]+( [^[0-9]A-Za-z]+)*$",max_bytes: 256,}];Location home = 4 [(validate.rules).message.required = true];
// 参数必须大于 0
int64 ids = 5 [(validate.rules).int64 = {gt: 0}];
// 参数必须在 0 到 120 之间
int32 age = 6 [(validate.rules).int32 = {gt:0, lte: 120}];
// 参数是 1 或 2 或 3
uint32 code = 7 [(validate.rules).uint32 = {in: [1,2,3]}];
// 参数不能是 0 或 99.99
float score = 8 [(validate.rules).float = {not_in: [0, 99.99]}];message Location {double lat = 1 [(validate.rules).double = { gte: -90,  lte: 90 }];double lng = 2 [(validate.rules).double = { gte: -180, lte: 180 }];}
}

使用命令生成go文件

% protoc \                                            -I . \--plugin=$GOPATH/bin/protoc-gen-validate \-I ${GOPATH}/pkg/mod/github.com/envoyproxy/protoc-gen-validate@v0.1.0/ \--go_out=":./generated" \--validate_out="lang=go:./generated" \example.proto

相应的,我们得到了两个文件

learn/pgv/generated/example/example.pb.go

// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
//   protoc-gen-go v1.28.1
//   protoc        v3.19.4
// source: example.protopackage exampleimport (_ "github.com/envoyproxy/protoc-gen-validate/validate"protoreflect "google.golang.org/protobuf/reflect/protoreflect"protoimpl "google.golang.org/protobuf/runtime/protoimpl"reflect "reflect"sync "sync"
)const (// Verify that this generated code is sufficiently up-to-date._ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)// Verify that runtime/protoimpl is sufficiently up-to-date._ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)type Person struct {state         protoimpl.MessageStatesizeCache     protoimpl.SizeCacheunknownFields protoimpl.UnknownFieldsId    uint64           `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`Email string           `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"`Name  string           `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"`Home  *Person_Location `protobuf:"bytes,4,opt,name=home,proto3" json:"home,omitempty"`// 参数必须大于 0Ids int64 `protobuf:"varint,5,opt,name=ids,proto3" json:"ids,omitempty"`// 参数必须在 0 到 120 之间Age int32 `protobuf:"varint,6,opt,name=age,proto3" json:"age,omitempty"`// 参数是 1 或 2 或 3Code uint32 `protobuf:"varint,7,opt,name=code,proto3" json:"code,omitempty"`// 参数不能是 0 或 99.99Score float32 `protobuf:"fixed32,8,opt,name=score,proto3" json:"score,omitempty"`
}func (x *Person) Reset() {*x = Person{}if protoimpl.UnsafeEnabled {mi := &file_example_proto_msgTypes[0]ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))ms.StoreMessageInfo(mi)}
}func (x *Person) String() string {return protoimpl.X.MessageStringOf(x)
}func (*Person) ProtoMessage() {}func (x *Person) ProtoReflect() protoreflect.Message {mi := &file_example_proto_msgTypes[0]if protoimpl.UnsafeEnabled && x != nil {ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))if ms.LoadMessageInfo() == nil {ms.StoreMessageInfo(mi)}return ms}return mi.MessageOf(x)
}// Deprecated: Use Person.ProtoReflect.Descriptor instead.
func (*Person) Descriptor() ([]byte, []int) {return file_example_proto_rawDescGZIP(), []int{0}
}func (x *Person) GetId() uint64 {if x != nil {return x.Id}return 0
}func (x *Person) GetEmail() string {if x != nil {return x.Email}return ""
}func (x *Person) GetName() string {if x != nil {return x.Name}return ""
}func (x *Person) GetHome() *Person_Location {if x != nil {return x.Home}return nil
}func (x *Person) GetIds() int64 {if x != nil {return x.Ids}return 0
}func (x *Person) GetAge() int32 {if x != nil {return x.Age}return 0
}func (x *Person) GetCode() uint32 {if x != nil {return x.Code}return 0
}func (x *Person) GetScore() float32 {if x != nil {return x.Score}return 0
}type Person_Location struct {state         protoimpl.MessageStatesizeCache     protoimpl.SizeCacheunknownFields protoimpl.UnknownFieldsLat float64 `protobuf:"fixed64,1,opt,name=lat,proto3" json:"lat,omitempty"`Lng float64 `protobuf:"fixed64,2,opt,name=lng,proto3" json:"lng,omitempty"`
}func (x *Person_Location) Reset() {*x = Person_Location{}if protoimpl.UnsafeEnabled {mi := &file_example_proto_msgTypes[1]ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))ms.StoreMessageInfo(mi)}
}func (x *Person_Location) String() string {return protoimpl.X.MessageStringOf(x)
}func (*Person_Location) ProtoMessage() {}func (x *Person_Location) ProtoReflect() protoreflect.Message {mi := &file_example_proto_msgTypes[1]if protoimpl.UnsafeEnabled && x != nil {ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))if ms.LoadMessageInfo() == nil {ms.StoreMessageInfo(mi)}return ms}return mi.MessageOf(x)
}// Deprecated: Use Person_Location.ProtoReflect.Descriptor instead.
func (*Person_Location) Descriptor() ([]byte, []int) {return file_example_proto_rawDescGZIP(), []int{0, 0}
}func (x *Person_Location) GetLat() float64 {if x != nil {return x.Lat}return 0
}func (x *Person_Location) GetLng() float64 {if x != nil {return x.Lng}return 0
}var File_example_proto protoreflect.FileDescriptorvar file_example_proto_rawDesc = []byte{0x0a, 0x0d, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,0x09, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x70, 0x62, 0x1a, 0x17, 0x76, 0x61, 0x6c, 0x69,0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72,0x6f, 0x74, 0x6f, 0x22, 0xb5, 0x03, 0x0a, 0x06, 0x50, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x12, 0x1a,0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x0a, 0xba, 0xe9, 0xc0, 0x03,0x05, 0x32, 0x03, 0x20, 0xe7, 0x07, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x05, 0x65, 0x6d,0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x09, 0xba, 0xe9, 0xc0, 0x03, 0x04,0x72, 0x02, 0x60, 0x01, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x44, 0x0a, 0x04, 0x6e,0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x30, 0xba, 0xe9, 0xc0, 0x03, 0x2b,0x72, 0x29, 0x28, 0x80, 0x02, 0x32, 0x24, 0x5e, 0x5b, 0x5e, 0x5b, 0x30, 0x2d, 0x39, 0x5d, 0x41,0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x5d, 0x2b, 0x28, 0x20, 0x5b, 0x5e, 0x5b, 0x30, 0x2d, 0x39, 0x5d,0x41, 0x2d, 0x5a, 0x61, 0x2d, 0x7a, 0x5d, 0x2b, 0x29, 0x2a, 0x24, 0x52, 0x04, 0x6e, 0x61, 0x6d,0x65, 0x12, 0x3a, 0x0a, 0x04, 0x68, 0x6f, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32,0x1a, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x70, 0x62, 0x2e, 0x50, 0x65, 0x72, 0x73,0x6f, 0x6e, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0a, 0xba, 0xe9, 0xc0,0x03, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x04, 0x68, 0x6f, 0x6d, 0x65, 0x12, 0x1b, 0x0a,0x03, 0x69, 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x42, 0x09, 0xba, 0xe9, 0xc0, 0x03,0x04, 0x22, 0x02, 0x20, 0x00, 0x52, 0x03, 0x69, 0x64, 0x73, 0x12, 0x1d, 0x0a, 0x03, 0x61, 0x67,0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x42, 0x0b, 0xba, 0xe9, 0xc0, 0x03, 0x06, 0x1a, 0x04,0x18, 0x78, 0x20, 0x00, 0x52, 0x03, 0x61, 0x67, 0x65, 0x12, 0x21, 0x0a, 0x04, 0x63, 0x6f, 0x64,0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x42, 0x0d, 0xba, 0xe9, 0xc0, 0x03, 0x08, 0x2a, 0x06,0x30, 0x01, 0x30, 0x02, 0x30, 0x03, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x27, 0x0a, 0x05,0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x02, 0x42, 0x11, 0xba, 0xe9, 0xc0,0x03, 0x0c, 0x0a, 0x0a, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x3d, 0xe1, 0xfa, 0xc7, 0x42, 0x52, 0x05,0x73, 0x63, 0x6f, 0x72, 0x65, 0x1a, 0x64, 0x0a, 0x08, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f,0x6e, 0x12, 0x2b, 0x0a, 0x03, 0x6c, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x42, 0x19,0xba, 0xe9, 0xc0, 0x03, 0x14, 0x12, 0x12, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x56, 0x40,0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x56, 0xc0, 0x52, 0x03, 0x6c, 0x61, 0x74, 0x12, 0x2b,0x0a, 0x03, 0x6c, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x42, 0x19, 0xba, 0xe9, 0xc0,0x03, 0x14, 0x12, 0x12, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x66, 0x40, 0x29, 0x00, 0x00,0x00, 0x00, 0x00, 0x80, 0x66, 0xc0, 0x52, 0x03, 0x6c, 0x6e, 0x67, 0x42, 0x0b, 0x5a, 0x09, 0x2e,0x2f, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}var (file_example_proto_rawDescOnce sync.Oncefile_example_proto_rawDescData = file_example_proto_rawDesc
)func file_example_proto_rawDescGZIP() []byte {file_example_proto_rawDescOnce.Do(func() {file_example_proto_rawDescData = protoimpl.X.CompressGZIP(file_example_proto_rawDescData)})return file_example_proto_rawDescData
}var file_example_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_example_proto_goTypes = []interface{}{(*Person)(nil),          // 0: examplepb.Person(*Person_Location)(nil), // 1: examplepb.Person.Location
}
var file_example_proto_depIdxs = []int32{1, // 0: examplepb.Person.home:type_name -> examplepb.Person.Location1, // [1:1] is the sub-list for method output_type1, // [1:1] is the sub-list for method input_type1, // [1:1] is the sub-list for extension type_name1, // [1:1] is the sub-list for extension extendee0, // [0:1] is the sub-list for field type_name
}func init() { file_example_proto_init() }
func file_example_proto_init() {if File_example_proto != nil {return}if !protoimpl.UnsafeEnabled {file_example_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {switch v := v.(*Person); i {case 0:return &v.statecase 1:return &v.sizeCachecase 2:return &v.unknownFieldsdefault:return nil}}file_example_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {switch v := v.(*Person_Location); i {case 0:return &v.statecase 1:return &v.sizeCachecase 2:return &v.unknownFieldsdefault:return nil}}}type x struct{}out := protoimpl.TypeBuilder{File: protoimpl.DescBuilder{GoPackagePath: reflect.TypeOf(x{}).PkgPath(),RawDescriptor: file_example_proto_rawDesc,NumEnums:      0,NumMessages:   2,NumExtensions: 0,NumServices:   0,},GoTypes:           file_example_proto_goTypes,DependencyIndexes: file_example_proto_depIdxs,MessageInfos:      file_example_proto_msgTypes,}.Build()File_example_proto = out.Filefile_example_proto_rawDesc = nilfile_example_proto_goTypes = nilfile_example_proto_depIdxs = nil
}

learn/pgv/generated/example/example.pb.validate.go

// Code generated by protoc-gen-validate. DO NOT EDIT.
// source: example.protopackage exampleimport ("bytes""errors""fmt""net""net/mail""net/url""regexp""strings""time""unicode/utf8""github.com/golang/protobuf/ptypes"
)// ensure the imports are used
var (_ = bytes.MinRead_ = errors.New("")_ = fmt.Print_ = utf8.UTFMax_ = (*regexp.Regexp)(nil)_ = (*strings.Reader)(nil)_ = net.IPv4len_ = time.Duration(0)_ = (*url.URL)(nil)_ = (*mail.Address)(nil)_ = ptypes.DynamicAny{}
)// Validate checks the field values on Person with the rules defined in the
// proto definition for this message. If any rules are violated, an error is returned.
func (m *Person) Validate() error {if m == nil {return nil}if m.GetId() <= 999 {return PersonValidationError{field:  "Id",reason: "value must be greater than 999",}}if err := m._validateEmail(m.GetEmail()); err != nil {return PersonValidationError{field:  "Email",reason: "value must be a valid email address",cause:  err,}}if len(m.GetName()) > 256 {return PersonValidationError{field:  "Name",reason: "value length must be at most 256 bytes",}}if !_Person_Name_Pattern.MatchString(m.GetName()) {return PersonValidationError{field:  "Name",reason: "value does not match regex pattern \"^[^[0-9]A-Za-z]+( [^[0-9]A-Za-z]+)*$\"",}}if m.GetHome() == nil {return PersonValidationError{field:  "Home",reason: "value is required",}}if v, ok := interface{}(m.GetHome()).(interface{ Validate() error }); ok {if err := v.Validate(); err != nil {return PersonValidationError{field:  "Home",reason: "embedded message failed validation",cause:  err,}}}if m.GetIds() <= 0 {return PersonValidationError{field:  "Ids",reason: "value must be greater than 0",}}if val := m.GetAge(); val <= 0 || val > 120 {return PersonValidationError{field:  "Age",reason: "value must be inside range (0, 120]",}}if _, ok := _Person_Code_InLookup[m.GetCode()]; !ok {return PersonValidationError{field:  "Code",reason: "value must be in list [1 2 3]",}}if _, ok := _Person_Score_NotInLookup[m.GetScore()]; ok {return PersonValidationError{field:  "Score",reason: "value must not be in list [0 99.99]",}}return nil
}func (m *Person) _validateHostname(host string) error {s := strings.ToLower(strings.TrimSuffix(host, "."))if len(host) > 253 {return errors.New("hostname cannot exceed 253 characters")}for _, part := range strings.Split(s, ".") {if l := len(part); l == 0 || l > 63 {return errors.New("hostname part must be non-empty and cannot exceed 63 characters")}if part[0] == '-' {return errors.New("hostname parts cannot begin with hyphens")}if part[len(part)-1] == '-' {return errors.New("hostname parts cannot end with hyphens")}for _, r := range part {if (r < 'a' || r > 'z') && (r < '0' || r > '9') && r != '-' {return fmt.Errorf("hostname parts can only contain alphanumeric characters or hyphens, got %q", string(r))}}}return nil
}func (m *Person) _validateEmail(addr string) error {a, err := mail.ParseAddress(addr)if err != nil {return err}addr = a.Addressif len(addr) > 254 {return errors.New("email addresses cannot exceed 254 characters")}parts := strings.SplitN(addr, "@", 2)if len(parts[0]) > 64 {return errors.New("email address local phrase cannot exceed 64 characters")}return m._validateHostname(parts[1])
}// PersonValidationError is the validation error returned by Person.Validate if
// the designated constraints aren't met.
type PersonValidationError struct {field  stringreason stringcause  errorkey    bool
}// Field function returns field value.
func (e PersonValidationError) Field() string { return e.field }// Reason function returns reason value.
func (e PersonValidationError) Reason() string { return e.reason }// Cause function returns cause value.
func (e PersonValidationError) Cause() error { return e.cause }// Key function returns key value.
func (e PersonValidationError) Key() bool { return e.key }// ErrorName returns error name.
func (e PersonValidationError) ErrorName() string { return "PersonValidationError" }// Error satisfies the builtin error interface
func (e PersonValidationError) Error() string {cause := ""if e.cause != nil {cause = fmt.Sprintf(" | caused by: %v", e.cause)}key := ""if e.key {key = "key for "}return fmt.Sprintf("invalid %sPerson.%s: %s%s",key,e.field,e.reason,cause)
}var _ error = PersonValidationError{}var _ interface {Field() stringReason() stringKey() boolCause() errorErrorName() string
} = PersonValidationError{}var _Person_Name_Pattern = regexp.MustCompile("^[^[0-9]A-Za-z]+( [^[0-9]A-Za-z]+)*$")var _Person_Code_InLookup = map[uint32]struct{}{1: {},2: {},3: {},
}var _Person_Score_NotInLookup = map[float32]struct{}{0:     {},99.99: {},
}// Validate checks the field values on Person_Location with the rules defined
// in the proto definition for this message. If any rules are violated, an
// error is returned.
func (m *Person_Location) Validate() error {if m == nil {return nil}if val := m.GetLat(); val < -90 || val > 90 {return Person_LocationValidationError{field:  "Lat",reason: "value must be inside range [-90, 90]",}}if val := m.GetLng(); val < -180 || val > 180 {return Person_LocationValidationError{field:  "Lng",reason: "value must be inside range [-180, 180]",}}return nil
}// Person_LocationValidationError is the validation error returned by
// Person_Location.Validate if the designated constraints aren't met.
type Person_LocationValidationError struct {field  stringreason stringcause  errorkey    bool
}// Field function returns field value.
func (e Person_LocationValidationError) Field() string { return e.field }// Reason function returns reason value.
func (e Person_LocationValidationError) Reason() string { return e.reason }// Cause function returns cause value.
func (e Person_LocationValidationError) Cause() error { return e.cause }// Key function returns key value.
func (e Person_LocationValidationError) Key() bool { return e.key }// ErrorName returns error name.
func (e Person_LocationValidationError) ErrorName() string { return "Person_LocationValidationError" }// Error satisfies the builtin error interface
func (e Person_LocationValidationError) Error() string {cause := ""if e.cause != nil {cause = fmt.Sprintf(" | caused by: %v", e.cause)}key := ""if e.key {key = "key for "}return fmt.Sprintf("invalid %sPerson_Location.%s: %s%s",key,e.field,e.reason,cause)
}var _ error = Person_LocationValidationError{}var _ interface {Field() stringReason() stringKey() boolCause() errorErrorName() string
} = Person_LocationValidationError{}

使用示例

然后我们就可以通过Validate方法来进行验证

结合之前博客介绍的拦截器进行校验:

// ValidateAll 对应 protoc-gen-validate 生成的 *.pb.validate.go 中的代码
type Validator interface {ValidateAll() error
}func ServerValidationUnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {if r, ok := req.(Validator); ok {if err := r.ValidateAll(); err != nil {return nil, status.Error(codes.InvalidArgument, err.Error())}}return handler(ctx, req)
}

然后在拦截器中引入我们定义的插件:

s := grpc.NewServer(grpc.ChainUnaryInterceptor(grpc_opentracing.UnaryServerInterceptor(grpc_opentracing.WithTracer(opentracing.GlobalTracer()),),ServerValidationUnaryInterceptor,),
)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/737124.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

人工智能迷惑行为大赏

近年来&#xff0c;随着人工智能技术的不断发展和应用&#xff0c;我们在日常生活中越来越多地接触到各种智能设备和程序。然而&#xff0c;随之而来的是一些令人瞠目结舌的人工智能迷惑行为&#xff0c;让人们对这一新兴技术产生了更多的好奇和思考。 在人工智能迷惑行为大赏…

「2024」不再内卷,让AI低代码带你玩点新花样!

这一次&#xff0c;OpenAI的不败传奇终于被撼动。 其强劲对手Anthropic于北京时间&#xff08;2024年3月4日&#xff09;震撼发布新一代AI大模型系列——Claude 3。 根据官方性能测试数据的展示&#xff0c;Claude 3系列模型在各项表现上均超越GPT-4&#xff0c;荣登全球最强…

基于yolov7与arduino的眼睛跟随模块

基于yolov7与arduino的眼睛跟随模块 整个模块的介绍摄像模块图片传输模块图像检测模块控制模块动力模块 整个模块的介绍 我们首先需要一个图片收集的模块来对当前的图片进行收集然后将图片传至服务端对图片中的眼睛利用YOLO进行检测最后将数据传至arduino使其控制动力模块来进…

小额投资者如何投资黄金?

天下熙熙&#xff0c;皆为利来。近来&#xff0c;无论是黄金还是英伟达&#xff0c;各有各的火爆&#xff0c;这引起了广泛新手投资者的关注&#xff0c;许多小白玩家也跃跃欲试。事实上&#xff0c;并非入场越久越可被称为成熟的投资者&#xff0c;投资并不限定于特定的资金规…

导出谷歌浏览器收藏的网页,并查看网页保存的登录密码

导出谷歌浏览器&#xff08;Chrome&#xff09;收藏的网页&#xff08;书签&#xff09;&#xff1a; 打开谷歌浏览器。在浏览器右上角找到并点击三个垂直排列的小点&#xff08;或称汉堡菜单&#xff09;以打开主菜单。在下拉菜单中选择“书签” > “书签管理器”。在书签…

ms office学习记录9:Excel㈢

懒癌犯了拖了好几天的学习进度&#xff0c;下决心暂别娱乐&#xff0c;还是先把两门计算机二级过完&#xff01;把该做的做到位了&#xff0c;剩下的考完再说。 以下为今日excel知识点总结&#xff1a; 条件格式的本质&#xff1a;把符合条件的信息设置特殊格式突出显示 双击…

不被折叠的朋友圈这样发

现在朋友圈折叠有两种&#xff1a;第一种是文案折叠&#xff0c;第二种是整条折叠。 说下现象、原因和解决办法。 01 文案折叠 现象&#xff1a;只有文字被折叠成一行&#xff0c;图片视频还能看到&#xff0c;其它内容看不到。 折叠原因&#xff1a;发布的内容在自己朋友圈…

Oracle数据恢复—Oracle被误删表不要慌!掌握如何恢复Oracle表!

Oracle数据库数据恢复环境&#xff1a; 北京某国企客户Oracle 11g R2数据库误truncate table CM_CHECK_ITEM_HIS&#xff0c;表数据丢失&#xff0c;业务查询到该表时报错&#xff0c;数据库的备份不可用&#xff0c;无法查询表数据。 Oracle数据库执行Truncate命令的原理&am…

快上车:人工智能的分类;机器学习的几种方式;神经网络神奇在哪里?

摘要&#xff1a; 本文深入探讨了人工智能的分类、机器学习的几种方式以及神经网络的神奇之处&#xff0c;为读者提供了丰富的技术知识和实践经验。 阅读时长&#xff1a;约60分钟 关键词&#xff1a;人工智能, 机器学习, 神经网络 引言 背景介绍 随着计算机技术的飞速发展…

SSM整合项目(使用Vue3 + Element-Plus创建项目基础页面)

1.配置Vue启动端口 1.修改vue.config.js const {defineConfig} require(vue/cli-service) module.exports defineConfig({transpileDependencies: true }) module.exports {devServer: {port: 9999 //启动端口} }2.启动 2.安装Element Plus 命令行输入 npm install eleme…

微信小程序购物/超市/餐饮/酒店商城开发搭建过程和需求

1. 商城开发的基本框架 a. 用户界面&#xff08;Frontend&#xff09; 页面设计&#xff1a;包括首页、商品列表、商品详情、购物车、下单界面、用户中心等。交云设计&#xff1a;如何让用户操作更加流畅&#xff0c;包括搜索、筛选、排序等功能的实现。响应式设计&#xff1…

python中怎样把一个文件夹中所有的*.ts文件拼接为.MP4文件?

你可以使用ffmpeg库来实现将一个文件夹中的所有*.ts文件拼接为一个.MP4文件。 首先&#xff0c;你需要确保已经安装了ffmpeg。然后&#xff0c;可以使用以下代码实现&#xff1a; import osdef concat_videos(input_folder, output_file):# 获取文件夹中所有的.ts文件ts_file…

群晖Synology Office本地文件如何分享给同事远程协作编辑【内网穿透】

文章目录 本教程解决的问题是&#xff1a;1. 本地环境配置2. 制作本地分享链接3. 制作公网访问链接4. 公网ip地址访问您的分享相册5. 制作固定公网访问链接 本教程解决的问题是&#xff1a; 1.Word&#xff0c;PPT&#xff0c;Excel等重要文件存在本地环境&#xff0c;如何在编…

访问一次网站的全过程

目录 流程图&#xff1a; 一、应用层开始 1. 在浏览器输入https://www.baidu.com 2. DNS获取IP地址 3. 根据HTTP协议生成HTTP请求报文 应用层结束 二、传输层开始 4. TCP三次握手 传输层结束 三、网络层开始 5. IP寻址 6. ARP协议获取MAC地址 网络层结束 四、数据…

CSS 02

1.复合选择器 &#xff08;1.1&#xff09;后代选择器 代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0&q…

ICLR 2024 | Meta AI提出ViT寄存器结构,巧妙消除大型ViT中的伪影以提高性能

论文题目&#xff1a;Vision Transformers Need Registers 论文链接&#xff1a;https://arxiv.org/abs/2309.16588 视觉Transformer&#xff08;ViT&#xff09;目前已替代CNN成为研究者们首选的视觉表示backbone&#xff0c;尤其是一些基于监督学习或自监督学习预训练的ViT&a…

【Java EE】线程安全的集合类

目录 &#x1f334;多线程环境使用 ArrayList&#x1f38d;多线程环境使⽤队列&#x1f340;多线程环境使⽤哈希表&#x1f338; Hashtable&#x1f338;ConcurrentHashMap ⭕相关面试题&#x1f525;其他常⻅问题 原来的集合类, 大部分都不是线程安全的. Vector, Stack, HashT…

小波散射网络及其应用

1989 年,Mallat 首次提出将基于小波变换的特征提取方法用于模式识别分类中[40],若干 年后,Mallat 又提出了基于小波变换的小波散射网络这一新型网络结构,该网络主要是基于 小波变换的散射算子(Scattering operators)[41],能提取出原始信息在弹性形变、仿射变换中 的不变…

智慧城市的定义是什么?

智慧城市&#xff1a;就是运用信息和通信技术手段感测、分析、整合城市运行核心系统的各项关键信息&#xff0c;从而对包括民生、环保、公共安全、城市服务、工商业活动在内的各种需求做出智能响应。其实质是利用**的信息技术&#xff0c;实现城市智慧式管理和运行&#xff0c;…

【小白必看】永久提升代码/算法能力的6个编程习惯(推荐阅读)

前言 编程和算法能力是当代工程师的核心竞争力之一。想要快速提升自己的编程和算法能力&#xff0c;却苦于没有合适的方法&#xff1f;别担心&#xff0c;今天我将与你分享6个高效编程习惯&#xff0c;让你轻松提升编程和算法能力&#xff01; 大家好&#xff0c;我是Erik&am…