gRPC之proto语法

本文描述如何使用proto3语法去构造你的数据结构,对官方文档不完全译文,只是摘出本人需要的部分来简单翻译官网地址,如果你无法进入官网链接请自行"跳墙"-_-.

目录
  • 1 定义消息类型
    • 1.1 指定字段类型
    • 1.2 分配标量
    • 1.3 指定属性规则
    • 1.4 添加更多的消息类型
    • 1.5 添加注释
    • 1.6 保留属性
  • 2 数据类型
  • 3 默认值
  • 4 枚举
  • 5 引用其他的消息类型
    • 5.1 导入其他proto中定义的消息
  • 6 内嵌类型
  • 8 包
  • 9 服务定义
  • 10 选项

1.定义消息类型

让我们先看一个 proto3 的查找请求参数的消息格式的例子,这个请求参数例子模仿分页查找请求,他有一个请求参数字符串,有一个当前页的参数还有一个每页返回数据大小的参数,proto文件内容如下:

syntax = "proto3";

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}
复制代码
  • 第一行的含义是限定该文件使用的是proto3的语法,如果没有 syntax = "proto3";

  • SearchRequest定义有三个承载消息的属性,每一个被定义在SearchRequest消息体中的字段,都是由数据类型和属性名称组成。

1.1 指定字段类型

在上面的例子中,所有的属性都是标量,两个整型(page_number、result_per_page)和一个字符串(query),你还可以在指定复合类型,包括枚举类型或者其他的消息类型。

1.2 分配标量

就像所看见的一样,每一个被定义在消息中的字段都会被分配给一个唯一的标量,这些标量用于标识你定义在二进制消息格式中的属性,标量一旦被定义就不允许在使用过程中再次被改变。标量的值在1~15的这个范围里占一个字节编码(详情请参看 谷歌的 Protocol Buffer Encoding )。

1.3 指定属性规则

消息属性规则如下:

  • singular: 一个正确的消息可以有零个或者多个这样的消息属性(但是不要超过一个).
  • repeated: 这个属性可以在一个正确的消息格式中重复任意次数(包括零次),
    在proto3中,标量数字类型的重复字段默认使用压缩编码

1.4 添加更多的消息类型

在一个proto文件中可以定义多个消息类型,你可以在一个文件中定义一些相关的消息类型,上面的例子proto文件中只有一个请求查找的消息类型,现在可以为他多添加一个响应的消息类型,具体如下:

syntax = "proto3";

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
}

message SearchResponse {
    ....
}
复制代码

1.5 添加注释

proto文件中的注释使用的是c/c++中的单行注释 // 语法风格。
如下:

