github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/grpc/Documentation/grpc-metadata.md (about) 1 # Metadata 2 3 gRPC supports sending metadata between client and server. 4 This doc shows how to send and receive metadata in gRPC-go. 5 6 ## Background 7 8 Four kinds of service method: 9 10 - [Unary RPC](https://grpc.io/docs/guides/concepts.html#unary-rpc) 11 - [Server streaming RPC](https://grpc.io/docs/guides/concepts.html#server-streaming-rpc) 12 - [Client streaming RPC](https://grpc.io/docs/guides/concepts.html#client-streaming-rpc) 13 - [Bidirectional streaming RPC](https://grpc.io/docs/guides/concepts.html#bidirectional-streaming-rpc) 14 15 And concept of [metadata](https://grpc.io/docs/guides/concepts.html#metadata). 16 17 ## Constructing metadata 18 19 A metadata can be created using package [metadata](https://godoc.org/google.golang.org/grpc/metadata). 20 The type MD is actually a map from string to a list of strings: 21 22 ```go 23 type MD map[string][]string 24 ``` 25 26 Metadata can be read like a normal map. 27 Note that the value type of this map is `[]string`, 28 so that users can attach multiple values using a single key. 29 30 ### Creating a new metadata 31 32 A metadata can be created from a `map[string]string` using function `New`: 33 34 ```go 35 md := metadata.New(map[string]string{"key1": "val1", "key2": "val2"}) 36 ``` 37 38 Another way is to use `Pairs`. 39 Values with the same key will be merged into a list: 40 41 ```go 42 md := metadata.Pairs( 43 "key1", "val1", 44 "key1", "val1-2", // "key1" will have map value []string{"val1", "val1-2"} 45 "key2", "val2", 46 ) 47 ``` 48 49 __Note:__ all the keys will be automatically converted to lowercase, 50 so "key1" and "kEy1" will be the same key and their values will be merged into the same list. 51 This happens for both `New` and `Pairs`. 52 53 ### Storing binary data in metadata 54 55 In metadata, keys are always strings. But values can be strings or binary data. 56 To store binary data value in metadata, simply add "-bin" suffix to the key. 57 The values with "-bin" suffixed keys will be encoded when creating the metadata: 58 59 ```go 60 md := metadata.Pairs( 61 "key", "string value", 62 "key-bin", string([]byte{96, 102}), // this binary data will be encoded (base64) before sending 63 // and will be decoded after being transferred. 64 ) 65 ``` 66 67 ## Retrieving metadata from context 68 69 Metadata can be retrieved from context using `FromIncomingContext`: 70 71 ```go 72 func (s *server) SomeRPC(ctx context.Context, in *pb.SomeRequest) (*pb.SomeResponse, err) { 73 md, ok := metadata.FromIncomingContext(ctx) 74 // do something with metadata 75 } 76 ``` 77 78 ## Sending and receiving metadata - client side 79 80 Client side metadata sending and receiving examples are available [here](../examples/features/metadata/client/main.go). 81 82 ### Sending metadata 83 84 There are two ways to send metadata to the server. The recommended way is to append kv pairs to the context using 85 `AppendToOutgoingContext`. This can be used with or without existing metadata on the context. When there is no prior 86 metadata, metadata is added; when metadata already exists on the context, kv pairs are merged in. 87 88 ```go 89 // create a new context with some metadata 90 ctx := metadata.AppendToOutgoingContext(ctx, "k1", "v1", "k1", "v2", "k2", "v3") 91 92 // later, add some more metadata to the context (e.g. in an interceptor) 93 ctx := metadata.AppendToOutgoingContext(ctx, "k3", "v4") 94 95 // make unary RPC 96 response, err := client.SomeRPC(ctx, someRequest) 97 98 // or make streaming RPC 99 stream, err := client.SomeStreamingRPC(ctx) 100 ``` 101 102 Alternatively, metadata may be attached to the context using `NewOutgoingContext`. However, this 103 replaces any existing metadata in the context, so care must be taken to preserve the existing 104 metadata if desired. This is slower than using `AppendToOutgoingContext`. An example of this 105 is below: 106 107 ```go 108 // create a new context with some metadata 109 md := metadata.Pairs("k1", "v1", "k1", "v2", "k2", "v3") 110 ctx := metadata.NewOutgoingContext(context.Background(), md) 111 112 // later, add some more metadata to the context (e.g. in an interceptor) 113 send, _ := metadata.FromOutgoingContext(ctx) 114 newMD := metadata.Pairs("k3", "v3") 115 ctx = metadata.NewOutgoingContext(ctx, metadata.Join(send, newMD)) 116 117 // make unary RPC 118 response, err := client.SomeRPC(ctx, someRequest) 119 120 // or make streaming RPC 121 stream, err := client.SomeStreamingRPC(ctx) 122 ``` 123 124 ### Receiving metadata 125 126 Metadata that a client can receive includes header and trailer. 127 128 #### Unary call 129 130 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): 131 132 ```go 133 var header, trailer metadata.MD // variable to store header and trailer 134 r, err := client.SomeRPC( 135 ctx, 136 someRequest, 137 grpc.Header(&header), // will retrieve header 138 grpc.Trailer(&trailer), // will retrieve trailer 139 ) 140 141 // do something with header and trailer 142 ``` 143 144 #### Streaming call 145 146 For streaming calls including: 147 148 - Server streaming RPC 149 - Client streaming RPC 150 - Bidirectional streaming RPC 151 152 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): 153 154 ```go 155 stream, err := client.SomeStreamingRPC(ctx) 156 157 // retrieve header 158 header, err := stream.Header() 159 160 // retrieve trailer 161 trailer := stream.Trailer() 162 163 ``` 164 165 ## Sending and receiving metadata - server side 166 167 Server side metadata sending and receiving examples are available [here](../examples/features/metadata/server/main.go). 168 169 ### Receiving metadata 170 171 To read metadata sent by the client, the server needs to retrieve it from RPC context. 172 If it is a unary call, the RPC handler's context can be used. 173 For streaming calls, the server needs to get context from the stream. 174 175 #### Unary call 176 177 ```go 178 func (s *server) SomeRPC(ctx context.Context, in *pb.someRequest) (*pb.someResponse, error) { 179 md, ok := metadata.FromIncomingContext(ctx) 180 // do something with metadata 181 } 182 ``` 183 184 #### Streaming call 185 186 ```go 187 func (s *server) SomeStreamingRPC(stream pb.Service_SomeStreamingRPCServer) error { 188 md, ok := metadata.FromIncomingContext(stream.Context()) // get context from stream 189 // do something with metadata 190 } 191 ``` 192 193 ### Sending metadata 194 195 #### Unary call 196 197 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). 198 These two functions take a context as the first parameter. 199 It should be the RPC handler's context or one derived from it: 200 201 ```go 202 func (s *server) SomeRPC(ctx context.Context, in *pb.someRequest) (*pb.someResponse, error) { 203 // create and send header 204 header := metadata.Pairs("header-key", "val") 205 grpc.SendHeader(ctx, header) 206 // create and set trailer 207 trailer := metadata.Pairs("trailer-key", "val") 208 grpc.SetTrailer(ctx, trailer) 209 } 210 ``` 211 212 #### Streaming call 213 214 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): 215 216 ```go 217 func (s *server) SomeStreamingRPC(stream pb.Service_SomeStreamingRPCServer) error { 218 // create and send header 219 header := metadata.Pairs("header-key", "val") 220 stream.SendHeader(header) 221 // create and set trailer 222 trailer := metadata.Pairs("trailer-key", "val") 223 stream.SetTrailer(trailer) 224 } 225 ```