为 gRPC JSON 转码配置 HTTP 和 JSON

作者:James Newton-King

gRPC JSON 转码从 gRPC 方法创建 RESTful JSON Web API。 它使用用于自定义如何将 RESTful API 映射到 gRPC 方法的注释和选项。

HTTP 规则

gRPC 方法必须在支持转码之前使用 HTTP 规则进行注释。 HTTP 规则包括有关将 gRPC 方法作为 RESTful API 调用的信息,例如 HTTP 方法和路由。

import "google/api/annotations.proto";

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {
    option (google.api.http) = {
      get: "/v1/greeter/{name}"
    };
  }
}

HTTP 规则:

注意

指向 .NET 参考源的文档链接通常会加载存储库的默认分支,该分支表示针对下一个 .NET 版本的当前开发。 若要为特定版本选择标记,请使用“切换分支或标记”下拉列表。 有关详细信息,请参阅如何选择 ASP.NET Core 源代码的版本标记 (dotnet/AspNetCore.Docs #26205)

HTTP 方法

通过将路由设置为匹配的 HTTP 方法字段名称来指定 HTTP 方法:

  • get
  • put
  • post
  • delete
  • patch

custom 字段适用于其他 HTTP 方法。

在以下示例中,CreateAddress 方法使用指定的路由映射到 POST

service Address {
  rpc CreateAddress (CreateAddressRequest) returns (CreateAddressReply) {
    option (google.api.http) = {
      post: "/v1/address",
      body: "*"
    };
  }
}

路由

gRPC JSON 转码路由支持路由参数。 例如,路由中的 {name} 绑定到请求消息上的 name 字段。

若要绑定嵌套消息上的字段,请指定字段的路径。 在以下示例中,{params.org} 绑定到 IssueParams 消息上的 org 字段:

service Repository {
  rpc GetIssue (GetIssueRequest) returns (GetIssueReply) {
    option (google.api.http) = {
      get: "/{apiVersion}/{params.org}/{params.repo}/issue/{params.issueId}"
    };
  }
}

message GetIssueRequest {
  int32 api_version = 1;
  IssueParams params = 2;
}
message IssueParams {
  string org = 1;
  string repo = 2;
  int32 issueId = 3;
}

转码路由和 ASP.NET Core 路由具有类似的语法和功能集。 但是,转码不支持某些 ASP.NET Core 路由功能。 其中包括:

请求正文

转码将请求正文 JSON 反序列化为请求消息。 body 字段指定 HTTP 请求正文如何映射到请求消息。 值是其值映射到 HTTP 请求正文的请求字段的名称,或用于映射所有请求字段的 *

在以下示例中,HTTP 请求正文反序列化为 address 字段:

service Address {
  rpc AddAddress (AddAddressRequest) returns (AddAddressReply) {
    option (google.api.http) = {
      post: "/{apiVersion}/address",
      body: "address"
    };
  }
}

message AddAddressRequest {
  int32 api_version = 1;
  Address address = 2;
}
message Address {
  string street = 1;
  string city = 2;
  string country = 3;
}

查询参数

可以使用 HTTP 查询参数设置请求消息中未被路由参数或请求正文绑定的任何字段。

service Repository {
  rpc GetIssues (GetIssuesRequest) returns (GetIssuesReply) {
    option (google.api.http) = {
      get: "/v1/{org}/{repo}/issue"
    };
  }
}

message GetIssuesRequest {
  string org = 1;
  string repo = 2;
  string text = 3;
  PageParams page = 4;
}
message PageParams {
  int32 index = 1;
  int32 size = 2;
}

在上面的示例中:

  • orgrepo 字段是从路由参数绑定的。
  • 其他字段(如 textpage 中的嵌套字段)可以从查询字符串绑定:?text=value&page.index=0&page.size=10

响应正文

默认情况下,转码会将整个响应消息序列化为 JSON。 response_body 字段允许序列化响应消息的子集。

service Address {
  rpc GetAddress (GetAddressRequest) returns (GetAddressReply) {
    option (google.api.http) = {
      get: "/v1/address/{id}",
      response_body: "address"
    };
  }
}

message GetAddressReply {
  int32 version = 1;
  Address address = 2;
}
message Address {
  string street = 1;
  string city = 2;
  string country = 3;
}

在上面的示例中,address 字段作为 JSON 序列化到响应正文。

规范

有关如何自定义 gRPC 转码的详细信息,请参阅 HttpRule 规范

自定义 JSON

使用 Protobuf 规范中的 JSON 映射在消息和 JSON 之间转换。 Protobuf 的 JSON 映射是一种在 JSON 和 Protobuf 之间转换的标准化方法,所有序列化都遵循这些规则。

但是,gRPC JSON 转码提供了用于使用 GrpcJsonSettings 自定义 JSON 的一些有限选项,如下表所示。

选项 默认值 说明
IgnoreDefaultValues false 如果设置为 true,则在序列化过程中将忽略具有默认值的字段。
WriteEnumsAsIntegers false 如果设置为 true,则枚举值将作为整数(而不是字符串)写入。
WriteInt64sAsStrings false 如果设置为 true,则 Int64UInt64 值将作为字符串(而不是数字)写入。
WriteIndented false 如果设置为 true,则 JSON 将使用整齐打印写入。 此选项不会影响流式处理方法,该方法写入以行分隔的 JSON 消息,并且无法使用整齐打印。
builder.Services.AddGrpc().AddJsonTranscoding(o =>
{
    o.JsonSettings.WriteIndented = true;
});

.proto 文件中,json_name 字段选项在序列化为 JSON 时自定义字段的名称,如以下示例所示:

message TestMessage {
  string my_field = 1 [json_name="customFieldName"];
}

转码不支持高级 JSON 自定义。 需要精确 JSON 结构控制的应用应考虑使用 ASP.NET Core Web API

其他资源