Skip to content

Commit ecced0c

Browse files
authored
Update gRPC client docs with trailer information (#17946)
1 parent 0bd997b commit ecced0c

1 file changed

Lines changed: 102 additions & 44 deletions

File tree

aspnetcore/grpc/client.md

Lines changed: 102 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ author: jamesnk
44
description: Learn how to call gRPC services with the .NET gRPC client.
55
monikerRange: '>= aspnetcore-3.0'
66
ms.author: jamesnk
7-
ms.date: 08/21/2019
7+
ms.date: 04/21/2020
88
uid: grpc/client
99
---
1010
# Call gRPC services with the .NET client
@@ -44,7 +44,7 @@ Channel and client performance and usage:
4444
* A channel and clients created from the channel can safely be used by multiple threads.
4545
* Clients created from the channel can make multiple simultaneous calls.
4646

47-
`GrpcChannel.ForAddress` isn't the only option for creating a gRPC client. If you're calling gRPC services from an ASP.NET Core app, consider [gRPC client factory integration](xref:grpc/clientfactory). gRPC integration with `HttpClientFactory` offers a centralized alternative to creating gRPC clients.
47+
`GrpcChannel.ForAddress` isn't the only option for creating a gRPC client. If calling gRPC services from an ASP.NET Core app, consider [gRPC client factory integration](xref:grpc/clientfactory). gRPC integration with `HttpClientFactory` offers a centralized alternative to creating gRPC clients.
4848

4949
> [!NOTE]
5050
> Additional configuration is required to [call insecure gRPC services with the .NET client](xref:grpc/troubleshoot#call-insecure-grpc-services-with-net-core-client).
@@ -56,7 +56,7 @@ Channel and client performance and usage:
5656

5757
A gRPC call is initiated by calling a method on the client. The gRPC client will handle message serialization and addressing the gRPC call to the correct service.
5858

59-
gRPC has different types of methods. How you use the client to make a gRPC call depends on the type of method you are calling. The gRPC method types are:
59+
gRPC has different types of methods. How the client is used to make a gRPC call depends on the type of method called. The gRPC method types are:
6060

6161
* Unary
6262
* Server streaming
@@ -96,78 +96,136 @@ using (var call = client.SayHellos(new HelloRequest { Name = "World" }))
9696
}
9797
```
9898

99-
If you are using C# 8 or later, the `await foreach` syntax can be used to read messages. The `IAsyncStreamReader<T>.ReadAllAsync()` extension method reads all messages from the response stream:
99+
When using C# 8 or later, the `await foreach` syntax can be used to read messages. The `IAsyncStreamReader<T>.ReadAllAsync()` extension method reads all messages from the response stream:
100100

101101
```csharp
102102
var client = new Greet.GreeterClient(channel);
103-
using (var call = client.SayHellos(new HelloRequest { Name = "World" }))
103+
using var call = client.SayHellos(new HelloRequest { Name = "World" });
104+
105+
await foreach (var response in call.ResponseStream.ReadAllAsync())
104106
{
105-
await foreach (var response in call.ResponseStream.ReadAllAsync())
106-
{
107-
Console.WriteLine("Greeting: " + response.Message);
108-
// "Greeting: Hello World" is written multiple times
109-
}
107+
Console.WriteLine("Greeting: " + response.Message);
108+
// "Greeting: Hello World" is written multiple times
110109
}
111110
```
112111

113112
### Client streaming call
114113

115-
A client streaming call starts *without* the client sending a message. The client can choose to send messages with `RequestStream.WriteAsync`. When the client has finished sending messages `RequestStream.CompleteAsync` should be called to notify the service. The call is finished when the service returns a response message.
114+
A client streaming call starts *without* the client sending a message. The client can choose to send messages with `RequestStream.WriteAsync`. When the client has finished sending messages, `RequestStream.CompleteAsync` should be called to notify the service. The call is finished when the service returns a response message.
116115

117116
```csharp
118117
var client = new Counter.CounterClient(channel);
119-
using (var call = client.AccumulateCount())
120-
{
121-
for (var i = 0; i < 3; i++)
122-
{
123-
await call.RequestStream.WriteAsync(new CounterRequest { Count = 1 });
124-
}
125-
await call.RequestStream.CompleteAsync();
118+
using var call = client.AccumulateCount();
126119

127-
var response = await call;
128-
Console.WriteLine($"Count: {response.Count}");
129-
// Count: 3
120+
for (var i = 0; i < 3; i++)
121+
{
122+
await call.RequestStream.WriteAsync(new CounterRequest { Count = 1 });
130123
}
124+
await call.RequestStream.CompleteAsync();
125+
126+
var response = await call;
127+
Console.WriteLine($"Count: {response.Count}");
128+
// Count: 3
131129
```
132130

133131
### Bi-directional streaming call
134132

135133
A bi-directional streaming call starts *without* the client sending a message. The client can choose to send messages with `RequestStream.WriteAsync`. Messages streamed from the service are accessible with `ResponseStream.MoveNext()` or `ResponseStream.ReadAllAsync()`. The bi-directional streaming call is complete when the `ResponseStream` has no more messages.
136134

137135
```csharp
138-
using (var call = client.Echo())
136+
var client = new Echo.EchoClient(channel);
137+
using var call = client.Echo();
138+
139+
Console.WriteLine("Starting background task to receive messages");
140+
var readTask = Task.Run(async () =>
139141
{
140-
Console.WriteLine("Starting background task to receive messages");
141-
var readTask = Task.Run(async () =>
142-
{
143-
await foreach (var response in call.ResponseStream.ReadAllAsync())
144-
{
145-
Console.WriteLine(response.Message);
146-
// Echo messages sent to the service
147-
}
148-
});
149-
150-
Console.WriteLine("Starting to send messages");
151-
Console.WriteLine("Type a message to echo then press enter.");
152-
while (true)
142+
await foreach (var response in call.ResponseStream.ReadAllAsync())
153143
{
154-
var result = Console.ReadLine();
155-
if (string.IsNullOrEmpty(result))
156-
{
157-
break;
158-
}
144+
Console.WriteLine(response.Message);
145+
// Echo messages sent to the service
146+
}
147+
});
159148

160-
await call.RequestStream.WriteAsync(new EchoMessage { Message = result });
149+
Console.WriteLine("Starting to send messages");
150+
Console.WriteLine("Type a message to echo then press enter.");
151+
while (true)
152+
{
153+
var result = Console.ReadLine();
154+
if (string.IsNullOrEmpty(result))
155+
{
156+
break;
161157
}
162158

163-
Console.WriteLine("Disconnecting");
164-
await call.RequestStream.CompleteAsync();
165-
await readTask;
159+
await call.RequestStream.WriteAsync(new EchoMessage { Message = result });
166160
}
161+
162+
Console.WriteLine("Disconnecting");
163+
await call.RequestStream.CompleteAsync();
164+
await readTask;
167165
```
168166

169167
During a bi-directional streaming call, the client and service can send messages to each other at any time. The best client logic for interacting with a bi-directional call varies depending upon the service logic.
170168

169+
## Access gRPC trailers
170+
171+
gRPC calls may return gRPC trailers. gRPC trailers are used to provide name/value metadata about a call. Trailers provide similar functionality to HTTP headers, but are received at the end of the call.
172+
173+
gRPC trailers are accessible using `GetTrailers()`, which returns a collection of metadata. Trailers are returned after the response is complete, therefore, you must await all response messages before accessing the trailers.
174+
175+
Unary and client streaming calls must await `ResponseAsync` before calling `GetTrailers()`:
176+
177+
```csharp
178+
var client = new Greet.GreeterClient(channel);
179+
using var call = client.SayHelloAsync(new HelloRequest { Name = "World" });
180+
var response = await call.ResponseAsync;
181+
182+
Console.WriteLine("Greeting: " + response.Message);
183+
// Greeting: Hello World
184+
185+
var trailers = call.GetTrailers();
186+
var myValue = trailers.First(e => e.Key == "my-trailer-name");
187+
```
188+
189+
Server and bidirectional streaming calls must finish awaiting the response stream before calling `GetTrailers()`:
190+
191+
```csharp
192+
var client = new Greet.GreeterClient(channel);
193+
using var call = client.SayHellos(new HelloRequest { Name = "World" });
194+
195+
await foreach (var response in call.ResponseStream.ReadAllAsync())
196+
{
197+
Console.WriteLine("Greeting: " + response.Message);
198+
// "Greeting: Hello World" is written multiple times
199+
}
200+
201+
var trailers = call.GetTrailers();
202+
var myValue = trailers.First(e => e.Key == "my-trailer-name");
203+
```
204+
205+
gRPC trailers are also accessible from `RpcException`. A service may return trailers together with a non-OK gRPC status. In this situation the trailers are retrieved from the exception thrown by the gRPC client:
206+
207+
```csharp
208+
var client = new Greet.GreeterClient(channel);
209+
string myValue = null;
210+
211+
try
212+
{
213+
using var call = client.SayHelloAsync(new HelloRequest { Name = "World" });
214+
var response = await call.ResponseAsync;
215+
216+
Console.WriteLine("Greeting: " + response.Message);
217+
// Greeting: Hello World
218+
219+
var trailers = call.GetTrailers();
220+
myValue = trailers.First(e => e.Key == "my-trailer-name");
221+
}
222+
catch (RpcException ex)
223+
{
224+
var trailers = ex.Trailers;
225+
myValue = trailers.First(e => e.Key == "my-trailer-name");
226+
}
227+
```
228+
171229
## Additional resources
172230

173231
* <xref:grpc/clientfactory>

0 commit comments

Comments
 (0)