1

grpc-gateway在同一个 go 应用程序中使用代理将 HTTP 转换为 GRPC。据我所见,默认情况下为所有 RPCgrpc-gateway设置应用程序application/json格式,包括流式传输。

所以,我的任务是:

  1. 传入的 HTTP 请求必须始终为Content-type: application/json,否则应根据 RFC 拒绝请求并发送 406。
  2. 传入的 HTTP 请求可能已Accept: application/x-ndjson为一元 RPC 设置,并Accept: applcation/x-ndjson为服务器流设置了标头。如果条件不满足406,应该返回。
  3. Content-type: applicaiton/json对于简单的一元 RPC 和Content-type: application/x-ndjson服务器流,必须设置传出 HTTP 请求。

因此,grpc-gateway仅建议设置自定义编组器 for application/x-ndjson,这实际上与默认编组器相同,因此只需覆盖ContentType方法。这种方法不允许我为每个方法调用设置封送拆收器,也不允许我拒绝每个请求不支持的内容类型。

我怎样才能做到这一点仍在使用grpc-gateway?或者我应该考虑手动实现http grpc转换?

4

1 回答 1

1

我建议您不要使用 grpc-gateway 或任何其他工具将 gRPC 转换为 HTTP RPC。您正在为您的应用程序增加不必要的复杂性。

如果您有 gRPC 服务,但出于某种原因,您的客户端无法调用 gRPC,您需要通过纯 HTTP 选项提供您的服务……是您的情况吗?

如果这是您的情况,那么正确的方法是提供 HTTP RPC 服务并从中调用您的 gRPC 服务。

HTTP RPC 比 REST 简单得多,您不需要任何工具。

我在这里用 GOlang 实现了这个确切的案例

    // Creates a new book.
func (h BookStoreService) CreateBook(w http.ResponseWriter, r *http.Request) {
    request := &bookv1.CreateBookRequest{}
    proxy := httputil.GetProxy(w, r, request)
    proxy.SetServiceRequest(func(request proto.Message) (proto.Message, error) {
        return h.client.CreateBook(r.Context(), request.(*bookv1.CreateBookRequest))
    })
    proxy.Call()
}

代理结构

func GetProxy(w http.ResponseWriter, r *http.Request, request proto.Message) *ServiceProxy {
    proxy := &ServiceProxy{}
    proxy.SetResponseWriter(w)
    proxy.SetSourceRequest(r)
    proxy.SetDestRequest(request)
    return proxy
}

type ServiceProxy struct {
    err            error
    serviceRequest func(request proto.Message) (proto.Message, error)
    writer         http.ResponseWriter
    destRequest    proto.Message
    sourceRequest  *http.Request
}

func (b *ServiceProxy) SetDestRequest(request proto.Message) {
    b.destRequest = request
}

func (b *ServiceProxy) SetSourceRequest(request *http.Request) {
    b.sourceRequest = request
}

func (b *ServiceProxy) SetServiceRequest(svcRequest func(request proto.Message) (proto.Message, error)) *ServiceProxy {
    b.serviceRequest = svcRequest
    return b
}

func (b *ServiceProxy) Call() {
    b.writer.Header().Set("Content-Type", "application/json; charset=utf-8")
    err := unmarshal(b.writer, b.sourceRequest, b.destRequest)
    if err != nil {
        return
    }
    resp, err := b.serviceRequest(b.destRequest)
    if err != nil {
        handleErrorResp(b.writer, err)
        return
    }
    b.writer.WriteHeader(http.StatusOK)
    json.NewEncoder(b.writer).Encode(resp)
}

func (b *ServiceProxy) SetResponseWriter(w http.ResponseWriter) {
    b.writer = w
}
于 2021-04-09T14:24:29.030 回答