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) | - [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) | - [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 | ## 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: | The type MD is actually a map from string to a list of strings: | ||||||
| 
 | 
 | ||||||
| ```go | ```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 | ## 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 | ### Sending metadata | ||||||
| 
 | 
 | ||||||
|  | @ -127,7 +117,8 @@ Metadata that a client can receive includes header and trailer. | ||||||
| 
 | 
 | ||||||
| #### Unary call | #### 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 | ```go | ||||||
| var header, trailer metadata.MD // variable to store header and trailer | var header, trailer metadata.MD // variable to store header and trailer | ||||||
|  | @ -149,7 +140,8 @@ For streaming calls including: | ||||||
| - Client streaming RPC | - Client streaming RPC | ||||||
| - Bidirectional 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 | ```go | ||||||
| stream, err := client.SomeStreamingRPC(ctx) | stream, err := client.SomeStreamingRPC(ctx) | ||||||
|  | @ -164,11 +156,13 @@ trailer := stream.Trailer() | ||||||
| 
 | 
 | ||||||
| ## Sending and receiving metadata - server side | ## 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 | ### 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. | 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. | 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 | #### 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. | These two functions take a context as the first parameter. | ||||||
| It should be the RPC handler's context or one derived from it: | It should be the RPC handler's context or one derived from it: | ||||||
| 
 | 
 | ||||||
| ```go | ```go | ||||||
| func (s *server) SomeRPC(ctx context.Context, in *pb.someRequest) (*pb.someResponse, error) { | 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") |     header := metadata.Pairs("header-key", "val") | ||||||
|     grpc.SendHeader(ctx, header) |     grpc.SetHeader(ctx, header) | ||||||
|     // create and set trailer |     // create and set trailer | ||||||
|     trailer := metadata.Pairs("trailer-key", "val") |     trailer := metadata.Pairs("trailer-key", "val") | ||||||
|     grpc.SetTrailer(ctx, trailer) |     grpc.SetTrailer(ctx, trailer) | ||||||
|  | @ -211,20 +206,39 @@ func (s *server) SomeRPC(ctx context.Context, in *pb.someRequest) (*pb.someRespo | ||||||
| 
 | 
 | ||||||
| #### Streaming call | #### 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 | ```go | ||||||
| func (s *server) SomeStreamingRPC(stream pb.Service_SomeStreamingRPCServer) error { | func (s *server) SomeStreamingRPC(stream pb.Service_SomeStreamingRPCServer) error { | ||||||
|     // create and send header |     // create and set header | ||||||
|     header := metadata.Pairs("header-key", "val") |     header := metadata.Pairs("header-key", "val") | ||||||
|     stream.SendHeader(header) |     stream.SetHeader(header) | ||||||
|     // create and set trailer |     // create and set trailer | ||||||
|     trailer := metadata.Pairs("trailer-key", "val") |     trailer := metadata.Pairs("trailer-key", "val") | ||||||
|     stream.SetTrailer(trailer) |     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 | ## Updating metadata from a server interceptor | ||||||
| 
 | 
 | ||||||
| An example for updating metadata from a server interceptor is | An example for updating metadata from a server interceptor is | ||||||
| available [here](../examples/features/metadata_interceptor/server/main.go). | 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" | ||||||
| 	"google.golang.org/grpc/credentials/insecure" | 	"google.golang.org/grpc/credentials/insecure" | ||||||
|  | 	"google.golang.org/grpc/metadata" | ||||||
| 
 | 
 | ||||||
| 	pb "google.golang.org/grpc/examples/features/proto/echo" | 	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") | var addr = flag.String("addr", "localhost:50051", "the address to connect to") | ||||||
| 
 | 
 | ||||||
| func callUnaryEcho(ctx context.Context, client pb.EchoClient) { | 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 { | 	if err != nil { | ||||||
| 		log.Fatalf("UnaryEcho: %v", err) | 		log.Fatalf("UnaryEcho: %v", err) | ||||||
| 	} | 	} | ||||||
| 	fmt.Println("UnaryEcho: ", resp.Message) | 	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) { | 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) | 		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() { | func main() { | ||||||
|  |  | ||||||
|  | @ -49,10 +49,22 @@ func unaryInterceptor(ctx context.Context, req any, info *grpc.UnaryServerInfo, | ||||||
| 		return nil, errMissingMetadata | 		return nil, errMissingMetadata | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// Create and set metadata from interceptor to server.
 | ||||||
| 	md.Append("key1", "value1") | 	md.Append("key1", "value1") | ||||||
| 	ctx = metadata.NewIncomingContext(ctx, md) | 	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) { | 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 | 		return errMissingMetadata | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// Create and set metadata from interceptor to server.
 | ||||||
| 	md.Append("key1", "value1") | 	md.Append("key1", "value1") | ||||||
| 	ctx := metadata.NewIncomingContext(ss.Context(), md) | 	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 { | func (s *server) BidirectionalStreamingEcho(stream pb.Echo_BidirectionalStreamingEchoServer) error { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue