我正在try 编写一个一元拦截器来向传入请求添加请求ID:

func RequestIDInterceptor(ctx context.Context, req interface{},
    info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (val interface{}, err error) {

    newCtx := ctx
    newCtx = createAndSetMetadata(newCtx, metadata.FromIncomingContext, metadata.NewIncomingContext)
    newCtx = createAndSetMetadata(newCtx, metadata.FromOutgoingContext, metadata.NewOutgoingContext)

    md, ok := metadata.FromOutgoingContext(newCtx)
    fmt.Printf("After update. Metadata: %v, Found: %t\n", md, ok)

    return handler(newCtx, req)
}

func createAndSetMetadata(ctx context.Context, getter func(context.Context) (metadata.MD, bool),
    setter func(context.Context, metadata.MD) context.Context) context.Context {

    meta, ok := getter(ctx)
    fmt.Printf("Found? %t, Metadata: %v\n", ok, meta)
    if !ok {
        meta = metadata.New(map[string]string{})
        ctx = setter(ctx, meta)
    }

    meta.Set("X-Request-Id", "TEST_REQUEST_ID")
    fmt.Printf("Metadata: %v\n", meta)
    return ctx
}

当请求不包括元数据(RequestIDInterceptor(context.Background(), nil, nil, handler))时,此代码按预期工作,但如果上下文包括元数据,则不会填充ID.基本上,输出如下所示:

WITHOUT METADATA
Found? false, Metadata: map[]
Metadata: map[x-request-id:[TEST_REQUEST_ID]]
Found? false, Metadata: map[]
Metadata: map[x-request-id:[TEST_REQUEST_ID]]
After update. Metadata: map[x-request-id:[TEST_REQUEST_ID]], Found: true

WITH METADATA
Found? true, Metadata: map[]
Metadata: map[x-request-id:[TEST_REQUEST_ID]]
Found? true, Metadata: map[]
Metadata: map[x-request-id:[TEST_REQUEST_ID]]
After update. Metadata: map[], Found: true

据我所知,发生这种情况的原因可能是metadata.MD是通过值传递的,而不是通过引用传递的,所以当createAndSetMetadata返回时,更新将被丢弃.我如何修复此代码?

推荐答案

问题是metadata.FromOutgoingContext实际上返回了在上下文中找到的metadata.MD的深层副本.你可以查the sources,这在google.golang.org/grpc@v1.50年前仍然是正确的.

因此,像使用 map 那样直接写入它(副本)并不会影响存储在上下文中的MD.

您必须再次调用setter来传播新值:

func createAndSetMetadata(/* args */) context.Context {
    // ...
    return setter(ctx, meta)
}

Go相关问答推荐

有没有更简单的方法在Go中编写这个逻辑?

使用恶意软件 scanner (ClamAV)时的Google云存储桶上传文件验证

如何预编译Golang标准库?

无法使用Segentio;S Kafka-Go连接到融合的Kafka

如何在链接中写入链接

Kafka架构注册表-Broker:Broker无法验证记录

在golang中以JSON格式获取xwwwformurlencoded请求的嵌套键值对

如何从Go项目连接Microsoft Access数据库?

golang gin 获取 cookie json

使用 unsafe.Pointer 将 struct point直接转换为另一个 struct 是否安全?

有没有什么方法可以在不使用 if/else 的情况下在 Golang 中处理 nil 指针?

如何使用 fyne Go 使用 canvas.NewText() 使文本可滚动

如何修改go gin的默认端口?我的 8080 端口正在使用中

为什么 0 big.Int 的 .Bytes() 值是空切片?

Golang 工作池实现意外工作

泛型:对具有返回自身的函数的类型的约束

Gorilla/Mux 和 Websocket 竞赛条件,这安全吗?

Golang 查询扫描未将查询正确扫描到 struct 中

在 Go 泛型中,如何对联合约束中的类型使用通用方法?

为什么在 unsafe.Sizeof() 中取消引用 nil 指针不会导致panic ?