message SearchRequest {
  string query = 1;
  int32 page_number = 2;  // 当前页数
  int32 result_per_page = 3;  // 每页数据返回的数据量
复制代码

1.6 保留属性

为了避免在加载相同的.proto的旧版本,包括数据损坏,隐含的错误等,这可能会导致严重的问题的方法是指定删除的字段的字段标签(和/或名称,也可能导致JSON序列化的问题)被保留。 如果将来的用户尝试使用这些字段标识符,协议缓冲区编译器将会报错。


保留字段的使用例子:

message Foo {
  reserved 2;
  reserved "foo", "bar";
}
复制代码

上述例子定义保留属性为"foo", "bar",定义保留属性位置为2,即在2这个位置上不可以定义属性,如:string name=2;是不允许的,编译器在编译proto文件的时候如果发现,2这个位置上有属性被定义则会报错。

2 数据类型



一个信息标量具有如下表格所示的数据类型,下表主要是对.proto文件的值类型和java的值类型的对照表

.proto TypeJava Type
doubledouble
floatfloat
int32int
int64long
uint32int
uint64long
sint32int
sint64long
fixed32int
fixed64long
sfixed32int
sfixed64long
boolboolean
stringString
bytesByteString

详情参看官方文档

3 默认值

当proto消息被解析成具体的语言的时候,如果消息编码没包含特定的元素,则消息对象中的属性会被设置默认值,这些默认值具体如下:

  • string类型,默认值是空字符串,注意不是null
  • bytes类型,默认值是空bytes
  • bool类型,默认值是false
  • 数字类型,默认值是0
  • 枚举类型,默认值是第一个枚举值,即0
  • repeated修饰的属性,默认值是空(在相对应的编程语言中通常是一个空的list).

4 枚举

proto允许你在定义的消息类型的时候定义枚举类型,如下例,在消息类型中定义并使用枚举类型:

message SearchRequest {
  string query = 1;
  int32 page_number = 2;
  int32 result_per_page = 3;
  enum Corpus {
    UNIVERSAL = 0;
    WEB = 1;
    IMAGES = 2;
    LOCAL = 3;
    NEWS = 4;
    PRODUCTS = 5;
    VIDEO = 6;
  }
  Corpus corpus = 4;
}
复制代码

如上例中所示,Corpus枚举类型的第一个枚举值是0,每一个枚举值定义都会与一个常量映射,而这些常量的第一个常量值必须为0,原因如下:

  • 必须有一个0作为值,以至于我们可是使用0作为默认值
  • 第一个元素的值取0,用于与第一个元素枚举值作为默认值的proto2语义兼容


    枚举类型允许你定义别名,别名的作用是分配不中的标量,使用相同的常量值,使用别名只需要在定义枚举类型的第一行中添加allow_alias选项,并将值设置为true即可,如果没有设置该值就是用别名,在编译的时候会报错。


    官网例子如下:
enum EnumAllowingAlias {
  option allow_alias = true;
  UNKNOWN = 0;
  STARTED = 1;
  RUNNING = 1;
}
enum EnumNotAllowingAlias {
  UNKNOWN = 0;
  STARTED = 1;
  //如果解除这个注释编译器在编译该proto文的时候会报错
  // RUNNING = 1;  
}
复制代码

proto支持的枚举值的范围是32位的整形,即Java 中的int类型,其他请参看官网。

5 引用其他的消息类型

你可以在定义消息类型的时候饮用其他已经定义好的消息类型作为新消息类型的属性,官网例子如下:

message SearchResponse {
  repeated Result results = 1;
}

message Result {
  string url = 1;
  string title = 2;
  repeated string snippets = 3;
}
复制代码

在上面的消息例子中,SearchResponse这个响应消息类型的属性results,返回的是一个Result类型的消息列表。

5.1 导入其他proto中定义的消息

在上面的例子中,Result和SearchResponse消息类型被定义在同一个.proto文件中,如果把他们分成两个文件定义,应该如何引用呢?


proto中为我们提供了import 关键字用于引入不同.proto文件中的消息类型,你可以在你的.proto文件的顶部加入如下语句因为其他.proto文件的消息类型:
import "myproject/other_protos.proto";
例子:

  • 文件名称search_response.proto
syntax = "proto3";
import "test/result.proto";
package test1;

message SearchResponse {
  //包名.消息名
  repeated test2.Result results = 1;
}
复制代码
  • 文件名称result.proto,在与search_response.proto同级目录的test下
syntax = "proto3";
package test2;

message Result {
  string url = 1;
  string title = 2;
  repeated string snippets = 3;
}
复制代码

如果两个.proto文件在同一个目录下直接这样import "result.proto";倒入即可。

6 内嵌类型

我们还可以在消息类型中定义消息,例子如下:

message SearchResponse {
  message Result {
    string url = 1;
    string title = 2;
    repeated string snippets = 3;
  }
  repeated Result results = 1;
}
复制代码

在上面的例子中在SearchResponse消息体中定义了一个Result消息并使用。


如果想在其他的消息体引用Result这个消息,可以Parent.Type这样引用,例子:

message SomeOtherMessage {
  SearchResponse.Result result = 1;
}
复制代码

消息还可以深层的嵌套定义,如下例子:

message Outer {                  // Level 0
  message MiddleAA {  // Level 1
    message Inner {   // Level 2
      int64 ival = 1;
      bool  booly = 2;
    }
  }
  message MiddleBB {  // Level 1
    message Inner {   // Level 2
      int32 ival = 1;
      bool  booly = 2;
    }
  }
}
复制代码

7 Map

proto支持map属性类型的定义,语法如下:
map<key_type,value_type> map_field = N;
key_type可以是任何整数或字符串类型(除浮点类型和字节之外的任何标量类型,枚举类型也是不合法的key类型),value_type可以是任何类型的数据。


map更具体的使用方式参看API

8 包

可以为proto文件指定包名,防止消息命名冲突。


例子如下:

package foo.bar;
message Open { ... }
复制代码

当你在为消息类型定义属性的时候,你可以通过命名.类型的形式来使用已经定义好的消息类型,如下:

Message Foo {
  ...
  foo.bar.Open open = 1;
  ...
}
复制代码

9 服务定义

如果你想在RPC中使用已经定义好的消息类型,你可以在.proto文件中定一个消息服务接口,protocol buffer编译器会生成对应语言的接口代码。

  • 接口定义例子:
service SearchService {
    //  方法名  方法参数                 返回值
    rpc Search(SearchRequest) returns (SearchResponse); 
}
复制代码

10 选项

下面只列出java的.proto文件常用的一下选贤,其他选项前参看官网文档

  • java_package(文件选项):指定生成的java类所在的包, 如果在.proto文件中没有提供明确的java_package选项,那么默认情况下,将使用proto包。如果没有生成java代码该选项默认是不生效的。
    option java_package = "org.example.foo";

  • java_multiple_files(文件选项):指定在proto文件中定义的所有消息、枚举和服务在生成java类的时候都会生成对应的java类文件,而不是以内部类的形式出现。
    option java_multiple_files = true;

  • java_outer_classname(文件选项):指定生成的java类文件名称,如果不指定则会默认使用.proto文件的文件名称,如果没有生成java类文件,则该选项不会生效 <span id="1">Hello World</span>。
    option java_outer_classname = "HelloWorld";


转载于:https://juejin.im/post/5c11e55c6fb9a04a0c2e5028

<think>我们正在解决C#使用gRPC时由proto文件生成的代码出现语法错误的问题。根据引用内容,我们可以从以下几个方面排查和解决:1.**检查proto文件语法**:确保proto文件符合proto3语法规范(引用[3])。常见的语法错误包括:-缺少`syntax="proto3";`声明-字段编号缺失或重复-枚举定义中别名未启用`allow_alias`(引用[2])2.**检查生成工具配置**:在C#项目中,生成gRPC代码通常通过`<Protobuf>`元素配置。确保配置正确:-指定正确的proto文件路径-设置正确的GrpcServices选项(如`Server`、`Client`、`Both`)(引用[1])3.**检查枚举定义**:如果proto文件中包含枚举,且需要为不同的枚举元素赋予相同的,必须设置`allow_alias=true`(引用[2])。否则会导致编译错误。4.**检查字段默认**:在proto3中,字段不能设置默认proto2不同),且未设置的字段会使用默认(引用[4])。但这不是语法错误,可能引起逻辑错误。**解决方案步骤**:**步骤1:修复proto文件语法**-确保文件首行有`syntax="proto3";`-检查所有字段是否有唯一编号(如`int32id=1;`)-如果枚举有别名,添加`optionallow_alias=true;`示例修复枚举错误:```proto//修复前(错误)enumStatus{UNKNOWN=0;PENDING=1;PROCESSING=1;//重复,未启用别名}//修复后enumStatus{optionallow_alias=true;UNKNOWN=0;PENDING=1;PROCESSING=1;//现在合法}```**步骤2:检查C#项目配置**在.csproj文件中,确保protobuf引用配置正确:```xml<ItemGroup><ProtobufInclude="Protos\greet.proto"GrpcServices="Both"/></ItemGroup>```**步骤3:重新生成代码**-清理项目(`dotnetclean`)-重新构建(`dotnetbuild`)**步骤4:检查生成代码的依赖**-确保项目引用了必要的NuGet包(如`Grpc.Tools`、`Google.Protobuf`、`Grpc.Net.Client`等)**常见错误示例及解决**:1.**错误**:`Thetypeornamespacename'Google'couldnotbefound`**解决**:安装NuGet包`Google.Protobuf`2.**错误**:`重复的枚举`**解决**:在枚举定义中添加`optionallow_alias=true;`3.**错误**:`字段编号重复`**解决**:确保同一消息内的所有字段编号唯一通过以上步骤,大多数语法错误应能解决。若问题依旧,请提供具体错误信息以便进一步分析。§§相关问题§§1.如何配置C#gRPC服务同时生成客户端和服务端代码?2.proto3语法中枚举定义有哪些注意事项?3.gRPC在C#项目中的完整工作流程是怎样的?4.如何解决gRPC生成代码时出现的“未找到类型”错误?</think>### C# gRPC proto 生成代码语法错误解决方案 当 C# 使用 gRPC 时,由 proto 文件生成的代码出现语法错误,通常由以下原因引起: #### 1. **枚举定义未启用别名选项**(常见错误) 当 proto 文件中枚举存在重复时,必须显式启用 `allow_alias` 选项(引用[2]): ```proto // 错误示例(导致 C# 生成代码报错) enum Status { UNKNOWN = 0; STARTED = 1; RUNNING = 1; // 重复未启用别名 } // 正确修复 enum Status { option allow_alias = true; // 必须添加此选项 UNKNOWN = 0; STARTED = 1; RUNNING = 1; // 现在合法 } ``` #### 2. **proto 语法版本未声明** 每个 proto 文件开头必须明确定义语法版本(引用[3]): ```proto // 缺失 syntax 声明会导致生成错误 syntax = "proto3"; // 必须放在文件第一行 message Request { string query = 1; } ``` #### 3. **字段编号冲突** proto3 要求同一消息内的字段编号唯一(引用[3][4]): ```proto message User { int32 id = 1; string name = 1; // 错误!字段编号重复 string email = 2; // 正确 } ``` #### 4. **工具链版本不匹配** 1. **更新 NuGet 包**:确保项目中包含最新 gRPC 工具包 ```xml <PackageReference Include="Grpc.Tools" Version="2.54.0" /> ``` 2. **清理生成文件**: - 删除 `obj/` 文件夹 - 执行 `dotnet clean` 和 `dotnet build` #### 5. **生成配置错误** 在 `.csproj` 中检查 protobuf 配置(引用[1]): ```xml <ItemGroup> <!-- 明确指定生成模式 --> <Protobuf Include="**.proto" GrpcServices="Both" /> </ItemGroup> ``` #### 6. **特殊字符处理** proto 文件中使用 C# 保留关键字时需转义: ```proto message Data { string @class = 1; // 使用 @ 转义 class 关键字 int32 @event = 2; // 转义 event } ``` > **验证流程**: > 1. 检查 proto 文件语法 → 2. 清理项目 → 3. 重新生成代码 → 4. 检查 C# 编译错误 > 引用说明:proto3 中未设置字段的默认行为是语言相关的(引用[4]),但此问题通常导致运行时错误而非生成错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值