Compartilhar via


Solucionar problemas de gRPC no .NET

Por James Newton-King

Observação

Esta não é a versão mais recente deste artigo. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Aviso

Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, confira .NET e a Política de Suporte do .NET Core. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Importante

Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.

Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Este documento discute problemas comumente encontrados no desenvolvimento de aplicativos gRPC no .NET.

Incompatibilidade entre as configurações SSL/TLS do cliente e do serviço

O modelo gRPC e os exemplos usam TLS (Transport Layer Security) para proteger os serviços gRPC por padrão. Os clientes gRPC precisam usar uma conexão segura para chamar serviços gRPC protegidos com êxito.

Você pode verificar se o serviço gRPC do ASP.NET Core está usando TLS nos logs gravados no início do aplicativo. O serviço vai escutar em um ponto de extremidade HTTPS:

info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development

O cliente .NET Core precisa usar https no endereço do servidor para fazer chamadas com uma conexão protegida:

var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new Greeter.GreeterClient(channel);

Todas as implementações de cliente gRPC dão suporte a TLS. Clientes gRPC de outros idiomas normalmente exigem o canal configurado com SslCredentials. SslCredentials especifica o certificado que o cliente usará e precisa ser usado no lugar das credenciais inseguras. Para obter exemplos de como configurar diferentes implementações de cliente gRPC para usar o TLS, confira Autenticação gRPC.

Chamar um serviço gRPC com um certificado não confiável/inválido

O cliente gRPC do .NET requer que o serviço tenha um certificado confiável. A seguinte mensagem de erro é retornada ao chamar um serviço gRPC sem um certificado confiável:

Exceção sem tratamento. System.Net.Http.HttpRequestException: a conexão SSL não pôde ser estabelecida; confira a exceção interna. ---> System.Security.Authentication.AuthenticationException: o certificado remoto é inválido de acordo com o procedimento de validação.

Você poderá ver esse erro se estiver testando o aplicativo localmente e o certificado de desenvolvimento HTTPS do ASP.NET Core não for confiável. Para obter instruções sobre como corrigir esse problema, confira Confiar no certificado de desenvolvimento HTTPS do ASP.NET Core no Windows e no macOS.

Se você estiver chamando um serviço gRPC em outro computador e não conseguir confiar no certificado, o cliente gRPC poderá ser configurado para ignorar o certificado inválido. O código a seguir usa HttpClientHandler.ServerCertificateCustomValidationCallback para permitir chamadas sem um certificado confiável:

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpHandler = handler });
var client = new Greeter.GreeterClient(channel);

O alocador de clientes gRPC permite chamadas sem um certificado confiável. Use o método de extensão ConfigurePrimaryHttpMessageHandler para configurar o manipulador no cliente:


var services = new ServiceCollection();

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

        return handler;
    });

Aviso

Certificados não confiáveis só devem ser usados durante o desenvolvimento do aplicativo. Os aplicativos de produção sempre devem usar certificados válidos.

Chamar serviços gRPC desprotegidos com o cliente .NET Core

O cliente gRPC do .NET pode chamar serviços gRPC desprotegidos especificando http no endereço do servidor. Por exemplo, GrpcChannel.ForAddress("http://localhost:5000").

Há alguns requisitos adicionais para chamar serviços gRPC desprotegidos, dependendo da versão do .NET que um aplicativo está usando:

Importante

Os serviços gRPC desprotegidos precisam ser hospedados em uma porta somente HTTP/2. Para obter mais informações, confira Negociação de protocolo do ASP.NET Core.

Não é possível iniciar o aplicativo gRPC do ASP.NET Core no macOS

Kestrel não dá suporte a HTTP/2 com TLS no macOS antes do .NET 8. O modelo e exemplos de gRPC do ASP.NET Core usam TLS por padrão. Você verá a seguinte mensagem de erro ao tentar iniciar o servidor gRPC:

Não é possível associar https://localhost:5001 à interface de loopback IPv4: 'HTTP/2 sobre TLS não tem suporte no macOS devido à falta de suporte do ALPN.'.

Para contornar esse problema no .NET 7 e anteriores, configure o Kestrel e o cliente gRPC para usar HTTP/2 sem TLS. Você só deve fazer isso durante o desenvolvimento. O não uso do TLS resultará no envio de mensagens gRPC sem criptografia. Para obter mais informações, confira Asp.Net Core 7.0: não é possível iniciar o aplicativo ASP.NET Core gRPC no macOS.

Os ativos de C# do gRPC não são gerados por código com base nos arquivos .proto

A geração de código gRPC de clientes concretos e classes base de serviço requer que arquivos e ferramentas protobuf sejam referenciados de um projeto. Você precisa incluir:

  • Arquivos .proto que você deseja usar no grupo de itens <Protobuf>. Os arquivos .protoimportados precisam ser referenciados pelo projeto.
  • Referência de pacote para o pacote de ferramentas Grpc.Tools.

Para obter mais informações sobre como gerar ativos C# gRPC, confira Serviços gRPC com C#.

Um aplicativo Web ASP.NET Core que hospeda serviços gRPC só precisa da classe base de serviço gerada:

<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
</ItemGroup>

Um aplicativo cliente gRPC que faz chamadas gRPC só precisa do cliente concreto gerado:

<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>

Projetos do WPF não podem gerar ativos de C# do gRPC de arquivos .proto

Os projetos do WPF têm um problema conhecido que impede que a geração de código gRPC funcione corretamente. Todos os tipos de gRPC gerados em um projeto do WPF referenciando arquivos Grpc.Tools e .proto criarão erros de compilação quando usados:

Erro CS0246: O tipo ou o nome do namespace 'MyGrpcServices' não foi encontrado (uma diretiva de uso ou uma referência de assembly está ausente?)

Você pode solucionar esse problema de forma alternativa:

  1. Criar um novo projeto de biblioteca de classes do .NET Core.
  2. No novo projeto, adicione referências para habilitar a geração de código C# de arquivos .proto:
  3. No aplicativo WPF, adicione uma referência ao novo projeto.

O aplicativo WPF pode usar os tipos gerados por gRPC do novo projeto de biblioteca de classes.

Chamando serviços gRPC hospedados em um subdiretório

Aviso

Muitas ferramentas gRPC de terceiros não dão suporte a serviços hospedados em subdiretórios. Considere encontrar uma maneira de hospedar o gRPC como o diretório raiz.

O componente de caminho do endereço de um canal gRPC é ignorado ao fazer chamadas gRPC. Por exemplo, GrpcChannel.ForAddress("https://localhost:5001/ignored_path") não usará ignored_path ao rotear chamadas gRPC para o serviço.

