gitee.com/ks-custle/core-gm@v0.0.0-20230922171213-b83bdd97b62c/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  ```