修改Go GRPC服务器流拦截器上的元数据

2023-12-31

我一直在尝试在服务器流拦截器上设置元数据,以便实际的 RPC 函数可以在下游读取它们:

func UserIDInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
    ss.SendHeader(metadata.New(map[string]string{"X-User-Id": "real_user_id"}))
    return handler(srv, ss)
}

func (server *Server) GetObjects(req *iam.GetObjectsRequest, client iam.Service_GetObjectsServer) error {
    ctx := client.Context()
    userID, ok := HeaderFromMetadata(ctx, "X-User-Id")

    log.Printf("User ID: %s, Ok: %t\n", userID, ok)
    return nil
}

func HeaderFromMetadata(ctx context.Context, headers ...string) (string, bool) {
    meta, ok := metadata.FromIncomingContext(ctx)
    if !ok {
        return "", false
    }

    for _, header := range headers {
        if value := meta.Get(header); len(value) > 0 {
            return value[0], true
        }
    }

    return "", false
}

我的服务器是这样注册的:

server := grpc.NewServer(
    grpc.StreamInterceptor(UserIDInterceptor))
RegisterIAMServer(server, NewServer())

我遇到的问题是找不到用户 ID 标头。我可以看到当客户端发送请求时拦截器被调用,并且我可以看到元数据包含标头,但实际的RPC似乎无法提取它。我在这里做错了什么?


Update

更简单的解决方案只是覆盖Context()的方法ServerStream

type serverStream struct {
    grpc.ServerStream
    ctx context.Context
}

func (s *serverStream) Context() context.Context {
    return s.ctx
}

func UserIDInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
    md, ok := metadata.FromIncomingContext(ss.Context())
    if ok {
        md.Append("X-User-Id", "real_user_id")
    }
    newCtx := metadata.NewIncomingContext(ss.Context(), md)

    return handler(srv, &serverStream{ss, newCtx})
}

Update

另一种简单的解决方案是定义一个包装器grpc.ServerStream如下

type serverStreamWrapper struct {
    ss  grpc.ServerStream
    ctx context.Context
}

func (w serverStreamWrapper) Context() context.Context        { return w.ctx }
func (w serverStreamWrapper) RecvMsg(msg interface{}) error   { return w.ss.RecvMsg(msg) }
func (w serverStreamWrapper) SendMsg(msg interface{}) error   { return w.ss.SendMsg(msg) }
func (w serverStreamWrapper) SendHeader(md metadata.MD) error { return w.ss.SendHeader(md) }
func (w serverStreamWrapper) SetHeader(md metadata.MD) error  { return w.ss.SetHeader(md) }
func (w serverStreamWrapper) SetTrailer(md metadata.MD)       { w.ss.SetTrailer(md) }

func UserIDInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
    md, ok := metadata.FromIncomingContext(ss.Context())
    if ok {
        md.Append("X-User-Id", "real_user_id")
    }
    newCtx := metadata.NewIncomingContext(ss.Context(), md)

    return handler(srv, serverStreamWrapper{ss, newCtx})
}

你可以使用新传入上下文 https://pkg.go.dev/google.golang.org/grpc/metadata#NewIncomingContext在流中创建当前上下文的副本。

由于没有方法可以设置context of grpc.ServerStream,以便将上下文设置回ServerStream, the wrappedStream定义为context.Context, and SetContext设定方法context.Context

type wrappedStream struct {
    grpc.ServerStream
    ctx context.Context
}

func (w *wrappedStream) SetContext(ctx context.Context) {
    w.ctx = ctx
}

完整示例代码

type wrappedStream struct {
    grpc.ServerStream
    ctx context.Context
}

func (w *wrappedStream) Context() context.Context {
    return w.ctx
}

func (w *wrappedStream) SetContext(ctx context.Context) {
    w.ctx = ctx
}

func (w *wrappedStream) RecvMsg(m interface{}) error {
    return w.ServerStream.RecvMsg(m)
}