O caminho do endereço é ignorado porque o gRPC tem uma estrutura de endereços padronizada e prescritiva. Um endereço gRPC combina os nomes de pacote, serviço e método: https://localhost:5001/PackageName.ServiceName/MethodName.

Há alguns cenários em que um aplicativo precisa incluir um caminho com chamadas gRPC. Por exemplo, quando um aplicativo gRPC do ASP.NET Core é hospedado em um diretório do IIS e o diretório precisa ser incluído na solicitação. Quando um caminho é obrigatório, ele pode ser adicionado à chamada gRPC usando o SubdirectoryHandler personalizado especificado abaixo:

public class SubdirectoryHandler : DelegatingHandler
{
    private readonly string _subdirectory;

    public SubdirectoryHandler(HttpMessageHandler innerHandler, string subdirectory)
        : base(innerHandler)
    {
        _subdirectory = subdirectory;
    }

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var old = request.RequestUri;

        var url = $"{old.Scheme}://{old.Host}:{old.Port}";
        url += $"{_subdirectory}{request.RequestUri.AbsolutePath}";
        request.RequestUri = new Uri(url, UriKind.Absolute);

        return base.SendAsync(request, cancellationToken);
    }
}

SubdirectoryHandler é usado quando o canal gRPC é criado.

var handler = new SubdirectoryHandler(new HttpClientHandler(), "/MyApp");

var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = handler });
var client = new Greeter.GreeterClient(channel);

var reply = await client.SayHelloAsync(
                  new HelloRequest { Name = "GreeterClient" });

O código anterior:

  • Cria um SubdirectoryHandler com o caminho /MyApp.
  • Configura um canal para usar SubdirectoryHandler.
  • Chama o serviço gRPC com SayHelloAsync. A chamada gRPC é enviada para https://localhost:5001/MyApp/greet.Greeter/SayHello.

Como alternativa, uma fábrica de clientes pode ser configurada com SubdirectoryHandler usando AddHttpMessageHandler.

Configurar o cliente gRPC para usar HTTP/3

O cliente gRPC do .NET dá suporte a HTTP/3 com .NET 6 ou posterior. Se o servidor enviar um cabeçalho de resposta alt-svc para o cliente que indica que o servidor dá suporte a HTTP/3, o cliente atualizará automaticamente sua conexão para HTTP/3. Para obter mais informações, consulte Usar HTTP/3 com o servidor Web Kestrel do ASP.NET Core.

Um DelegatingHandler pode ser usado para forçar um cliente gRPC a usar HTTP/3. Forçar HTTP/3 evita a sobrecarga de atualizar a solicitação. Force HTTP/3 com código semelhante ao seguinte:

public class Http3Handler : DelegatingHandler
{
    public Http3Handler() { }
    public Http3Handler(HttpMessageHandler innerHandler) : base(innerHandler) { }

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.Version = HttpVersion.Version30;
        request.VersionPolicy = HttpVersionPolicy.RequestVersionExact;

        return base.SendAsync(request, cancellationToken);
    }
}

Http3Handler é usado quando o canal gRPC é criado. O código a seguir cria um canal configurado para usar Http3Handler.

var handler = new Http3Handler(new HttpClientHandler());

var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = handler });
var client = new Greeter.GreeterClient(channel);

var reply = await client.SayHelloAsync(
                  new HelloRequest { Name = "GreeterClient" });

Como alternativa, uma fábrica de clientes pode ser configurada com Http3Handler usando AddHttpMessageHandler.

Compilando gRPC no Alpine Linux

O pacote Grpc.Tools gera tipos .NET a partir de arquivos .proto usando um binário nativo agrupado chamado protoc. São necessárias etapas adicionais para criar aplicativos gRPC em plataformas que não têm suporte dos binários nativos em Grpc.Tools, como o Alpine Linux.

Gerar código ahead-of-time

Uma solução é gerar ahead-of-time.

  1. Mova arquivos .proto e a referência de pacote Grpc.Tools para um novo projeto.
  2. Publique o projeto como um pacote NuGet e carregue-o em um feed do NuGet.
  3. Atualize o aplicativo para fazer referência ao pacote NuGet.

Com as etapas anteriores, o aplicativo não requer mais o Grpc.Tools para compilar porque o código ahead-of-time é gerado.

Personalizar binários nativos Grpc.Tools

Grpc.Tools dá suporte ao uso de binários nativos personalizados. Esse recurso permite que as ferramentas gRPC sejam executadas em ambientes que não são compatíveis com os binários nativos incluídos no pacote.

Crie ou adquira binários nativos protoc e grpc_csharp_plugin, e configure Grpc.Tools para usá-los. Configure binários nativos definindo as seguintes variáveis de ambiente:

  • PROTOBUF_PROTOC – Caminho completo para o compilador de buffers de protocolo
  • GRPC_PROTOC_PLUGIN – Caminho completo para o grpc_csharp_plugin

Para o Alpine Linux, existem pacotes fornecidos pela comunidade para o compilador de buffers de protocolo e plug-ins gRPC em https://pkgs.alpinelinux.org/.

# Build or install the binaries for your architecture.

# For Alpine Linux, the grpc-plugins package can be used.
# See https://pkgs.alpinelinux.org/package/edge/community/x86_64/grpc-plugins
apk add grpc-plugins  # Alpine Linux specific package installer

# Set environment variables for the built/installed protoc
# and grpc_csharp_plugin binaries
export PROTOBUF_PROTOC=/usr/bin/protoc
export GRPC_PROTOC_PLUGIN=/usr/bin/grpc_csharp_plugin

# When dotnet build runs, the Grpc.Tools NuGet package
# uses the binaries pointed to by the environment variables.
dotnet build

Para obter mais informações sobre como usar Grpc.Tools com arquiteturas sem suporte, confira a documentação de integração de build gRPC.

Tempo limite da chamada gRPC de HttpClient.Timeout

HttpClient é configurado com um tempo limite de 100 segundos por padrão. Se GrpcChannel estiver configurado para usar HttpClient, as chamadas de streaming gRPC de longa duração serão canceladas se não forem concluídas dentro do limite de tempo.

System.OperationCanceledException: The request was canceled due to the configured HttpClient.Timeout of 100 seconds elapsing.

Há algumas maneiras de corrigir esse erro. A primeira é configurar HttpClient.Timeout com um valor maior. Timeout.InfiniteTimeSpan desabilita o tempo limite:

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var httpClient = new HttpClient(handler) { Timeout = Timeout.InfiniteTimeSpan };
var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpClient = httpClient });
var client = new Greeter.GreeterClient(channel);

