grpc-gateway: как установить разрешенный тип контента для каждого запроса

Я использую grpc-gateway внутри того же приложения go, чтобы прокси конвертировать HTTP в GRPC. Насколько я понимаю, по умолчанию grpc-gateway устанавливает формат приложения application/json для всех rpcs, включая стриминговые.

Итак, моя задача:

  1. Входящие HTTP-запросы ДОЛЖНЫ всегда быть Content-type: application/json, в противном случае запрос должен быть отклонен и отправлен 406 в соответствии с RFC.
  2. Входящий HTTP-запрос МОЖЕТ иметь Accept: application/x-ndjson установленный для унарных RPC и 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 вручную?


person QuestionAndAnswer    schedule 08.04.2021    source источник


Ответы (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
}
person Alexsandro Souza    schedule 09.04.2021