func (w *wrappedStream) SendMsg(m interface{}) error {
    return w.ServerStream.SendMsg(m)
}

type StreamContextWrapper interface {
    grpc.ServerStream
    SetContext(context.Context)
}

func newStreamContextWrapper(ss grpc.ServerStream) StreamContextWrapper {
    ctx := ss.Context()
    return &wrappedStream{
        ss,
        ctx,
    }
}

func UserIDInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
    md, ok := metadata.FromIncomingContext(ss.Context())
    if ok {
        md.Append("X-User-Id", "real_user_id")
    }
    newCtx := metadata.NewIncomingContext(ss.Context(), md)

    sw := newStreamContextWrapper(ss)
    sw.SetContext(newCtx)

    return handler(srv, sw)
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

修改Go GRPC服务器流拦截器上的元数据 的相关文章

  • 如何使用 GOPATH 的 Samba 服务器位置?

    我正在尝试将 GOPATH 设置为共享网络文件夹 当我进入 export GOPATH smb path to shared folder I get go GOPATH entry is relative must be absolute
  • 如何修复“缺少表的 FROM 子句条目”错误

    我正在尝试根据游戏 ID 获取平台名称 我有如下三个表 我正在尝试加入它们以获得所需的结果 Games Id 1 2 3 4 Game Platforms Id game id platform id 1 1 1 2 1 2 3 3 3
  • 什么时候返回结构体指针是个好主意?

    我正在学习 Go 我对何时使用指针有点困惑 具体来说 当返回一个struct从函数中 什么时候适合返回结构体实例本身 什么时候适合返回指向结构体的指针 示例代码 type Car struct make string model strin
  • 如何将所有GET请求查询参数放入Go中的结构体中?

    你好 我想将 get 查询参数转换为 Go 中的结构 例如我有这样的结构 type Filter struct Offset int64 json offset Limit int64 json limit SortBy string js
  • 使用 mgo 驱动程序进行 mongo 聚合查询

    我在 mongodb 中有以下查询 db devices aggregate match userId v73TuQqZykbxFXsWo state true project userId 1 categorySlug 1 weight
  • 在复杂的文件夹结构中进行测试

    我正在 golang 中构建一个设计模式存储库 为了运行所有测试 我使用这个 bash 脚本 有用 bin bash go test creational abstract factory go go test creational bui
  • 单值上下文中的多值错误

    我在编译 GO 代码时遇到此错误 multiple value fmt Println in single value context 我正在尝试创建一个函数 该函数接受可变数量的整数并将每个变量打印在一行上 GO package main
  • Facebook服务器端登录、CORS

    我正在实现一个带有 FB 服务器端登录的网站 简化步骤如下 一个简单的按钮触发 JS 脚本 该脚本调用我的后端 APIhttps localhost fblogin function sendFbLoginData get https lo
  • container_memory_working_set_bytes 与 process_resident_memory_bytes 和total_rss 之间的关系

    我希望了解以下关系 容器内存工作集字节 vs 进程驻留内存字节 vs 总计RSS 容器内存 rss 文件映射以便更好地配备OOM可能性警报系统 这似乎违背了我的理解 这让我现在感到困惑 如果容器 pod 运行单个进程 执行用 Go 编写的编
  • 测试 gRPC 服务

    我想测试用 Go 编写的 gRPC 服务 我使用的示例是 Hello World 服务器示例grpc go 仓库 https github com grpc grpc go blob master examples helloworld g
  • 如何使用 go1.6.2 构建 linux 32 位

    有没有任何组合GOARCH and GOOS我可以设置哪些值来构建 ELF 32 位二进制文 件 GOOS linux and GOARCH 386 更多示例 架构 32 bit gt GOARCH 386 64 bit gt GOARCH
  • Golang const unsafe.Sizeof

    不明白为什么我可以做到 const OK uint64 0 const OK int unsafe Sizeof uint64 0 但不是这个 const NOK binary Size uint64 0 它的解释在规格 https gol
  • 所有可能的 GOOS 价值?

    如果我做对了 GOOS在编译源代码时确定 为了更好地支持多个操作系统 我感兴趣的是GOOS可能 当然 Go 是开源的 所以它可能有无限的可能性 所以我真正想要的是一个 通用列表 已知值为 windows linux darwin or fr
  • 如何在 Go 中获取给定月份的第一个星期一?

    我正在尝试获取给定月份的第一个星期一 我能想到的最好方法是循环前 7 天 然后返回 Weekday Monday 有一个更好的方法吗 通过查看时间的 Weekday 您可以计算出第一个星期一 package main import fmt
  • 管理多租户 ArangoDB 连接

    我使用 ArangoDB Go 使用 go driver 并且需要实现多租户 这意味着每个客户都将在单独的数据库中拥有他的数据 我想要弄清楚的是如何使这种多租户发挥作用 我知道为每个请求创建一个新的数据库连接是不可持续的 这意味着我必须维护
  • 仅导出嵌入结构实现的方法子集

    是否可以仅导出嵌入结构实现的方法的子集 这是一种与减少代码复制和粘贴非常不同的方法吗 还有更惯用的方法吗 type A struct func a A Hello fmt Println Hello func a A World fmt P
  • 可执行文件不在路径中 - GO

    我正在尝试调用命令提示符的内置命令 但出现了我不明白的错误 func main cmd exec Command del C trial now txt Reboot if needed cmd Stdout os Stdout if er
  • 在 Go 中解析多个 JSON 对象

    可以使用以下方法轻松解析如下对象encoding json包裹 something foo something else bar 我面临的问题是当服务器返回多个字典时 如下所示 something foo something else ba
  • Golang:从文本文件中替换字符串中的换行符时出现问题

    我一直在尝试读取一个文件 然后将读取的材料放入字符串中 然后字符串将按行分割成多个字符串 absPath filepath Abs Go input txt data err ioutil ReadFile absPath if err n
  • 在 OSX 上交叉编译 Go?

    我正在尝试在 OSX 上交叉编译 go 应用程序以构建适用于 Windows 和 Linux 的二进制文件 我已经阅读了网上能找到的所有内容 我发现的最接近的例子已经发布在 除了疯狂邮件列表上许多未完成的讨论之外 http solovyov