Como alternativa, evite criar HttpClient e, em vez disso, defina GrpcChannel.HttpHandler:

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpHandler = handler });
var client = new Greeter.GreeterClient(channel);

Este documento discute problemas comumente encontrados no desenvolvimento de aplicativos gRPC no .NET.

Incompatibilidade entre as configurações SSL/TLS do cliente e do serviço

O modelo gRPC e os exemplos usam TLS (Transport Layer Security) para proteger os serviços gRPC por padrão. Os clientes gRPC precisam usar uma conexão segura para chamar serviços gRPC protegidos com êxito.

Você pode verificar se o serviço gRPC do ASP.NET Core está usando TLS nos logs gravados no início do aplicativo. O serviço vai escutar em um ponto de extremidade HTTPS:

info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development

O cliente .NET Core precisa usar https no endereço do servidor para fazer chamadas com uma conexão protegida:

static async Task Main(string[] args)
{
    // The port number(5001) must match the port of the gRPC server.
    var channel = GrpcChannel.ForAddress("https://localhost:5001");
    var client = new Greet.GreeterClient(channel);
}

Todas as implementações de cliente gRPC dão suporte a TLS. Clientes gRPC de outros idiomas normalmente exigem o canal configurado com SslCredentials. SslCredentials especifica o certificado que o cliente usará e precisa ser usado no lugar das credenciais inseguras. Para obter exemplos de como configurar diferentes implementações de cliente gRPC para usar o TLS, confira Autenticação gRPC.

Chamar um serviço gRPC com um certificado não confiável/inválido

O cliente gRPC do .NET requer que o serviço tenha um certificado confiável. A seguinte mensagem de erro é retornada ao chamar um serviço gRPC sem um certificado confiável:

Exceção sem tratamento. System.Net.Http.HttpRequestException: a conexão SSL não pôde ser estabelecida; confira a exceção interna. ---> System.Security.Authentication.AuthenticationException: o certificado remoto é inválido de acordo com o procedimento de validação.

Você poderá ver esse erro se estiver testando o aplicativo localmente e o certificado de desenvolvimento HTTPS do ASP.NET Core não for confiável. Para obter instruções sobre como corrigir esse problema, confira Confiar no certificado de desenvolvimento HTTPS do ASP.NET Core no Windows e no macOS.

Se você estiver chamando um serviço gRPC em outro computador e não conseguir confiar no certificado, o cliente gRPC poderá ser configurado para ignorar o certificado inválido. O código a seguir usa HttpClientHandler.ServerCertificateCustomValidationCallback para permitir chamadas sem um certificado confiável:

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpHandler = handler });
var client = new Greet.GreeterClient(channel);

O alocador de clientes gRPC permite chamadas sem um certificado confiável. Use o método de extensão ConfigurePrimaryHttpMessageHandler para configurar o manipulador no cliente:

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
        o.Address = new Uri("https://localhost:5001");
    })
    .ConfigurePrimaryHttpMessageHandler(() =>
    {
        var handler = new HttpClientHandler();
        handler.ServerCertificateCustomValidationCallback = 
            HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

        return handler;
    });

Aviso

Certificados não confiáveis só devem ser usados durante o desenvolvimento do aplicativo. Os aplicativos de produção sempre devem usar certificados válidos.

Chamar serviços gRPC desprotegidos com o cliente .NET Core

O cliente gRPC do .NET pode chamar serviços gRPC desprotegidos especificando http no endereço do servidor. Por exemplo, GrpcChannel.ForAddress("http://localhost:5000").

