发送元数据
在 gRPC 中,除了使用上下文进行请求取消和设置超时外,还可以通过上下文传递元数据。元数据可以是 HTTP 头部或 HTTP 尾部,它们都是键值对的列表,通常用于很多用途,例如传递身份验证令牌、数字签名、数据完整性等。在本节中,我们将重点介绍如何通过头部传递元数据。尾部是发送在消息之后的头部,通常开发人员使用较少,但 gRPC 在实现流接口时会使用它们。如果你对尾部感兴趣,可以查看 grpc.SetTrailer
函数( SetTrailer )。
我们的用例是向 UpdateTasks
RPC 接口传递一个身份验证令牌,服务器验证令牌后,根据验证结果决定是更新任务还是返回 Unauthenticated
错误。当然,我们并不会涉及如何生成身份验证令牌的问题,假设正确的令牌是 authd
,其它所有令牌都会被视为不正确。
服务器接收数据时可以从上下文中提取元数据,因此我们将使用 gRPC 中 metadata
包的 FromIncomingContext
函数来提取元数据。该函数返回一个元数据的映射,并告诉我们是否成功提取到了元数据。在 UpdateTasks
中,我们可以这样做:
func (s *server) UpdateTasks(stream pb.TodoService_UpdateTasksServer) error {
ctx := stream.Context()
md, _ := metadata.FromIncomingContext(ctx)
// ...
}
现在,我们可以检查是否传递了身份验证令牌。为此,我们使用 Go 的映射(map)来访问 auth_token
元素。如果存在该键,它会返回值和一个布尔值,表示该键是否存在于映射中。如果存在,我们会检查它是否只有一个值,并且这个值是否为 "authd"。如果不是,我们返回一个 Unauthenticated
错误:
func (s *server) UpdateTasks(stream pb.TodoService_UpdateTasksServer) error {
ctx := stream.Context()
md, _ := metadata.FromIncomingContext(ctx)
if t, ok := md["auth_token"]; ok {
switch {
case len(t) != 1:
return status.Errorf(
codes.InvalidArgument,
"auth_token should contain only 1 value",
)
case t[0] != "authd":
return status.Errorf(
codes.Unauthenticated,
"incorrect auth_token",
)
}
} else {
return status.Errorf(
codes.Unauthenticated,
"failed to get auth_token",
)
}
// ...
}
这就是服务器端的全部内容;如果没有元数据、如果有元数据但没有 auth_token
、如果 auth_token
有多个值,或者如果 auth_token
的值不是 "authd",我们都会返回一个错误。
现在我们可以转到客户端,发送适当的请求头。这可以通过元数据包中的另一个函数 AppendToOutgoingContext
来完成。我们知道在调用端点之前,已经创建了一个上下文,因此我们只需要将 auth_token
添加到该上下文中。实现起来非常简单,如下所示:
func updateTasks(c pb.TodoServiceClient, reqs ...*pb.UpdateTasksRequest) {
ctx := context.Background()
ctx = metadata.AppendToOutgoingContext(ctx, "auth_token", "authd")
stream, err := c.UpdateTasks(ctx)
// ...
}
我们用新的上下文覆盖了之前创建的上下文,这样就添加了包含键值对的元数据。请注意,键值对是可以交替添加的,也就是说,我们可以像下面这样同时传递多个键值对:
metadata.AppendToOutgoingContext(ctx, K1, V1, K2, V2, ...)
其中 K
表示键,V
表示值。
现在我们可以运行服务器:
$ go run ./server 0.0.0.0:50051
listening at 0.0.0.0:50051
然后运行客户端:
$ go run ./client 0.0.0.0:50051
如果一切正常,客户端会发送请求并得到相应的响应。然而,如果你将 auth_token
的值设置为非 "authd",你应该看到以下错误信息:
unexpected error: rpc error: code = Unauthenticated desc = incorrect auth_token
如果你没有设置 auth_token
头部,你会看到以下错误信息:
unexpected error: rpc error: code = Unauthenticated desc = failed to get auth_token
如果你设置了多个 auth_token
的值,比如:
ctx = metadata.AppendToOutgoingContext(ctx, "auth_token", "authd", "auth_token", "authd")
你将看到以下错误信息:
unexpected error: rpc error: code = InvalidArgument desc = auth_token should contain only 1 value
通过本节,我们学习了如何从上下文中获取元数据,使用 metadata.FromIncomingContext
函数提取元数据,并处理可能出现的各种错误。同时,我们也学会了如何通过 metadata.AppendToOutgoingContext
函数将元数据从客户端发送到服务器。