首页 > 解决方案 > 将 HTTP 转码为 gRPC:具有不同参数的相同端点

问题描述

我已经有一个正在运行的 gRPC 项目。我正在寻找构建一个能够执行一些 HTTP 请求的 API。

我有以下两种类型:

message FindRequest {
  ModelType model_type = 1;
  oneof by {
    string id = 2;
    string name = 3;
  }
}
message GetAllRequest {
  ModelType model_type = 1;
  int32 page_size = 2;
  oneof paging {
    int32 page = 3;
    bool skip_paging = 4;
  }
}

然后,我想要这两个端点:

  // Get a data set by ID or name. Returns an empty data set if there is no such
  // data set in the database. If there are multiple data sets with the same
  // name in the database it returns the first of these data sets.
rpc Find(FindRequest) returns (DataSet){
    option (google.api.http) = { get: "/datasets" };
 }
  // Get (a page of) all data sets of a given type. If no page size is given
  // (page <= 0) it defaults to 100. An unset page (page <= 0) defaults to the
  // first page.
rpc GetAll(GetAllRequest) returns (GetAllResponse){
    option (google.api.http) = { get: "/datasets" };
}

对我来说,有两个同名的不同端点是有意义的,但参数不同。例如,请求/datasets?model-type=XXX应该映射到GetAll函数,而请求/datasets?model-type=XXX&name=YYY应该映射到Find函数。但是,它不起作用,因为我猜映射失败,所以这些端点都没有返回任何东西。

我认为使映射工作的解决方案是强制参数是必需的,但是,我正在使用proto3,这不允许该required字段。

那么,我如何能够使用 proto3 拥有 2 个名称相同但参数不同的端点?

我知道如果我使用不同的端点名称,它可以工作,例如findRequest,我可以有以下端点:/findDatasets,但关于 API 命名约定的最佳实践,这是不可取的,或者是吗?

标签: apigrpcnaming-conventionstranscoding

解决方案


解决这个问题的常规方法是使用不同的方法。我的直觉是,尝试使用请求字符串中的字段进行区分是一种反模式。

service YourService {
    rpc FindSomething(FindSomethingRequest) returns (FindSomethingResponse){
        option (google.api.http) = { get: "/something/find" };
    }
    rpc ListSomething(ListSomethingRequest) returns (ListSomethingResponse){
        option (google.api.http) = { get: "/something/list" };
    }
}

message FindSomethingRequest {
  ModelType model_type = 1;
  string id = 2;
  string name = 3;
}
message ListSomethingRequest {
  int32 page_size = 2;
  int32 page_token = 3;
}
message ListSomethingResponse {
  repeated ModelType model_types = 1;
  int32 page_size = 2;
  int32 next_page_token = 3;
}

我不确定您的底层事物结构,但是,我认为更好的做法是使用所有可能的属性对事物进行建模并允许保留一些未设置(例如,其中一个idname可能两者都在FindSomethingRequest),而不是为所有可能的查询创建不同的消息类型。你为事物建模,而不是你如何与之交互。

在您的实现(!)中FindSomething,您将处理消息用户如何构造字段的排列。可能会报告一个错误“要么是id要么name是必需的”。

我认为ListSomething's 的消息也可以更简单。您请求 (ModelTypes) 的列表并给出一个page_size和一个page_token(可能是"")。它返回一个ModelTypes 列表、返回页面的大小(可能小于请求的大小)以及 anext_page_token如果有更多数据,您可以使用它来发出下一个ListSomething请求。


推荐阅读