Há alguns requisitos adicionais para chamar serviços gRPC desprotegidos, dependendo da versão do .NET que um aplicativo está usando:

  • O .NET 5 ou posterior requer Grpc.Net.Client versão 2.32.0 ou posterior.

  • O .NET Core 3.x requer configuração adicional. O aplicativo precisa definir a opção System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport como true:

    // This switch must be set before creating the GrpcChannel/HttpClient.
    AppContext.SetSwitch(
        "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
    
    // The port number(5000) must match the port of the gRPC server.
    var channel = GrpcChannel.ForAddress("http://localhost:5000");
    var client = new Greet.GreeterClient(channel);
    

A opção System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport é obrigatória apenas para o .NET Core 3.x. Ela não faz nada no .NET 5 e não é necessária.

Importante

Os serviços gRPC desprotegidos precisam ser hospedados em uma porta somente HTTP/2. Para obter mais informações, confira Negociação de protocolo do ASP.NET Core.

Não é possível iniciar o aplicativo gRPC do ASP.NET Core no macOS

Kestrel não dá suporte a HTTP/2 com TLS no macOS antes do .NET 8. O modelo e exemplos de gRPC do ASP.NET Core usam TLS por padrão. Você verá a seguinte mensagem de erro ao tentar iniciar o servidor gRPC:

Não é possível associar https://localhost:5001 à interface de loopback IPv4: 'HTTP/2 sobre TLS não tem suporte no macOS devido à falta de suporte do ALPN.'.

Para contornar esse problema no .NET 7 e anteriores, configure o Kestrel e o cliente gRPC para usar HTTP/2 sem TLS. Você só deve fazer isso durante o desenvolvimento. O não uso do TLS resultará no envio de mensagens gRPC sem criptografia.

O Kestrel precisa configurar um ponto de extremidade HTTP/2 sem TLS em Program.cs:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(options =>
{
    // Setup a HTTP/2 endpoint without TLS.
    options.ListenLocalhost(<5287>, o => o.Protocols =
        HttpProtocols.Http2);
});
  • No código anterior, substitua o número 5287 da porta localhost pelo número da porta HTTP (não HTTPS) especificado em Properties/launchSettings.json no projeto de serviço gRPC.

Quando um ponto de extremidade HTTP/2 é configurado sem TLS, os ListenOptions.Protocols do ponto de extremidade precisam ser definidos como HttpProtocols.Http2. HttpProtocols.Http1AndHttp2 não pode ser usado porque o TLS é exigido para a negociação de HTTP/2. Sem TLS, todas as conexões com o ponto de extremidade padrão para HTTP/1.1 e chamadas gRPC falham.

O cliente gRPC também precisa ser configurado para não usar TLS. Para obter mais informações, confira Chamar serviços gRPC desprotegidos com o cliente .NET Core.

Aviso

HTTP/2 sem TLS só deve ser usado durante o desenvolvimento de aplicativos. Os aplicativos de produção sempre devem usar a segurança do transporte. Para obter mais informações, confira Considerações de segurança no gRPC para ASP.NET Core.

Os ativos de C# do gRPC não são gerados por código com base nos arquivos .proto

A geração de código gRPC de clientes concretos e classes base de serviço requer que arquivos e ferramentas protobuf sejam referenciados de um projeto. Você precisa incluir:

  • Arquivos .proto que você deseja usar no grupo de itens <Protobuf>. Os arquivos .protoimportados precisam ser referenciados pelo projeto.
  • Referência de pacote para o pacote de ferramentas Grpc.Tools.

Para obter mais informações sobre como gerar ativos C# gRPC, confira Serviços gRPC com C#.

Um aplicativo Web ASP.NET Core que hospeda serviços gRPC só precisa da classe base de serviço gerada:

<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
</ItemGroup>

Um aplicativo cliente gRPC que faz chamadas gRPC só precisa do cliente concreto gerado:

<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>

Projetos do WPF não podem gerar ativos de C# do gRPC de arquivos .proto

Os projetos do WPF têm um problema conhecido que impede que a geração de código gRPC funcione corretamente. Todos os tipos de gRPC gerados em um projeto do WPF referenciando arquivos Grpc.Tools e .proto criarão erros de compilação quando usados:

Erro CS0246: O tipo ou o nome do namespace 'MyGrpcServices' não foi encontrado (uma diretiva de uso ou uma referência de assembly está ausente?)

Você pode solucionar esse problema de forma alternativa:

  1. Criar um novo projeto de biblioteca de classes do .NET Core.
  2. No novo projeto, adicione referências para habilitar a geração de código C# de arquivos .proto:
  3. No aplicativo WPF, adicione uma referência ao novo projeto.

O aplicativo WPF pode usar os tipos gerados por gRPC do novo projeto de biblioteca de classes.

Chamando serviços gRPC hospedados em um subdiretório

Aviso

Muitas ferramentas gRPC de terceiros não dão suporte a serviços hospedados em subdiretórios. Considere encontrar uma maneira de hospedar o gRPC como o diretório raiz.

O componente de caminho do endereço de um canal gRPC é ignorado ao fazer chamadas gRPC. Por exemplo, GrpcChannel.ForAddress("https://localhost:5001/ignored_path") não usará ignored_path ao rotear chamadas gRPC para o serviço.

O caminho do endereço é ignorado porque o gRPC tem uma estrutura de endereços padronizada e prescritiva. Um endereço gRPC combina os nomes de pacote, serviço e método: https://localhost:5001/PackageName.ServiceName/MethodName.

Há alguns cenários em que um aplicativo precisa incluir um caminho com chamadas gRPC. Por exemplo, quando um aplicativo gRPC do ASP.NET Core é hospedado em um diretório do IIS e o diretório precisa ser incluído na solicitação. Quando um caminho é obrigatório, ele pode ser adicionado à chamada gRPC usando o SubdirectoryHandler personalizado especificado abaixo:

/// <summary>
/// A delegating handler that adds a subdirectory to the URI of gRPC requests.
/// </summary>
public class SubdirectoryHandler : DelegatingHandler
{
    private readonly string _subdirectory;

    public SubdirectoryHandler(HttpMessageHandler innerHandler, string subdirectory)
        : base(innerHandler)
    {
        _subdirectory = subdirectory;
    }

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var old = request.RequestUri;

        var url = $"{old.Scheme}://{old.Host}:{old.Port}";
        url += $"{_subdirectory}{request.RequestUri.AbsolutePath}";
        request.RequestUri = new Uri(url, UriKind.Absolute);

        return base.SendAsync(request, cancellationToken);
    }
}

SubdirectoryHandler é usado quando o canal gRPC é criado.

var handler = new SubdirectoryHandler(new HttpClientHandler(), "/MyApp");

var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = handler });
var client = new Greet.GreeterClient(channel);

var reply = await client.SayHelloAsync(new HelloRequest { Name = ".NET" });

O código anterior:

  • Cria um SubdirectoryHandler com o caminho /MyApp.
  • Configura um canal para usar SubdirectoryHandler.
  • Chama o serviço gRPC com SayHelloAsync. A chamada gRPC é enviada para https://localhost:5001/MyApp/greet.Greeter/SayHello.

Como alternativa, uma fábrica de clientes pode ser configurada com SubdirectoryHandler usando AddHttpMessageHandler.

Configurar o cliente gRPC para usar HTTP/3

O cliente gRPC do .NET dá suporte a HTTP/3 com .NET 6 ou posterior. Se o servidor enviar um cabeçalho de resposta alt-svc para o cliente que indica que o servidor dá suporte a HTTP/3, o cliente atualizará automaticamente sua conexão para HTTP/3. Para obter informações sobre como habilitar HTTP/3 no servidor, confira Usar HTTP/3 com o servidor Web Kestrel do ASP.NET Core.

O suporte a HTTP/3 no .NET 8 está habilitado por padrão. O suporte a HTTP/3 no .NET 6 e .NET 7 precisa ser habilitado por meio de um sinalizador de configuração no arquivo de projeto:

<ItemGroup>
  <RuntimeHostConfigurationOption Include="System.Net.SocketsHttpHandler.Http3Support" Value="true" />
</ItemGroup>

System.Net.SocketsHttpHandler.Http3Support também pode ser definido com AppContext.SetSwitch.

Um DelegatingHandler pode ser usado para forçar um cliente gRPC a usar HTTP/3. Forçar HTTP/3 evita a sobrecarga de atualizar a solicitação. Force HTTP/3 com código semelhante ao seguinte:

/// <summary>
/// A delegating handler that changes the request HTTP version to HTTP/3.
/// </summary>
public class Http3Handler : DelegatingHandler
{
    public Http3Handler() { }
    public Http3Handler(HttpMessageHandler innerHandler) : base(innerHandler) { }

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        request.Version = HttpVersion.Version30;
        request.VersionPolicy = HttpVersionPolicy.RequestVersionExact;

        return base.SendAsync(request, cancellationToken);
    }
}

Http3Handler é usado quando o canal gRPC é criado. O código a seguir cria um canal configurado para usar Http3Handler.

var handler = new Http3Handler(new HttpClientHandler());

var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = handler });
var client = new Greet.GreeterClient(channel);

var reply = await client.SayHelloAsync(new HelloRequest { Name = ".NET" });

Como alternativa, uma fábrica de clientes pode ser configurada com Http3Handler usando AddHttpMessageHandler.