随机推荐

  • Matlab 快速傅立叶变换 / fft 用于时间和速度

    我有一个 2 列向量 其中包含数据子集的时间和速度 如下所示 5 40 10 37 15 34 20 39 等等 我想要对速度进行傅立叶变换以获得频率 我将如何使用快速傅里叶变换 fft 来做到这一点 如果我的矢量名称是sampleData
  • Python - 处理混合编码文件

    我有一个文件 大部分是 UTF 8 但也有一些 Windows 1252 字符 我创建了一个表来将 Windows 1252 cp1252 字符映射到其 Unicode 对应字符 并希望使用它来修复错误编码的字符 例如 cp1252 to
  • 通过使其成为包装器来优化斐波那契数列递归函数

    斐波那契数列的递归定义在效率方面存在问题 它的定义如下 private fib int n if n lt 2 return n else return fib n 1 fib n 2 假设我们调用 fib 5 这使得 1 次调用 fib
  • 如何在 Amazon OpsWorks 上设置 Chef 的日志输出级别?

    我的问题类似于 如何在控制台中显示 Opscode Chef bash 命令的输出 https stackoverflow com questions 17813592 how can i display the output of a o
  • 在android中动态地将字体添加到textview中

    我是安卓新手 我有一个文本视图 想为其分配自定义字体 我的字体文件 ttf 位于服务器上 我必须在代码中使用该文件来动态设置字体 即时 我不想将文件放在资产文件夹或任何原始文件夹中 如何实施 从服务器下载字体 保存到SD卡 Use Type
  • 修复翻译错误

    liferay 门户中有很多地方翻译成我的语言 sk SK 是错误的 是否可以用 hook 重写那些不好的翻译 任何其他想法都欢迎 多谢 是的 你可以做到 in your liferay hook xml文件添加要覆盖的语言文件的条目 就像
  • 嵌套在结构中的 LINQ 和分组依据数据

    我的结构大致如下 List
  • android.view.WindowManager$BadTokenException:无法在 Toast 处添加窗口

    当我在我的 Android 应用程序上频繁执行某些操作 我的假设是由于 Toast 消息 时 出现以下错误 我没有得到此问题的确切位置 我可以从某人那里获得帮助来解决相同问题吗 beginning of crash 10 04 16 13
  • C# 通用约束问题

    我收到以下错误 类型 Test ICacheProvider 不能用作类型参数 泛型类型或方法中的 TStorageProvider StorageManager Test IFileInfo 没有 隐式引用转换自 StorageManag
  • 将 blob 转换为图像流并将其分配给 jLabel

    我只是想将数据库中的 blob 字符串转换为字节数组 然后在转换后将其转换为缓冲图像 然后将其分配给标签 这是我的代码 package ims project import java sql import javax swing impor
  • jQuery 仅获取此元素的父同级元素

    我不知道如何写这个 请参阅我的标记结构 该结构在页面上重复多次 div class module div class archive info span class archive meta open span div div class
  • Google 地图 fitBounds 无法正常工作

    我对 googlemaps fitBounds 函数有疑问 for var i 0 i lt countries length i var country countries i var latlng new google maps Lat
  • JavaScript 中去除字符串中的所有非数字字符

    考虑一个非 DOM 场景 您希望使用 JavaScript ECMAScript 从字符串中删除所有非数字字符 范围内的任何字符0 9应该保留 var myString abc123 8
  • 如何高效解析固定宽度文件?

    我正在尝试找到一种有效的方法来解析包含固定宽度行的文件 例如 前 20 个字符代表一列 从 21 30 开始代表另一列 依此类推 假设该行包含 100 个字符 将一行解析为多个组成部分的有效方法是什么 我可以对每行使用字符串切片 但如果行很
  • 具有多个条件的布尔索引[重复]

    这个问题在这里已经有答案了 我有一个熊猫DF我需要去哪里filter输出一些包含特征 a 和特征 b 的值 0 的行 为了检查这些值 我运行以下命令 DF1 DF DF a 0 它返回正确的值 同样 通过这样做 DF2 DF DF b 0
  • 来自 Pyspark ArrayType 列的随机样本

    我在 Pyspark 数据框中有一列 其结构如下 Column1 a b c d e c b d f g h i p l m 我想返回另一列 其中随机选择每行中的每个数组 以及函数中指定的数量 所以像data withColumn samp
  • 在套接字上多次调用listen——预期的行为?

    我在使用简单的基于 C 的服务器时注意到一些奇怪的事情 我的 Linux 4 10 3 系统上的程序 我不小心打通了电话listen 我在套接字上两次 来自服务器进程 被称为bind 早些时候 我注意到两个监听电话 成功 没有任何错误 事实
  • NSRulerView 如何将行号与正文正确对齐

    我在 MacOS 中使用 NSRulerView 来显示 NSTextView 旁边的行号 两个视图共享相同的字体和相同的字体大小 但是 在 NSTextView 中 字符串渲染是自动管理的 而在 NSRulerView 中 我需要计算正确
  • Symfony2 - 如何在控制器中使用 __construct() 并访问 Securty.Context?

    我在使用 Symfony2 时遇到了一些问题 即如何使用 construct 函数 官方文档非常糟糕 我希望能够使用以下内容 public function construct parent construct user this gt g
  • 修改Go GRPC服务器流拦截器上的元数据

    我一直在尝试在服务器流拦截器上设置元数据 以便实际的 RPC 函数可以在下游读取它们 func UserIDInterceptor srv interface ss grpc ServerStream info grpc StreamSer