mirror of https://github.com/grpc/grpc-go.git
documentation: on server, use FromIncomingContext for retrieving context and `SetHeader`, `SetTrailer` to send metadata to client (#7238)
This commit is contained in:
parent
7e5898e7c5
commit
6d236200ea
|
@ -12,11 +12,11 @@ Four kinds of service method:
|
|||
- [Client streaming RPC](https://grpc.io/docs/guides/concepts.html#client-streaming-rpc)
|
||||
- [Bidirectional streaming RPC](https://grpc.io/docs/guides/concepts.html#bidirectional-streaming-rpc)
|
||||
|
||||
And concept of [metadata](https://grpc.io/docs/guides/concepts.html#metadata).
|
||||
And concept of [metadata].
|
||||
|
||||
## Constructing metadata
|
||||
|
||||
A metadata can be created using package [metadata](https://godoc.org/google.golang.org/grpc/metadata).
|
||||
A metadata can be created using package [metadata].
|
||||
The type MD is actually a map from string to a list of strings:
|
||||
|
||||
```go
|
||||
|
@ -64,20 +64,10 @@ md := metadata.Pairs(
|
|||
)
|
||||
```
|
||||
|
||||
## Retrieving metadata from context
|
||||
|
||||
Metadata can be retrieved from context using `FromIncomingContext`:
|
||||
|
||||
```go
|
||||
func (s *server) SomeRPC(ctx context.Context, in *pb.SomeRequest) (*pb.SomeResponse, err) {
|
||||
md, ok := metadata.FromIncomingContext(ctx)
|
||||
// do something with metadata
|
||||
}
|
||||
```
|
||||
|
||||
## Sending and receiving metadata - client side
|
||||
|
||||
Client side metadata sending and receiving examples are available [here](../examples/features/metadata/client/main.go).
|
||||
Client side metadata sending and receiving examples are available
|
||||
[here](../examples/features/metadata/client/main.go).
|
||||
|
||||
### Sending metadata
|
||||
|
||||
|
@ -127,7 +117,8 @@ Metadata that a client can receive includes header and trailer.
|
|||
|
||||
#### Unary call
|
||||
|
||||
Header and trailer sent along with a unary call can be retrieved using function [Header](https://godoc.org/google.golang.org/grpc#Header) and [Trailer](https://godoc.org/google.golang.org/grpc#Trailer) in [CallOption](https://godoc.org/google.golang.org/grpc#CallOption):
|
||||
Header and trailer sent along with a unary call can be retrieved using function
|
||||
[Header] and [Trailer] in [CallOption]:
|
||||
|
||||
```go
|
||||
var header, trailer metadata.MD // variable to store header and trailer
|
||||
|
@ -149,7 +140,8 @@ For streaming calls including:
|
|||
- Client streaming RPC
|
||||
- Bidirectional streaming RPC
|
||||
|
||||
Header and trailer can be retrieved from the returned stream using function `Header` and `Trailer` in interface [ClientStream](https://godoc.org/google.golang.org/grpc#ClientStream):
|
||||
Header and trailer can be retrieved from the returned stream using function
|
||||
`Header` and `Trailer` in interface [ClientStream]:
|
||||
|
||||
```go
|
||||
stream, err := client.SomeStreamingRPC(ctx)
|
||||
|
@ -164,11 +156,13 @@ trailer := stream.Trailer()
|
|||
|
||||
## Sending and receiving metadata - server side
|
||||
|
||||
Server side metadata sending and receiving examples are available [here](../examples/features/metadata/server/main.go).
|
||||
Server side metadata sending and receiving examples are available
|
||||
[here](../examples/features/metadata/server/main.go).
|
||||
|
||||
### Receiving metadata
|
||||
|
||||
To read metadata sent by the client, the server needs to retrieve it from RPC context.
|
||||
To read metadata sent by the client, the server needs to retrieve it from RPC
|
||||
context using [FromIncomingContext].
|
||||
If it is a unary call, the RPC handler's context can be used.
|
||||
For streaming calls, the server needs to get context from the stream.
|
||||
|
||||
|
@ -194,15 +188,16 @@ func (s *server) SomeStreamingRPC(stream pb.Service_SomeStreamingRPCServer) erro
|
|||
|
||||
#### Unary call
|
||||
|
||||
To send header and trailer to client in unary call, the server can call [SendHeader](https://godoc.org/google.golang.org/grpc#SendHeader) and [SetTrailer](https://godoc.org/google.golang.org/grpc#SetTrailer) functions in module [grpc](https://godoc.org/google.golang.org/grpc).
|
||||
To send header and trailer to client in unary call, the server can call
|
||||
[SetHeader] and [SetTrailer] functions in module [grpc].
|
||||
These two functions take a context as the first parameter.
|
||||
It should be the RPC handler's context or one derived from it:
|
||||
|
||||
```go
|
||||
func (s *server) SomeRPC(ctx context.Context, in *pb.someRequest) (*pb.someResponse, error) {
|
||||
// create and send header
|
||||
// create and set header
|
||||
header := metadata.Pairs("header-key", "val")
|
||||
grpc.SendHeader(ctx, header)
|
||||
grpc.SetHeader(ctx, header)
|
||||
// create and set trailer
|
||||
trailer := metadata.Pairs("trailer-key", "val")
|
||||
grpc.SetTrailer(ctx, trailer)
|
||||
|
@ -211,20 +206,39 @@ func (s *server) SomeRPC(ctx context.Context, in *pb.someRequest) (*pb.someRespo
|
|||
|
||||
#### Streaming call
|
||||
|
||||
For streaming calls, header and trailer can be sent using function `SendHeader` and `SetTrailer` in interface [ServerStream](https://godoc.org/google.golang.org/grpc#ServerStream):
|
||||
For streaming calls, header and trailer can be sent using function
|
||||
[SetHeader] and [SetTrailer] in interface [ServerStream]:
|
||||
|
||||
```go
|
||||
func (s *server) SomeStreamingRPC(stream pb.Service_SomeStreamingRPCServer) error {
|
||||
// create and send header
|
||||
// create and set header
|
||||
header := metadata.Pairs("header-key", "val")
|
||||
stream.SendHeader(header)
|
||||
stream.SetHeader(header)
|
||||
// create and set trailer
|
||||
trailer := metadata.Pairs("trailer-key", "val")
|
||||
stream.SetTrailer(trailer)
|
||||
}
|
||||
```
|
||||
|
||||
**Important**
|
||||
|
||||
Do not use
|
||||
[FromOutgoingContext] on the server to write metadata to be sent to the client.
|
||||
[FromOutgoingContext] is for client-side use only.
|
||||
|
||||
## Updating metadata from a server interceptor
|
||||
|
||||
An example for updating metadata from a server interceptor is
|
||||
available [here](../examples/features/metadata_interceptor/server/main.go).
|
||||
|
||||
[FromIncomingContext]: <https://pkg.go.dev/google.golang.org/grpc/metadata#FromIncomingContext>
|
||||
[SetHeader]: <https://godoc.org/google.golang.org/grpc#SetHeader>
|
||||
[SetTrailer]: https://godoc.org/google.golang.org/grpc#SetTrailer
|
||||
[FromOutgoingContext]: https://pkg.go.dev/google.golang.org/grpc/metadata#FromOutgoingContext
|
||||
[ServerStream]: https://godoc.org/google.golang.org/grpc#ServerStream
|
||||
[grpc]: https://godoc.org/google.golang.org/grpc
|
||||
[ClientStream]: https://godoc.org/google.golang.org/grpc#ClientStream
|
||||
[Header]: https://godoc.org/google.golang.org/grpc#Header
|
||||
[Trailer]: https://godoc.org/google.golang.org/grpc#Trailer
|
||||
[CallOption]: https://godoc.org/google.golang.org/grpc#CallOption
|
||||
[metadata]: https://godoc.org/google.golang.org/grpc/metadata
|
|
@ -29,6 +29,7 @@ import (
|
|||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
pb "google.golang.org/grpc/examples/features/proto/echo"
|
||||
)
|
||||
|
@ -36,11 +37,22 @@ import (
|
|||
var addr = flag.String("addr", "localhost:50051", "the address to connect to")
|
||||
|
||||
func callUnaryEcho(ctx context.Context, client pb.EchoClient) {
|
||||
resp, err := client.UnaryEcho(ctx, &pb.EchoRequest{Message: "hello world"})
|
||||
var header, trailer metadata.MD
|
||||
resp, err := client.UnaryEcho(ctx, &pb.EchoRequest{Message: "hello world"}, grpc.Header(&header), grpc.Trailer(&trailer))
|
||||
if err != nil {
|
||||
log.Fatalf("UnaryEcho: %v", err)
|
||||
}
|
||||
fmt.Println("UnaryEcho: ", resp.Message)
|
||||
|
||||
fmt.Println("Received headers:")
|
||||
for k, v := range header {
|
||||
fmt.Printf("%s: %v\n", k, v)
|
||||
}
|
||||
|
||||
fmt.Println("Received trailers:")
|
||||
for k, v := range trailer {
|
||||
fmt.Printf("%s: %v\n", k, v)
|
||||
}
|
||||
}
|
||||
|
||||
func callBidiStreamingEcho(ctx context.Context, client pb.EchoClient) {
|
||||
|
@ -64,6 +76,21 @@ func callBidiStreamingEcho(ctx context.Context, client pb.EchoClient) {
|
|||
}
|
||||
fmt.Println("BidiStreaming Echo: ", resp.Message)
|
||||
}
|
||||
|
||||
header, err := c.Header()
|
||||
if err != nil {
|
||||
log.Fatalf("Receiving headers: %v", err)
|
||||
}
|
||||
fmt.Println("Received headers:")
|
||||
for k, v := range header {
|
||||
fmt.Printf("%s: %v\n", k, v)
|
||||
}
|
||||
|
||||
trailer := c.Trailer()
|
||||
fmt.Println("Received tailers:")
|
||||
for k, v := range trailer {
|
||||
fmt.Printf("%s: %v\n", k, v)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
|
|
@ -49,10 +49,22 @@ func unaryInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo,
|
|||
return nil, errMissingMetadata
|
||||
}
|
||||
|
||||
// Create and set metadata from interceptor to server.
|
||||
md.Append("key1", "value1")
|
||||
ctx = metadata.NewIncomingContext(ctx, md)
|
||||
|
||||
return handler(ctx, req)
|
||||
// Call the handler to complete the normal execution of the RPC.
|
||||
resp, err := handler(ctx, req)
|
||||
|
||||
// Create and set header metadata from interceptor to client.
|
||||
header := metadata.Pairs("header-key", "val")
|
||||
grpc.SetHeader(ctx, header)
|
||||
|
||||
// Create and set trailer metadata from interceptor to client.
|
||||
trailer := metadata.Pairs("trailer-key", "val")
|
||||
grpc.SetTrailer(ctx, trailer)
|
||||
|
||||
return resp, err
|
||||
}
|
||||
|
||||
func (s *server) UnaryEcho(ctx context.Context, in *pb.EchoRequest) (*pb.EchoResponse, error) {
|
||||
|
@ -89,10 +101,22 @@ func streamInterceptor(srv any, ss grpc.ServerStream, info *grpc.StreamServerInf
|
|||
return errMissingMetadata
|
||||
}
|
||||
|
||||
// Create and set metadata from interceptor to server.
|
||||
md.Append("key1", "value1")
|
||||
ctx := metadata.NewIncomingContext(ss.Context(), md)
|
||||
|
||||
return handler(srv, &wrappedStream{ss, ctx})
|
||||
// Call the handler to complete the normal execution of the RPC.
|
||||
err := handler(srv, &wrappedStream{ss, ctx})
|
||||
|
||||
// Create and set header metadata from interceptor to client.
|
||||
header := metadata.Pairs("header-key", "val")
|
||||
ss.SetHeader(header)
|
||||
|
||||
// Create and set trailer metadata from interceptor to client.
|
||||
trailer := metadata.Pairs("trailer-key", "val")
|
||||
ss.SetTrailer(trailer)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *server) BidirectionalStreamingEcho(stream pb.Echo_BidirectionalStreamingEchoServer) error {
|
||||
|
|
Loading…
Reference in New Issue