Compilando gRPC no Alpine Linux

O pacote Grpc.Tools gera tipos .NET a partir de arquivos .proto usando um binário nativo agrupado chamado protoc. São necessárias etapas adicionais para criar aplicativos gRPC em plataformas que não têm suporte dos binários nativos em Grpc.Tools, como o Alpine Linux.

Gerar código ahead-of-time

Uma solução é gerar ahead-of-time.

  1. Mova arquivos .proto e a referência de pacote Grpc.Tools para um novo projeto.
  2. Publique o projeto como um pacote NuGet e carregue-o em um feed do NuGet.
  3. Atualize o aplicativo para fazer referência ao pacote NuGet.

Com as etapas anteriores, o aplicativo não requer mais o Grpc.Tools para compilar porque o código ahead-of-time é gerado.

Personalizar binários nativos Grpc.Tools

Grpc.Tools dá suporte ao uso de binários nativos personalizados. Esse recurso permite que as ferramentas gRPC sejam executadas em ambientes que não são compatíveis com os binários nativos incluídos no pacote.

Crie ou adquira binários nativos protoc e grpc_csharp_plugin, e configure Grpc.Tools para usá-los. Configure binários nativos definindo as seguintes variáveis de ambiente:

  • PROTOBUF_PROTOC – Caminho completo para o compilador de buffers de protocolo
  • GRPC_PROTOC_PLUGIN – Caminho completo para o grpc_csharp_plugin

Para o Alpine Linux, existem pacotes fornecidos pela comunidade para o compilador de buffers de protocolo e plug-ins gRPC em https://pkgs.alpinelinux.org/.

# Build or install the binaries for your architecture.

# For Alpine Linux, the grpc-plugins package can be used.
#  See https://pkgs.alpinelinux.org/package/edge/community/x86_64/grpc-plugins
apk add grpc-plugins  # Alpine Linux specific package installer

# Set environment variables for the built/installed protoc
# and grpc_csharp_plugin binaries
export PROTOBUF_PROTOC=/usr/bin/protoc
export GRPC_PROTOC_PLUGIN=/usr/bin/grpc_csharp_plugin

# When dotnet build runs, the Grpc.Tools NuGet package
# uses the binaries pointed to by the environment variables.
dotnet build

Para obter mais informações sobre como usar Grpc.Tools com arquiteturas sem suporte, confira a documentação de integração de build gRPC.

Tempo limite da chamada gRPC de HttpClient.Timeout

HttpClient é configurado com um tempo limite de 100 segundos por padrão. Se GrpcChannel estiver configurado para usar HttpClient, as chamadas de streaming gRPC de longa duração serão canceladas se não forem concluídas dentro do limite de tempo.

System.OperationCanceledException: The request was canceled due to the configured HttpClient.Timeout of 100 seconds elapsing.

Há algumas maneiras de corrigir esse erro. A primeira é configurar HttpClient.Timeout com um valor maior. Timeout.InfiniteTimeSpan desabilita o tempo limite:

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var httpClient = new HttpClient(handler) { Timeout = Timeout.InfiniteTimeSpan };
var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpClient = httpClient });
var client = new Greeter.GreeterClient(channel);

Como alternativa, evite criar HttpClient e, em vez disso, defina GrpcChannel.HttpHandler:

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpHandler = handler });
var client = new Greeter.GreeterClient(channel);

Este documento discute problemas comumente encontrados no desenvolvimento de aplicativos gRPC no .NET.

Incompatibilidade entre as configurações SSL/TLS do cliente e do serviço

O modelo gRPC e os exemplos usam TLS (Transport Layer Security) para proteger os serviços gRPC por padrão. Os clientes gRPC precisam usar uma conexão segura para chamar serviços gRPC protegidos com êxito.

Você pode verificar se o serviço gRPC do ASP.NET Core está usando TLS nos logs gravados no início do aplicativo. O serviço vai escutar em um ponto de extremidade HTTPS:

info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development

O cliente .NET Core precisa usar https no endereço do servidor para fazer chamadas com uma conexão protegida:

static async Task Main(string[] args)
{
    // The port number(5001) must match the port of the gRPC server.
    var channel = GrpcChannel.ForAddress("https://localhost:5001");
    var client = new Greet.GreeterClient(channel);
}

Todas as implementações de cliente gRPC dão suporte a TLS. Clientes gRPC de outros idiomas normalmente exigem o canal configurado com SslCredentials. SslCredentials especifica o certificado que o cliente usará e precisa ser usado no lugar das credenciais inseguras. Para obter exemplos de como configurar diferentes implementações de cliente gRPC para usar o TLS, confira Autenticação gRPC.

Chamar um serviço gRPC com um certificado não confiável/inválido

O cliente gRPC do .NET requer que o serviço tenha um certificado confiável. A seguinte mensagem de erro é retornada ao chamar um serviço gRPC sem um certificado confiável:

Exceção sem tratamento. System.Net.Http.HttpRequestException: a conexão SSL não pôde ser estabelecida; confira a exceção interna. ---> System.Security.Authentication.AuthenticationException: o certificado remoto é inválido de acordo com o procedimento de validação.

Você poderá ver esse erro se estiver testando o aplicativo localmente e o certificado de desenvolvimento HTTPS do ASP.NET Core não for confiável. Para obter instruções sobre como corrigir esse problema, confira Confiar no certificado de desenvolvimento HTTPS do ASP.NET Core no Windows e no macOS.

Se você estiver chamando um serviço gRPC em outro computador e não conseguir confiar no certificado, o cliente gRPC poderá ser configurado para ignorar o certificado inválido. O código a seguir usa HttpClientHandler.ServerCertificateCustomValidationCallback para permitir chamadas sem um certificado confiável:

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpHandler = handler });
var client = new Greet.GreeterClient(channel);

O alocador de clientes gRPC permite chamadas sem um certificado confiável. Use o método de extensão ConfigurePrimaryHttpMessageHandler para configurar o manipulador no cliente:

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

        return handler;
    });

Aviso

Certificados não confiáveis só devem ser usados durante o desenvolvimento do aplicativo. Os aplicativos de produção sempre devem usar certificados válidos.

Chamar serviços gRPC desprotegidos com o cliente .NET Core

O cliente gRPC do .NET pode chamar serviços gRPC desprotegidos especificando http no endereço do servidor. Por exemplo, GrpcChannel.ForAddress("http://localhost:5000").

