В ходе работы возникла необходимость выполнить strings.TrimSpace на всех полях с типом protobufstring
В результате было решено написать плагин, генерирующий необходимые функции для каждой структуры: trimAll (для создания функции обрезки полей типа string) и UnmarshalJSON (мы думали, что это функция, используемая grpc-gateway для перевода из http.Request.Body в proto.Message).
func (m *GetLogbookCall_Request) trimAll() {
if m == nil {
return
}
m.Id = strings.TrimSpace(m.Id)
}
func (m *GetLogbookCall_Request) UnmarshalJSON(data []byte) error {
err := proto.Unmarshal(data, m)
if err != nil {
return err
}
m.trimAll()
return nil
}
Но в результате ничего не получилось)
Вопрос в следующем: есть ли способ добавить собственную логику при переводе http.Request.Body в proto.Message?
ПС:
Вот функции, созданные плагином grpc-gateway.
func request_AccountService_UpdatePassword_0(ctx context.Context, marshaler runtime.Marshaler, client AccountServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq account.UpdatePasswordCall_Request
var metadata runtime.ServerMetadata
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.UpdatePassword(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}

Я нашел решение своей проблемы. Вы можете написать собственный маршалер, а затем использовать его для создания прокси-сервера.
type Marshaler struct {
runtime.JSONPb
}
func (m *Marshaler) NewDecoder(r io.Reader) runtime.Decoder {
d := json.NewDecoder(r)
return Decoder{
decoder: &runtime.DecoderWrapper{
Decoder: d,
UnmarshalOptions: m.UnmarshalOptions,
},
}
}
type Decoder struct {
decoder *runtime.DecoderWrapper
}
func (d Decoder) Decode(v interface{}) error {
err := d.decoder.Decode(v)
if err != nil {
return err
}
// Write your own custom logic here. In my case it's use TrimAll method
type Trimmable interface {
TrimAll()
}
if v, ok := v.(Trimmable); ok {
v.TrimAll()
}
return nil
}
mux := runtime.NewServeMux(
runtime.WithMetadata(func(ctx context.Context, r *http.Request) metadata.MD {
runtime.WithMarshalerOption(runtime.MIMEWildcard, &Marshaler{
runtime.JSONPb{
MarshalOptions: protojson.MarshalOptions{
EmitUnpopulated: true,
},
},
}),
)
Меня совершенно сбивало с толку, когда я получал обрезанные строки в JSON API, но разные ответы от вызовов gRPC. Это, казалось бы, «необходимость», указывает на более серьезные проблемы в архитектуре.