2 回答

TA贡献1799条经验 获得超9个赞
Deepankar 概述了一种解决方案,但我确实看到了将所有响应数据保存在模式定义的响应结构中的吸引力。protoc-gen-go如果生成的 setter 与 getter 一起使用,这肯定会更简单!
我找不到将旧响应中的标头/尾部复制到新响应中的方法。(我认为此时它们还没有真正确定,但我不确定。)
你不需要这样做。在您的示例中,res.Any()返回指向 protobuf 消息的指针 - 您可以就地修改它。您的类型开关可能如下所示:
switch resMsg := res.Any().(type) {
case *livev1.StartResponse:
resMsg.DeviceOffset = durationpb.New(deviceTimeOffset)
case *livev1.StatusResponse:
resMsg.DeviceOffset = durationpb.New(deviceTimeOffset)
}
return res, err
使用类型断言需要我为每种类型一遍又一遍地重复几乎相同的代码块。
不幸的是,你最好的选择可能是反思。您可以在标准 Go 反射或 protobuf 反射之间进行选择——两者都可以。使用 protobuf 反射,这样的事情应该可以解决问题:
res, err := next(ctx, req)
if err != nil {
return nil, err
}
msg, ok := res.Any().(proto.Message)
if !ok {
return res, nil
}
// Keep your logic to calculate offset!
var deviceTimeOffset time.Duration
// You could make this a global.
durationName := (*durationpb.Duration)(nil).ProtoReflect().Descriptor().FullName()
refMsg := msg.ProtoReflect()
offsetFD := refMsg.Descriptor().Fields().ByName("DeviceOffset")
if offsetFD != nil &&
offsetFD.Message() != nil &&
offsetFD.Message().FullName() == durationName {
refOffset := durationpb.New(deviceTimeOffset).ProtoReflect()
refMsg.Set(
offsetFD,
protoreflect.ValueOf(refOffset),
)
}
return res, nil
这取决于您是否认为这比重复类型切换更好或更差——它要复杂得多,但它确实让事情变得更干燥。

TA贡献1936条经验 获得超6个赞
您是否有可能使用标题而不是正文。如果客户端可以NowOnDevice通过请求标头发送,那么您可以改为在响应标头中发回响应。Unix 时间戳可能是最好的方法。
func MakeDeviceTimeInterceptor() connect.UnaryInterceptorFunc {
return func(next connect.UnaryFunc) connect.UnaryFunc {
return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
now := time.Now().UTC()
ctxa := context.WithValue(ctx, CurrentTimestampKey{}, now)
var deviceTimeOffset time.Duration
// Check the header message `now-on-device` field, instead of body
reqWithNow := req.Header().Get("now-on-device")
if reqWithNow != "" {
val, err := strconv.Atoi(reqWithNow)
if err != nil {
return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("invalid timestamp"))
}
deviceTime := time.Unix(int64(val), 0)
deviceTimeOffset = now.Sub(deviceTime)
ctxa = context.WithValue(ctxa, DeviceTimeDiffKey{}, deviceTimeOffset)
}
res, err := next(ctxa, req)
// Set to response header if value is set
if deviceTimeOffset != 0 {
res.Header().Set("device-time-offset", fmt.Sprintf("%d", deviceTimeOffset))
}
return res, err
}
}
}
然后你有回应:
curl -v \
--header "Content-Type: application/json" --header "now-on-device: 1656442814" \
--data '{"name": "Jane"}' \
http://localhost:8080/greet.v1.GreetService/Greet
* Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /greet.v1.GreetService/Greet HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.79.1
> Accept: */*
> Content-Type: application/json
> now-on-device: 1656442814
> Content-Length: 16
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Accept-Encoding: gzip
< Content-Type: application/json
< Device-Time-Offset: 7259524766000
< Greet-Version: v1
< Date: Tue, 28 Jun 2022 21:01:13 GMT
< Content-Length: 27
<
* Connection #0 to host localhost left intact
{"greeting":"Hello, Jane!"}
- 2 回答
- 0 关注
- 122 浏览
添加回答
举报