AspNetCore.Docs/aspnetcore/grpc/clientfactory.md

7.1 KiB

title author description monikerRange ms.author ms.date no-loc uid
gRPC client factory integration in .NET jamesnk Learn how to create gRPC clients using the client factory. >= aspnetcore-3.0 jamesnk 04/01/2021
Home
Privacy
Kestrel
appsettings.json
ASP.NET Core Identity
cookie
Cookie
Blazor
Blazor Server
Blazor WebAssembly
Identity
Let's Encrypt
Razor
SignalR
grpc/clientfactory

gRPC client factory integration in .NET

By James Newton-King

gRPC integration with HttpClientFactory offers a centralized way to create gRPC clients. It can be used as an alternative to configuring stand-alone gRPC client instances. Factory integration is available in the Grpc.Net.ClientFactory NuGet package.

The factory offers the following benefits:

  • Provides a central location for configuring logical gRPC client instances
  • Manages the lifetime of the underlying HttpClientMessageHandler
  • Automatic propagation of deadline and cancellation in an ASP.NET Core gRPC service

Register gRPC clients

To register a gRPC client, the generic AddGrpcClient extension method can be used within Startup.ConfigureServices, specifying the gRPC typed client class and service address:

services.AddGrpcClient<Greeter.GreeterClient>(o =>
{
    o.Address = new Uri("https://localhost:5001");
});

The gRPC client type is registered as transient with dependency injection (DI). The client can now be injected and consumed directly in types created by DI. ASP.NET Core MVC controllers, SignalR hubs and gRPC services are places where gRPC clients can automatically be injected:

public class AggregatorService : Aggregator.AggregatorBase
{
    private readonly Greeter.GreeterClient _client;

    public AggregatorService(Greeter.GreeterClient client)
    {
        _client = client;
    }

    public override async Task SayHellos(HelloRequest request,
        IServerStreamWriter<HelloReply> responseStream, ServerCallContext context)
    {
        // Forward the call on to the greeter service
        using (var call = _client.SayHellos(request))
        {
            await foreach (var response in call.ResponseStream.ReadAllAsync())
            {
                await responseStream.WriteAsync(response);
            }
        }
    }
}

Configure HttpHandler

HttpClientFactory creates the HttpMessageHandler used by the gRPC client. Standard HttpClientFactory methods can be used to add outgoing request middleware or to configure the underlying HttpClientHandler of the HttpClient:

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        var handler = new HttpClientHandler();
        handler.ClientCertificates.Add(LoadCertificate());
        return handler;
    });

For more information, see Make HTTP requests using IHttpClientFactory.

Configure Channel and Interceptors

gRPC-specific methods are available to:

  • Configure a gRPC client's underlying channel.
  • Add Interceptor instances that the client will use when making gRPC calls.
services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .AddInterceptor(() => new LoggingInterceptor())
    .ConfigureChannel(o =>
    {
        o.Credentials = new CustomCredentials();
    });

A gRPC interceptor or channel credentials can be used to send Authorization metadata with each request. For more information about configuring authentication, see Send a bearer token with gRPC client factory.

Deadline and cancellation propagation

gRPC clients created by the factory in a gRPC service can be configured with EnableCallContextPropagation() to automatically propagate the deadline and cancellation token to child calls. The EnableCallContextPropagation() extension method is available in the Grpc.AspNetCore.Server.ClientFactory NuGet package.

Call context propagation works by reading the deadline and cancellation token from the current gRPC request context and automatically propagating them to outgoing calls made by the gRPC client. Call context propagation is an excellent way of ensuring that complex, nested gRPC scenarios always propagate the deadline and cancellation.

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .EnableCallContextPropagation();

By default, EnableCallContextPropagation raises an error if the client is used outside the context of a gRPC call. The error is designed to alert you that there isn't a call context to propagate. If you want to use the client outside of a call context, suppress the error when the client is configured with SuppressContextNotFoundErrors:

services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .EnableCallContextPropagation(o => o.SuppressContextNotFoundErrors = true);

For more information about deadlines and RPC cancellation, see xref:grpc/deadlines-cancellation.

Named clients

Typically, a gRPC client type is registered once and then injected directly into a type's constructor by DI. However, there are scenarios where it is useful to have multiple configurations for one client. For example, a client that makes gRPC calls with and without authentication.

Multiple clients with the same type can be registered by giving each client a name. Each named client can have its own configuration. The generic AddGrpcClient extension method has an overload that includes a name parameter:

services
    .AddGrpcClient<Greeter.GreeterClient>("Greeter", o =>
    {
        o.Address = new Uri("https://localhost:5001");
    });
    
services
    .AddGrpcClient<Greeter.GreeterClient>("GreeterAuthenticated", o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigureChannel(o =>
    {
        o.Credentials = new CustomCredentials();
    });

The preceding code:

  • Registers the GreeterClient type twice, specifying a unique name for each.
  • Configures different settings for each named client. The GreeterAuthenticated registration adds credentials to the channel so that gRPC calls made with it are authenticated.

A named gRPC client is created in app code using GrpcClientFactory. The type and name of the desired client is specified using the generic GrpcClientFactory.CreateClient method:

public class AggregatorService : Aggregator.AggregatorBase
{
    private readonly Greeter.GreeterClient _client;

    public AggregatorService(GrpcClientFactory grpcClientFactory)
    {
        _client = grpcClientFactory.CreateClient<Greeter.GreeterClient>("GreeterAuthenticated");
    }
}

Additional resources