【问题标题】:grpc-gateway: how to set allowed content-type per requestgrpc-gateway:如何为每个请求设置允许的内容类型
【发布时间】:2021-04-09 22:24:29
【问题描述】:

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

所以,我的任务是:

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

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

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

【问题讨论】:

    标签: http go grpc grpc-gateway


    【解决方案1】:

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

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

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

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

    我在 GOlang here 中实现了这个确切的案例

        // 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
    }
    

    【讨论】:

    • 也认为 grpc 网关对于大多数用例来说都是多余的
    猜你喜欢
    • 2021-04-19
    • 2020-08-23
    • 2012-04-07
    • 1970-01-01
    • 2022-12-21
    • 2012-04-23
    • 1970-01-01
    • 2018-05-26
    • 2011-07-24
    相关资源
    最近更新 更多