Há alguns requisitos adicionais para chamar serviços gRPC desprotegidos, dependendo da versão do .NET que um aplicativo está usando:

  • O .NET 5 ou posterior requer Grpc.Net.Client versão 2.32.0 ou posterior.

  • O .NET Core 3.x requer configuração adicional. O aplicativo precisa definir a opção System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport como true:

    // This switch must be set before creating the GrpcChannel/HttpClient.
    AppContext.SetSwitch(
        "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
    
    // The port number(5000) must match the port of the gRPC server.
    var channel = GrpcChannel.ForAddress("http://localhost:5000");
    var client = new Greet.GreeterClient(channel);
    

A opção System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport é obrigatória apenas para o .NET Core 3.x. Ela não faz nada no .NET 5 e não é necessária.

Importante

Os serviços gRPC desprotegidos precisam ser hospedados em uma porta somente HTTP/2. Para obter mais informações, confira Negociação de protocolo do ASP.NET Core.

Não é possível iniciar o aplicativo gRPC do ASP.NET Core no macOS

Kestrel não dá suporte a HTTP/2 com TLS no macOS antes do .NET 8. O modelo e exemplos de gRPC do ASP.NET Core usam TLS por padrão. Você verá a seguinte mensagem de erro ao tentar iniciar o servidor gRPC:

Não é possível associar https://localhost:5001 à interface de loopback IPv4: 'HTTP/2 sobre TLS não tem suporte no macOS devido à falta de suporte do ALPN.'.

Para contornar esse problema no .NET 7 e anteriores, configure o Kestrel e o cliente gRPC para usar HTTP/2 sem TLS. Você só deve fazer isso durante o desenvolvimento. O não uso do TLS resultará no envio de mensagens gRPC sem criptografia.

O Kestrel precisa configurar um ponto de extremidade HTTP/2 sem TLS em Program.cs:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.ConfigureKestrel(options =>
            {
                // Setup a HTTP/2 endpoint without TLS.
                options.ListenLocalhost(5000, o => o.Protocols = 
                    HttpProtocols.Http2);
            });
            webBuilder.UseStartup<Startup>();
        });

Quando um ponto de extremidade HTTP/2 é configurado sem TLS, os ListenOptions.Protocols do ponto de extremidade precisam ser definidos como HttpProtocols.Http2. HttpProtocols.Http1AndHttp2 não pode ser usado porque o TLS é exigido para a negociação de HTTP/2. Sem TLS, todas as conexões com o ponto de extremidade padrão para HTTP/1.1 e chamadas gRPC falham.

O cliente gRPC também precisa ser configurado para não usar TLS. Para obter mais informações, confira Chamar serviços gRPC desprotegidos com o cliente .NET Core.

Aviso

HTTP/2 sem TLS só deve ser usado durante o desenvolvimento de aplicativos. Os aplicativos de produção sempre devem usar a segurança do transporte. Para obter mais informações, confira Considerações de segurança no gRPC para ASP.NET Core.

Os ativos de C# do gRPC não são gerados por código com base nos arquivos .proto

A geração de código gRPC de clientes concretos e classes base de serviço requer que arquivos e ferramentas protobuf sejam referenciados de um projeto. Você precisa incluir:

  • Arquivos .proto que você deseja usar no grupo de itens <Protobuf>. Os arquivos .protoimportados precisam ser referenciados pelo projeto.
  • Referência de pacote para o pacote de ferramentas Grpc.Tools.

Para obter mais informações sobre como gerar ativos C# gRPC, confira Serviços gRPC com C#.

Um aplicativo Web ASP.NET Core que hospeda serviços gRPC só precisa da classe base de serviço gerada:

<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
</ItemGroup>

Um aplicativo cliente gRPC que faz chamadas gRPC só precisa do cliente concreto gerado:

<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>

Projetos do WPF não podem gerar ativos de C# do gRPC de arquivos .proto

Os projetos do WPF têm um problema conhecido que impede que a geração de código gRPC funcione corretamente. Todos os tipos de gRPC gerados em um projeto do WPF referenciando arquivos Grpc.Tools e .proto criarão erros de compilação quando usados:

Erro CS0246: O tipo ou o nome do namespace 'MyGrpcServices' não foi encontrado (uma diretiva de uso ou uma referência de assembly está ausente?)

Você pode solucionar esse problema de forma alternativa:

  1. Criar um novo projeto de biblioteca de classes do .NET Core.
  2. No novo projeto, adicione referências para habilitar a geração de código C# de arquivos .proto:
  3. No aplicativo WPF, adicione uma referência ao novo projeto.

O aplicativo WPF pode usar os tipos gerados por gRPC do novo projeto de biblioteca de classes.

Chamando serviços gRPC hospedados em um subdiretório

Aviso

Muitas ferramentas gRPC de terceiros não dão suporte a serviços hospedados em subdiretórios. Considere encontrar uma maneira de hospedar o gRPC como o diretório raiz.

O componente de caminho do endereço de um canal gRPC é ignorado ao fazer chamadas gRPC. Por exemplo, GrpcChannel.ForAddress("https://localhost:5001/ignored_path") não usará ignored_path ao rotear chamadas gRPC para o serviço.

O caminho do endereço é ignorado porque o gRPC tem uma estrutura de endereços padronizada e prescritiva. Um endereço gRPC combina os nomes de pacote, serviço e método: https://localhost:5001/PackageName.ServiceName/MethodName.

Há alguns cenários em que um aplicativo precisa incluir um caminho com chamadas gRPC. Por exemplo, quando um aplicativo gRPC do ASP.NET Core é hospedado em um diretório do IIS e o diretório precisa ser incluído na solicitação. Quando um caminho é obrigatório, ele pode ser adicionado à chamada gRPC usando o SubdirectoryHandler personalizado especificado abaixo:

/// <summary>
/// A delegating handler that adds a subdirectory to the URI of gRPC requests.
/// </summary>
public class SubdirectoryHandler : DelegatingHandler
{
    private readonly string _subdirectory;

    public SubdirectoryHandler(HttpMessageHandler innerHandler, string subdirectory)
        : base(innerHandler)
    {
        _subdirectory = subdirectory;
    }

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var old = request.RequestUri;

        var url = $"{old.Scheme}://{old.Host}:{old.Port}";
        url += $"{_subdirectory}{request.RequestUri.AbsolutePath}";
        request.RequestUri = new Uri(url, UriKind.Absolute);

        return base.SendAsync(request, cancellationToken);
    }
}

SubdirectoryHandler é usado quando o canal gRPC é criado.

var handler = new SubdirectoryHandler(new HttpClientHandler(), "/MyApp");

var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = handler });
var client = new Greet.GreeterClient(channel);

var reply = await client.SayHelloAsync(new HelloRequest { Name = ".NET" });

O código anterior:

  • Cria um SubdirectoryHandler com o caminho /MyApp.
  • Configura um canal para usar SubdirectoryHandler.
  • Chama o serviço gRPC com SayHelloAsync. A chamada gRPC é enviada para https://localhost:5001/MyApp/greet.Greeter/SayHello.

Como alternativa, uma fábrica de clientes pode ser configurada com SubdirectoryHandler usando AddHttpMessageHandler.

Tempo limite da chamada gRPC de HttpClient.Timeout

HttpClient é configurado com um tempo limite de 100 segundos por padrão. Se GrpcChannel estiver configurado para usar HttpClient, as chamadas de streaming gRPC de longa duração serão canceladas se não forem concluídas dentro do limite de tempo.

System.OperationCanceledException: The request was canceled due to the configured HttpClient.Timeout of 100 seconds elapsing.

Há algumas maneiras de corrigir esse erro. A primeira é configurar HttpClient.Timeout com um valor maior. Timeout.InfiniteTimeSpan desabilita o tempo limite:

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var httpClient = new HttpClient(handler) { Timeout = Timeout.InfiniteTimeSpan };
var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpClient = httpClient });
var client = new Greeter.GreeterClient(channel);

Como alternativa, evite criar HttpClient e, em vez disso, defina GrpcChannel.HttpHandler:

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpHandler = handler });
var client = new Greeter.GreeterClient(channel);

Este documento discute problemas comumente encontrados no desenvolvimento de aplicativos gRPC no .NET.

Incompatibilidade entre as configurações SSL/TLS do cliente e do serviço

O modelo gRPC e os exemplos usam TLS (Transport Layer Security) para proteger os serviços gRPC por padrão. Os clientes gRPC precisam usar uma conexão segura para chamar serviços gRPC protegidos com êxito.

Você pode verificar se o serviço gRPC do ASP.NET Core está usando TLS nos logs gravados no início do aplicativo. O serviço vai escutar em um ponto de extremidade HTTPS:

info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development

O cliente .NET Core precisa usar https no endereço do servidor para fazer chamadas com uma conexão protegida:

static async Task Main(string[] args)
{
    // The port number(5001) must match the port of the gRPC server.
    var channel = GrpcChannel.ForAddress("https://localhost:5001");
    var client = new Greet.GreeterClient(channel);
}

Todas as implementações de cliente gRPC dão suporte a TLS. Clientes gRPC de outros idiomas normalmente exigem o canal configurado com SslCredentials. SslCredentials especifica o certificado que o cliente usará e precisa ser usado no lugar das credenciais inseguras. Para obter exemplos de como configurar diferentes implementações de cliente gRPC para usar o TLS, confira Autenticação gRPC.

Chamar um serviço gRPC com um certificado não confiável/inválido

O cliente gRPC do .NET requer que o serviço tenha um certificado confiável. A seguinte mensagem de erro é retornada ao chamar um serviço gRPC sem um certificado confiável:

Exceção sem tratamento. System.Net.Http.HttpRequestException: a conexão SSL não pôde ser estabelecida; confira a exceção interna. ---> System.Security.Authentication.AuthenticationException: o certificado remoto é inválido de acordo com o procedimento de validação.

Você poderá ver esse erro se estiver testando o aplicativo localmente e o certificado de desenvolvimento HTTPS do ASP.NET Core não for confiável. Para obter instruções sobre como corrigir esse problema, confira Confiar no certificado de desenvolvimento HTTPS do ASP.NET Core no Windows e no macOS.

Se você estiver chamando um serviço gRPC em outro computador e não conseguir confiar no certificado, o cliente gRPC poderá ser configurado para ignorar o certificado inválido. O código a seguir usa HttpClientHandler.ServerCertificateCustomValidationCallback para permitir chamadas sem um certificado confiável:

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpHandler = handler });
var client = new Greet.GreeterClient(channel);

O alocador de clientes gRPC permite chamadas sem um certificado confiável. Use o método de extensão ConfigurePrimaryHttpMessageHandler para configurar o manipulador no cliente:

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

        return handler;
    });

Aviso

Certificados não confiáveis só devem ser usados durante o desenvolvimento do aplicativo. Os aplicativos de produção sempre devem usar certificados válidos.

Chamar serviços gRPC desprotegidos com o cliente .NET Core

O cliente gRPC do .NET pode chamar serviços gRPC desprotegidos especificando http no endereço do servidor. Por exemplo, GrpcChannel.ForAddress("http://localhost:5000").

Há alguns requisitos adicionais para chamar serviços gRPC desprotegidos, dependendo da versão do .NET que um aplicativo está usando:

  • O .NET 5 ou posterior requer Grpc.Net.Client versão 2.32.0 ou posterior.

  • O .NET Core 3.x requer configuração adicional. O aplicativo precisa definir a opção System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport como true:

    // This switch must be set before creating the GrpcChannel/HttpClient.
    AppContext.SetSwitch(
        "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
    
    // The port number(5000) must match the port of the gRPC server.
    var channel = GrpcChannel.ForAddress("http://localhost:5000");
    var client = new Greet.GreeterClient(channel);
    

A opção System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport é obrigatória apenas para o .NET Core 3.x. Ela não faz nada no .NET 5 e não é necessária.

Importante

Os serviços gRPC desprotegidos precisam ser hospedados em uma porta somente HTTP/2. Para obter mais informações, confira Negociação de protocolo do ASP.NET Core.

Não é possível iniciar o aplicativo gRPC do ASP.NET Core no macOS

Kestrel não dá suporte a HTTP/2 com TLS no macOS antes do .NET 8. O modelo e exemplos de gRPC do ASP.NET Core usam TLS por padrão. Você verá a seguinte mensagem de erro ao tentar iniciar o servidor gRPC:

Não é possível associar https://localhost:5001 à interface de loopback IPv4: 'HTTP/2 sobre TLS não tem suporte no macOS devido à falta de suporte do ALPN.'.

Para contornar esse problema no .NET 7 e anteriores, configure o Kestrel e o cliente gRPC para usar HTTP/2 sem TLS. Você só deve fazer isso durante o desenvolvimento. O não uso do TLS resultará no envio de mensagens gRPC sem criptografia.

O Kestrel precisa configurar um ponto de extremidade HTTP/2 sem TLS em Program.cs:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.ConfigureKestrel(options =>
            {
                // Setup a HTTP/2 endpoint without TLS.
                options.ListenLocalhost(5000, o => o.Protocols = 
                    HttpProtocols.Http2);
            });
            webBuilder.UseStartup<Startup>();
        });

Quando um ponto de extremidade HTTP/2 é configurado sem TLS, os ListenOptions.Protocols do ponto de extremidade precisam ser definidos como HttpProtocols.Http2. HttpProtocols.Http1AndHttp2 não pode ser usado porque o TLS é exigido para a negociação de HTTP/2. Sem TLS, todas as conexões com o ponto de extremidade padrão para HTTP/1.1 e chamadas gRPC falham.

O cliente gRPC também precisa ser configurado para não usar TLS. Para obter mais informações, confira Chamar serviços gRPC desprotegidos com o cliente .NET Core.

Aviso

HTTP/2 sem TLS só deve ser usado durante o desenvolvimento de aplicativos. Os aplicativos de produção sempre devem usar a segurança do transporte. Para obter mais informações, confira Considerações de segurança no gRPC para ASP.NET Core.

Os ativos de C# do gRPC não são gerados por código com base nos arquivos .proto

A geração de código gRPC de clientes concretos e classes base de serviço requer que arquivos e ferramentas protobuf sejam referenciados de um projeto. Você precisa incluir:

  • Arquivos .proto que você deseja usar no grupo de itens <Protobuf>. Os arquivos .protoimportados precisam ser referenciados pelo projeto.
  • Referência de pacote para o pacote de ferramentas Grpc.Tools.

Para obter mais informações sobre como gerar ativos C# gRPC, confira Serviços gRPC com C#.

Um aplicativo Web ASP.NET Core que hospeda serviços gRPC só precisa da classe base de serviço gerada:

<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
</ItemGroup>

Um aplicativo cliente gRPC que faz chamadas gRPC só precisa do cliente concreto gerado:

<ItemGroup>
  <Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>

Projetos do WPF não podem gerar ativos de C# do gRPC de arquivos .proto

Os projetos do WPF têm um problema conhecido que impede que a geração de código gRPC funcione corretamente. Todos os tipos de gRPC gerados em um projeto do WPF referenciando arquivos Grpc.Tools e .proto criarão erros de compilação quando usados:

Erro CS0246: O tipo ou o nome do namespace 'MyGrpcServices' não foi encontrado (uma diretiva de uso ou uma referência de assembly está ausente?)

Você pode solucionar esse problema de forma alternativa:

  1. Criar um novo projeto de biblioteca de classes do .NET Core.
  2. No novo projeto, adicione referências para habilitar a geração de código C# de arquivos .proto:
  3. No aplicativo WPF, adicione uma referência ao novo projeto.

O aplicativo WPF pode usar os tipos gerados por gRPC do novo projeto de biblioteca de classes.

Chamando serviços gRPC hospedados em um subdiretório

Aviso

Muitas ferramentas gRPC de terceiros não dão suporte a serviços hospedados em subdiretórios. Considere encontrar uma maneira de hospedar o gRPC como o diretório raiz.

O componente de caminho do endereço de um canal gRPC é ignorado ao fazer chamadas gRPC. Por exemplo, GrpcChannel.ForAddress("https://localhost:5001/ignored_path") não usará ignored_path ao rotear chamadas gRPC para o serviço.

O caminho do endereço é ignorado porque o gRPC tem uma estrutura de endereços padronizada e prescritiva. Um endereço gRPC combina os nomes de pacote, serviço e método: https://localhost:5001/PackageName.ServiceName/MethodName.

Há alguns cenários em que um aplicativo precisa incluir um caminho com chamadas gRPC. Por exemplo, quando um aplicativo gRPC do ASP.NET Core é hospedado em um diretório do IIS e o diretório precisa ser incluído na solicitação. Quando um caminho é obrigatório, ele pode ser adicionado à chamada gRPC usando o SubdirectoryHandler personalizado especificado abaixo:

/// <summary>
/// A delegating handler that adds a subdirectory to the URI of gRPC requests.
/// </summary>
public class SubdirectoryHandler : DelegatingHandler
{
    private readonly string _subdirectory;

    public SubdirectoryHandler(HttpMessageHandler innerHandler, string subdirectory)
        : base(innerHandler)
    {
        _subdirectory = subdirectory;
    }

    protected override Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var old = request.RequestUri;

        var url = $"{old.Scheme}://{old.Host}:{old.Port}";
        url += $"{_subdirectory}{request.RequestUri.AbsolutePath}";
        request.RequestUri = new Uri(url, UriKind.Absolute);

        return base.SendAsync(request, cancellationToken);
    }
}

SubdirectoryHandler é usado quando o canal gRPC é criado.

var handler = new SubdirectoryHandler(new HttpClientHandler(), "/MyApp");

var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions { HttpHandler = handler });
var client = new Greet.GreeterClient(channel);

var reply = await client.SayHelloAsync(new HelloRequest { Name = ".NET" });

O código anterior:

  • Cria um SubdirectoryHandler com o caminho /MyApp.
  • Configura um canal para usar SubdirectoryHandler.
  • Chama o serviço gRPC com SayHelloAsync. A chamada gRPC é enviada para https://localhost:5001/MyApp/greet.Greeter/SayHello.

Como alternativa, uma fábrica de clientes pode ser configurada com SubdirectoryHandler usando AddHttpMessageHandler.

Tempo limite da chamada gRPC de HttpClient.Timeout

HttpClient é configurado com um tempo limite de 100 segundos por padrão. Se GrpcChannel estiver configurado para usar HttpClient, as chamadas de streaming gRPC de longa duração serão canceladas se não forem concluídas dentro do limite de tempo.

System.OperationCanceledException: The request was canceled due to the configured HttpClient.Timeout of 100 seconds elapsing.

Há algumas maneiras de corrigir esse erro. A primeira é configurar HttpClient.Timeout com um valor maior. Timeout.InfiniteTimeSpan desabilita o tempo limite:

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var httpClient = new HttpClient(handler) { Timeout = Timeout.InfiniteTimeSpan };
var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpClient = httpClient });
var client = new Greeter.GreeterClient(channel);

Como alternativa, evite criar HttpClient e, em vez disso, defina GrpcChannel.HttpHandler:

var handler = new HttpClientHandler();
handler.ServerCertificateCustomValidationCallback = 
    HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

var channel = GrpcChannel.ForAddress("https://localhost:5001",
    new GrpcChannelOptions { HttpHandler = handler });
var client = new Greeter.GreeterClient(channel);