Solución de problemas de gRPC en .NET

Por James Newton-King

Nota

Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 9 de este artículo.

Advertencia

Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulta la Directiva de soporte técnico de .NET y .NET Core. Para la versión actual, consulta la versión .NET 8 de este artículo.

Importante

Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.

Para la versión actual, consulte la versión de .NET 9 de este artículo.

En este documento se describen los problemas que suelen surgir al desarrollar aplicaciones de gRPC en .NET.

Falta de coincidencia entre la configuración de cliente y servicio de SSL/TLS

La plantilla y los ejemplos de gRPC usan Seguridad de la capa de transporte (TLS) de forma predeterminada para proteger los servicios gRPC. Los clientes de gRPC deben usar una conexión segura para llamar correctamente a los servicios gRPC protegidos.

Puede comprobar si el servicio gRPC de ASP.NET Core usa TLS en los registros que se escriben al iniciar la aplicación. El servicio escuchará en un punto de conexión 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

El cliente de .NET Core debe usar https en la dirección del servidor para realizar llamadas con una conexión segura:

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

Todas las implementaciones de cliente de gRPC admiten TLS. Los clientes de gRPC de otros lenguajes de programación normalmente requieren que el canal esté configurado con SslCredentials. SslCredentials especifica el certificado que usará el cliente, y debe usarse en lugar de credenciales no seguras. Para obtener ejemplos de configuración de las distintas implementaciones de cliente de gRPC para usar TLS, vea Autenticación en gRPC.

Llamada a un servicio gRPC con un certificado no válido o que no es de confianza

El cliente de gRPC de .NET requiere que el servicio tenga un certificado de confianza. Cuando se llama a un servicio gRPC sin un certificado de confianza, se devuelve el siguiente mensaje de error:

Excepción no controlada. System.Net.Http.HttpRequestException: No se ha podido establecer la conexión SSL, vea la excepción interna. ---> System.Security.Authentication.AuthenticationException: el certificado remoto no es válido de acuerdo con el procedimiento de validación.

Puede aparecer este error si está probando la aplicación de forma local y el certificado de desarrollo HTTPS de ASP.NET Core no es de confianza. Para instrucciones sobre cómo corregir este problema, consulte Confiar en el certificado de desarrollo de ASP.NET Core HTTPS en Windows y macOS.

Si está llamando a un servicio gRPC en otro equipo y no puede confiar en el certificado, el cliente de gRPC se puede configurar para que omita el certificado no válido. En el código siguiente se usa HttpClientHandler.ServerCertificateCustomValidationCallback para permitir llamadas sin un certificado de confianza:

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);

La fábrica cliente gRPC permite llamadas sin un certificado de confianza. Use el método de extensión ConfigurePrimaryHttpMessageHandler para configurar el controlador en el 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;
    });

Advertencia

Los certificados que no son de confianza solo se deben usar durante el desarrollo de aplicaciones. Las aplicaciones de producción siempre deben usar certificados válidos.

Llamada a servicios gRPC no seguros con el cliente de .NET Core

El cliente de gRPC de .NET puede llamar a servicios de gRPC no seguros al especificar http en la dirección del servidor. Por ejemplo, GrpcChannel.ForAddress("http://localhost:5000").

Hay algunos requisitos adicionales para llamar a los servicios de gRPC no seguros en función de la versión de .NET que use una aplicación:

Importante

Los servicios gRPC no seguros deben hospedarse en un puerto solo HTTP/2. Para más información, consulte Negociación de protocolos de ASP.NET Core.

No se puede iniciar la aplicación gRPC de ASP.NET Core en macOS

Kestrel no admite HTTP/2 con TLS en macOS antes de .NET 8. La plantilla y los ejemplos de gRPC de ASP.NET Core usan TLS de forma predeterminada. Al intentar iniciar el servidor de gRPC, verá el siguiente mensaje de error:

No se puede enlazar a https://localhost:5001 en la interfaz de bucle invertido de IPv4: "No se admite HTTP/2 a través de TLS en macOS debido a la falta de compatibilidad con ALPN".

Para solucionar esta incidencia en .NET 7, configure Kestrel y el cliente de gRPC para que use HTTP/2 sin TLS. Esto solo debe realizarse durante el desarrollo. Si no se usa TLS, se enviarán mensajes gRPC sin cifrado. Para obtener más información, consulte Asp.Net Core 7.0: no se puede iniciar la aplicación gRPC de ASP.NET Core en macOS.

Recursos de C# para gRPC no generados a partir de archivos .proto

Para generar código de gRPC de clientes concretos y clases base de servicio es necesario hacer referencia a los archivos y las herramientas de Protobuf desde un proyecto. Debe incluir:

  • Los archivos .proto que quiere usar en el grupo de elementos <Protobuf>. El proyecto debe hacer referencia a los archivos .proto importados.
  • Una referencia de paquete al paquete de herramientas de gRPC Grpc.Tools.

Para más información sobre la generación de recursos de C# para gRPC, consulte Servicios gRPC con C#.

Una aplicación web de ASP.NET Core que hospeda servicios gRPC solo necesita la clase base de servicio generada:

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

Una aplicación cliente de gRPC que realiza llamadas a gRPC solo necesita el cliente concreto generado:

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

Imposibilidad para los proyectos de WPF de generar recursos de C# para gRPC a partir de archivos .proto

Los proyectos de WPF tienen un problema conocido que impide que la generación de código de gRPC se realice correctamente. Los tipos de gRPC generados en un proyecto de WPF haciendo referencia a Grpc.Tools y a archivos .proto producirán errores de compilación cuando se usen:

Error CS0246: No se ha encontrado el tipo o el nombre del espacio de nombres "MyGrpcServices" (¿falta una directiva "using" o una referencia de ensamblado?).

Para solucionar este problema:

  1. Cree un proyecto de biblioteca de clases .NET Core.
  2. En el nuevo proyecto, agregue referencias para habilitar la generación de código de C# a partir de archivos .proto:
  3. En la aplicación de WPF, agregue una referencia al nuevo proyecto.

La aplicación de WPF puede usar los tipos generados por gRPC a partir del nuevo proyecto de biblioteca de clases.

Llamada a servicios de gRPC hospedados en un subdirectorio

Advertencia

Muchas herramientas de gRPC de terceros no admiten servicios hospedados en subdirectorios. Considere la posibilidad de buscar una manera de hospedar gRPC como directorio raíz.

El componente de ruta de acceso de la dirección de un canal de gRPC se pasa por alto al realizar llamadas gRPC. Por ejemplo, GrpcChannel.ForAddress("https://localhost:5001/ignored_path") no se utilizará ignored_path al enrutar llamadas gRPC del servicio.

Se omite la ruta de acceso de la dirección porque gRPC tiene una estructura de dirección preceptiva normalizada. Una dirección gRPC combina los nombres de paquete, servicio y método: https://localhost:5001/PackageName.ServiceName/MethodName.

Hay algunos escenarios en los que una aplicación necesita incluir una ruta de acceso con llamadas gRPC. Por ejemplo, cuando una aplicación gRPC de ASP.NET Core está hospedada en un directorio de IIS y el directorio debe incluirse en la solicitud. Cuando se requiera una ruta de acceso, se puede agregar a la llamada gRPC mediante el valor de SubdirectoryHandler personalizado especificado a continuación:

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 se usa cuando se crea el canal gRPC.

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" });

El código anterior:

  • Crea un objeto SubdirectoryHandler con la ruta de acceso /MyApp.
  • Configura un canal para que use SubdirectoryHandler.
  • Llama al servicio gRPC con SayHelloAsync. La llamada gRPC se envía a https://localhost:5001/MyApp/greet.Greeter/SayHello.

Como alternativa, se puede configurar una fábrica de cliente con SubdirectoryHandler mediante AddHttpMessageHandler.

Configuración del cliente gRPC para usar HTTP/3

El cliente gRPC de .NET admite HTTP/3 con .NET 6 o posterior. Si el servidor envía un encabezado de respuesta alt-svc al cliente que indica que el servidor admite HTTP/3, el cliente actualizará automáticamente su conexión a HTTP/3. Para más información, consulte Uso de HTTP/3 con el servidor web Kestrel de ASP.NET Core.

Se puede usar un elemento DelegatingHandler para forzar a un cliente gRPC a usar HTTP/3. Al forzar HTTP/3 se evita la sobrecarga de actualizar la solicitud. Fuerce HTTP/3 con código similar al siguiente:

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 se usa cuando se crea el canal gRPC. El código siguiente crea un 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, se puede configurar una fábrica de cliente con Http3Handler mediante AddHttpMessageHandler.

Compilación de gRPC en Alpine Linux

El paquete Grpc.Toolsgenera tipos de .NET a partir de archivos .proto mediante un binario nativo agrupado denominado protoc. Se requieren pasos adicionales para crear aplicaciones gRPC en las plataformas que no son compatibles con los archivos binarios nativos en Grpc.Tools, como Alpine Linux.

Generación de código con antelación

Una solución consiste en generar código con antelación.

  1. Mueva los archivos .proto y la referencia del paquete Grpc.Tools a un nuevo proyecto.
  2. Publique el proyecto como paquete NuGet y cárguelo en una fuente de NuGet.
  3. Actualice la aplicación para hacer referencia al paquete NuGet.

Con los pasos anteriores, la aplicación ya no requiere Grpc.Tools para la creación, ya que el código se genera con antelación.

Personalización de archivos binarios nativos Grpc.Tools

Grpc.Tools admite los archivos binarios nativos personalizados. Esta característica permite que las herramientas de gRPC se ejecuten en entornos que sus archivos binarios nativos agrupados no admiten.

Cree o adquiera archivos binarios nativos protoc y grpc_csharp_plugin, y configure Grpc.Tools para que los use. Establezca las siguientes variables de entorno para configurar archivos binarios:

  • PROTOBUF_PROTOC: ruta de acceso completa al compilador de búferes de protocolo
  • GRPC_PROTOC_PLUGIN: ruta de acceso completa a grpc_csharp_plugin

Para Alpine Linux, hay paquetes que proporciona la comunidad para el compilador de búferes de protocolo y los complementos gRPC en 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 más información sobre el uso de Grpc.Tools con arquitecturas no admitidas, consulte la documentación de la integración de versiones de gRPC.

Tiempo de espera de llamada de gRPC desde HttpClient.Timeout

HttpClient se configura con un tiempo de espera de 100 segundos de forma predeterminada. Si un GrpcChannel está configurado para usar un HttpClient, las llamadas de streaming de gRPC de ejecución prolongada se cancelan si no se completan dentro del límite de tiempo de espera.

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

Hay un par de maneras de corregir este error. La primera consiste en configurar HttpClient.Timeout en un valor mayor. Timeout.InfiniteTimeSpan deshabilita el tiempo de espera:

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 crear HttpClient y establezca GrpcChannel.HttpHandler en su lugar:

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);

En este documento se describen los problemas que suelen surgir al desarrollar aplicaciones de gRPC en .NET.

Falta de coincidencia entre la configuración de cliente y servicio de SSL/TLS

La plantilla y los ejemplos de gRPC usan Seguridad de la capa de transporte (TLS) de forma predeterminada para proteger los servicios gRPC. Los clientes de gRPC deben usar una conexión segura para llamar correctamente a los servicios gRPC protegidos.

Puede comprobar si el servicio gRPC de ASP.NET Core usa TLS en los registros que se escriben al iniciar la aplicación. El servicio escuchará en un punto de conexión 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

El cliente de .NET Core debe usar https en la dirección del servidor para realizar llamadas con una conexión segura:

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 las implementaciones de cliente de gRPC admiten TLS. Los clientes de gRPC de otros lenguajes de programación normalmente requieren que el canal esté configurado con SslCredentials. SslCredentials especifica el certificado que usará el cliente, y debe usarse en lugar de credenciales no seguras. Para obtener ejemplos de configuración de las distintas implementaciones de cliente de gRPC para usar TLS, vea Autenticación en gRPC.

Llamada a un servicio gRPC con un certificado no válido o que no es de confianza

El cliente de gRPC de .NET requiere que el servicio tenga un certificado de confianza. Cuando se llama a un servicio gRPC sin un certificado de confianza, se devuelve el siguiente mensaje de error:

Excepción no controlada. System.Net.Http.HttpRequestException: No se ha podido establecer la conexión SSL, vea la excepción interna. ---> System.Security.Authentication.AuthenticationException: el certificado remoto no es válido de acuerdo con el procedimiento de validación.

Puede aparecer este error si está probando la aplicación de forma local y el certificado de desarrollo HTTPS de ASP.NET Core no es de confianza. Para instrucciones sobre cómo corregir este problema, consulte Confiar en el certificado de desarrollo de ASP.NET Core HTTPS en Windows y macOS.

Si está llamando a un servicio gRPC en otro equipo y no puede confiar en el certificado, el cliente de gRPC se puede configurar para que omita el certificado no válido. En el código siguiente se usa HttpClientHandler.ServerCertificateCustomValidationCallback para permitir llamadas sin un certificado de confianza:

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);

La fábrica cliente gRPC permite llamadas sin un certificado de confianza. Use el método de extensión ConfigurePrimaryHttpMessageHandler para configurar el controlador en el 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;
    });

Advertencia

Los certificados que no son de confianza solo se deben usar durante el desarrollo de aplicaciones. Las aplicaciones de producción siempre deben usar certificados válidos.

Llamada a servicios gRPC no seguros con el cliente de .NET Core

El cliente de gRPC de .NET puede llamar a servicios de gRPC no seguros al especificar http en la dirección del servidor. Por ejemplo, GrpcChannel.ForAddress("http://localhost:5000").

Hay algunos requisitos adicionales para llamar a los servicios de gRPC no seguros en función de la versión de .NET que use una aplicación:

  • .NET 5 y versiones posteriores requieren la versión 2.32.0 u otra posterior de Grpc.Net.Client.

  • .NET Core 3.x requiere una configuración adicional. La aplicación debe establecer el conmutador System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport en 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);
    

El conmutador System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport solo es necesario para .NET Core 3.x. No afecta a .NET 5 y no es necesario.

Importante

Los servicios gRPC no seguros deben hospedarse en un puerto solo HTTP/2. Para más información, consulte Negociación de protocolos de ASP.NET Core.

No se puede iniciar la aplicación gRPC de ASP.NET Core en macOS

Kestrel no admite HTTP/2 con TLS en macOS antes de .NET 8. La plantilla y los ejemplos de gRPC de ASP.NET Core usan TLS de forma predeterminada. Al intentar iniciar el servidor de gRPC, verá el siguiente mensaje de error:

No se puede enlazar a https://localhost:5001 en la interfaz de bucle invertido de IPv4: "No se admite HTTP/2 a través de TLS en macOS debido a la falta de compatibilidad con ALPN".

Para solucionar esta incidencia en .NET 7, configure Kestrel y el cliente de gRPC para que use HTTP/2 sin TLS. Esto solo debe realizarse durante el desarrollo. Si no se usa TLS, se enviarán mensajes gRPC sin cifrado.

Kestrel debe configurar un punto de conexión HTTP/2 sin TLS en 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);
});
  • En el código anterior, reemplace el número de puerto localhost 5287 por el número de puerto HTTP (no HTTPS) especificado en Properties/launchSettings.json dentro del proyecto de servicio gRPC.

Si un punto de conexión HTTP/2 se configura sin TLS, el valor ListenOptions.Protocols del punto de conexión debe establecerse en HttpProtocols.Http2. HttpProtocols.Http1AndHttp2 no se puede usar porque se requiere TLS para negociar HTTP/2. Sin TLS, se usa HTTP/1.1 de forma predeterminada para todas las conexiones al punto de conexión y se produce un error en las llamadas a gRPC.

El cliente de gRPC también debe estar configurado para no usar TLS. Para obtener más información, consulte la sección Llamada a servicios gRPC no seguros con el cliente de .NET Core.

Advertencia

Solo se debe usar HTTP/2 sin TLS durante el desarrollo de aplicaciones. Las aplicaciones de producción siempre deben usar seguridad de transporte. Para obtener más información, vea Consideraciones de seguridad en gRPC para ASP.NET Core.

Recursos de C# para gRPC no generados a partir de archivos .proto

Para generar código de gRPC de clientes concretos y clases base de servicio es necesario hacer referencia a los archivos y las herramientas de Protobuf desde un proyecto. Debe incluir:

  • Los archivos .proto que quiere usar en el grupo de elementos <Protobuf>. El proyecto debe hacer referencia a los archivos .proto importados.
  • Una referencia de paquete al paquete de herramientas de gRPC Grpc.Tools.

Para más información sobre la generación de recursos de C# para gRPC, consulte Servicios gRPC con C#.

Una aplicación web de ASP.NET Core que hospeda servicios gRPC solo necesita la clase base de servicio generada:

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

Una aplicación cliente de gRPC que realiza llamadas a gRPC solo necesita el cliente concreto generado:

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

Imposibilidad para los proyectos de WPF de generar recursos de C# para gRPC a partir de archivos .proto

Los proyectos de WPF tienen un problema conocido que impide que la generación de código de gRPC se realice correctamente. Los tipos de gRPC generados en un proyecto de WPF haciendo referencia a Grpc.Tools y a archivos .proto producirán errores de compilación cuando se usen:

Error CS0246: No se ha encontrado el tipo o el nombre del espacio de nombres "MyGrpcServices" (¿falta una directiva "using" o una referencia de ensamblado?).

Para solucionar este problema:

  1. Cree un proyecto de biblioteca de clases .NET Core.
  2. En el nuevo proyecto, agregue referencias para habilitar la generación de código de C# a partir de archivos .proto:
  3. En la aplicación de WPF, agregue una referencia al nuevo proyecto.

La aplicación de WPF puede usar los tipos generados por gRPC a partir del nuevo proyecto de biblioteca de clases.

Llamada a servicios de gRPC hospedados en un subdirectorio

Advertencia

Muchas herramientas de gRPC de terceros no admiten servicios hospedados en subdirectorios. Considere la posibilidad de buscar una manera de hospedar gRPC como directorio raíz.

El componente de ruta de acceso de la dirección de un canal de gRPC se pasa por alto al realizar llamadas gRPC. Por ejemplo, GrpcChannel.ForAddress("https://localhost:5001/ignored_path") no se utilizará ignored_path al enrutar llamadas gRPC del servicio.

Se omite la ruta de acceso de la dirección porque gRPC tiene una estructura de dirección preceptiva normalizada. Una dirección gRPC combina los nombres de paquete, servicio y método: https://localhost:5001/PackageName.ServiceName/MethodName.

Hay algunos escenarios en los que una aplicación necesita incluir una ruta de acceso con llamadas gRPC. Por ejemplo, cuando una aplicación gRPC de ASP.NET Core está hospedada en un directorio de IIS y el directorio debe incluirse en la solicitud. Cuando se requiera una ruta de acceso, se puede agregar a la llamada gRPC mediante el valor de SubdirectoryHandler personalizado especificado a continuación:

/// <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 se usa cuando se crea el canal gRPC.

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" });

El código anterior:

  • Crea un objeto SubdirectoryHandler con la ruta de acceso /MyApp.
  • Configura un canal para que use SubdirectoryHandler.
  • Llama al servicio gRPC con SayHelloAsync. La llamada gRPC se envía a https://localhost:5001/MyApp/greet.Greeter/SayHello.

Como alternativa, se puede configurar una fábrica de cliente con SubdirectoryHandler mediante AddHttpMessageHandler.

Configuración del cliente gRPC para usar HTTP/3

El cliente gRPC de .NET admite HTTP/3 con .NET 6 o posterior. Si el servidor envía un encabezado de respuesta alt-svc al cliente que indica que el servidor admite HTTP/3, el cliente actualizará automáticamente su conexión a HTTP/3. Para obtener información sobre cómo habilitar HTTP/3 en el servidor, vea Uso de HTTP/3 con el servidor web Kestrel de ASP.NET Core.

La compatibilidad con HTTP/3 en .NET 8 está habilitada de forma predeterminada. La compatibilidad con HTTP/3 en .NET 6 y .NET 7 debe habilitarse mediante una marca de configuración en el archivo del proyecto:

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

System.Net.SocketsHttpHandler.Http3Support también se puede establecer mediante AppContext.SetSwitch.

Se puede usar un elemento DelegatingHandler para forzar a un cliente gRPC a usar HTTP/3. Al forzar HTTP/3 se evita la sobrecarga de actualizar la solicitud. Fuerce HTTP/3 con código similar al siguiente:

/// <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 se usa cuando se crea el canal gRPC. El código siguiente crea un 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, se puede configurar una fábrica de cliente con Http3Handler mediante AddHttpMessageHandler.

Compilación de gRPC en Alpine Linux

El paquete Grpc.Toolsgenera tipos de .NET a partir de archivos .proto mediante un binario nativo agrupado denominado protoc. Se requieren pasos adicionales para crear aplicaciones gRPC en las plataformas que no son compatibles con los archivos binarios nativos en Grpc.Tools, como Alpine Linux.

Generación de código con antelación

Una solución consiste en generar código con antelación.

  1. Mueva los archivos .proto y la referencia del paquete Grpc.Tools a un nuevo proyecto.
  2. Publique el proyecto como paquete NuGet y cárguelo en una fuente de NuGet.
  3. Actualice la aplicación para hacer referencia al paquete NuGet.

Con los pasos anteriores, la aplicación ya no requiere Grpc.Tools para la creación, ya que el código se genera con antelación.

Personalización de archivos binarios nativos Grpc.Tools

Grpc.Tools admite los archivos binarios nativos personalizados. Esta característica permite que las herramientas de gRPC se ejecuten en entornos que sus archivos binarios nativos agrupados no admiten.

Cree o adquiera archivos binarios nativos protoc y grpc_csharp_plugin, y configure Grpc.Tools para que los use. Establezca las siguientes variables de entorno para configurar archivos binarios:

  • PROTOBUF_PROTOC: ruta de acceso completa al compilador de búferes de protocolo
  • GRPC_PROTOC_PLUGIN: ruta de acceso completa a grpc_csharp_plugin

Para Alpine Linux, hay paquetes que proporciona la comunidad para el compilador de búferes de protocolo y los complementos gRPC en 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 más información sobre el uso de Grpc.Tools con arquitecturas no admitidas, consulte la documentación de la integración de versiones de gRPC.

Tiempo de espera de llamada de gRPC desde HttpClient.Timeout

HttpClient se configura con un tiempo de espera de 100 segundos de forma predeterminada. Si un GrpcChannel está configurado para usar un HttpClient, las llamadas de streaming de gRPC de ejecución prolongada se cancelan si no se completan dentro del límite de tiempo de espera.

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

Hay un par de maneras de corregir este error. La primera consiste en configurar HttpClient.Timeout en un valor mayor. Timeout.InfiniteTimeSpan deshabilita el tiempo de espera:

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 crear HttpClient y establezca GrpcChannel.HttpHandler en su lugar:

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);

En este documento se describen los problemas que suelen surgir al desarrollar aplicaciones de gRPC en .NET.

Falta de coincidencia entre la configuración de cliente y servicio de SSL/TLS

La plantilla y los ejemplos de gRPC usan Seguridad de la capa de transporte (TLS) de forma predeterminada para proteger los servicios gRPC. Los clientes de gRPC deben usar una conexión segura para llamar correctamente a los servicios gRPC protegidos.

Puede comprobar si el servicio gRPC de ASP.NET Core usa TLS en los registros que se escriben al iniciar la aplicación. El servicio escuchará en un punto de conexión 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

El cliente de .NET Core debe usar https en la dirección del servidor para realizar llamadas con una conexión segura:

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 las implementaciones de cliente de gRPC admiten TLS. Los clientes de gRPC de otros lenguajes de programación normalmente requieren que el canal esté configurado con SslCredentials. SslCredentials especifica el certificado que usará el cliente, y debe usarse en lugar de credenciales no seguras. Para obtener ejemplos de configuración de las distintas implementaciones de cliente de gRPC para usar TLS, vea Autenticación en gRPC.

Llamada a un servicio gRPC con un certificado no válido o que no es de confianza

El cliente de gRPC de .NET requiere que el servicio tenga un certificado de confianza. Cuando se llama a un servicio gRPC sin un certificado de confianza, se devuelve el siguiente mensaje de error:

Excepción no controlada. System.Net.Http.HttpRequestException: No se ha podido establecer la conexión SSL, vea la excepción interna. ---> System.Security.Authentication.AuthenticationException: el certificado remoto no es válido de acuerdo con el procedimiento de validación.

Puede aparecer este error si está probando la aplicación de forma local y el certificado de desarrollo HTTPS de ASP.NET Core no es de confianza. Para instrucciones sobre cómo corregir este problema, consulte Confiar en el certificado de desarrollo de ASP.NET Core HTTPS en Windows y macOS.

Si está llamando a un servicio gRPC en otro equipo y no puede confiar en el certificado, el cliente de gRPC se puede configurar para que omita el certificado no válido. En el código siguiente se usa HttpClientHandler.ServerCertificateCustomValidationCallback para permitir llamadas sin un certificado de confianza:

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);

La fábrica cliente gRPC permite llamadas sin un certificado de confianza. Use el método de extensión ConfigurePrimaryHttpMessageHandler para configurar el controlador en el cliente:

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

        return handler;
    });

Advertencia

Los certificados que no son de confianza solo se deben usar durante el desarrollo de aplicaciones. Las aplicaciones de producción siempre deben usar certificados válidos.

Llamada a servicios gRPC no seguros con el cliente de .NET Core

El cliente de gRPC de .NET puede llamar a servicios de gRPC no seguros al especificar http en la dirección del servidor. Por ejemplo, GrpcChannel.ForAddress("http://localhost:5000").

Hay algunos requisitos adicionales para llamar a los servicios de gRPC no seguros en función de la versión de .NET que use una aplicación:

  • .NET 5 y versiones posteriores requieren la versión 2.32.0 u otra posterior de Grpc.Net.Client.

  • .NET Core 3.x requiere una configuración adicional. La aplicación debe establecer el conmutador System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport en 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);
    

El conmutador System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport solo es necesario para .NET Core 3.x. No afecta a .NET 5 y no es necesario.

Importante

Los servicios gRPC no seguros deben hospedarse en un puerto solo HTTP/2. Para más información, consulte Negociación de protocolos de ASP.NET Core.

No se puede iniciar la aplicación gRPC de ASP.NET Core en macOS

Kestrel no admite HTTP/2 con TLS en macOS antes de .NET 8. La plantilla y los ejemplos de gRPC de ASP.NET Core usan TLS de forma predeterminada. Al intentar iniciar el servidor de gRPC, verá el siguiente mensaje de error:

No se puede enlazar a https://localhost:5001 en la interfaz de bucle invertido de IPv4: "No se admite HTTP/2 a través de TLS en macOS debido a la falta de compatibilidad con ALPN".

Para solucionar esta incidencia en .NET 7, configure Kestrel y el cliente de gRPC para que use HTTP/2 sin TLS. Esto solo debe realizarse durante el desarrollo. Si no se usa TLS, se enviarán mensajes gRPC sin cifrado.

Kestrel debe configurar un punto de conexión HTTP/2 sin TLS en 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>();
        });

Si un punto de conexión HTTP/2 se configura sin TLS, el valor ListenOptions.Protocols del punto de conexión debe establecerse en HttpProtocols.Http2. HttpProtocols.Http1AndHttp2 no se puede usar porque se requiere TLS para negociar HTTP/2. Sin TLS, se usa HTTP/1.1 de forma predeterminada para todas las conexiones al punto de conexión y se produce un error en las llamadas a gRPC.

El cliente de gRPC también debe estar configurado para no usar TLS. Para obtener más información, consulte la sección Llamada a servicios gRPC no seguros con el cliente de .NET Core.

Advertencia

Solo se debe usar HTTP/2 sin TLS durante el desarrollo de aplicaciones. Las aplicaciones de producción siempre deben usar seguridad de transporte. Para obtener más información, vea Consideraciones de seguridad en gRPC para ASP.NET Core.

Recursos de C# para gRPC no generados a partir de archivos .proto

Para generar código de gRPC de clientes concretos y clases base de servicio es necesario hacer referencia a los archivos y las herramientas de Protobuf desde un proyecto. Debe incluir:

  • Los archivos .proto que quiere usar en el grupo de elementos <Protobuf>. El proyecto debe hacer referencia a los archivos .proto importados.
  • Una referencia de paquete al paquete de herramientas de gRPC Grpc.Tools.

Para más información sobre la generación de recursos de C# para gRPC, consulte Servicios gRPC con C#.

Una aplicación web de ASP.NET Core que hospeda servicios gRPC solo necesita la clase base de servicio generada:

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

Una aplicación cliente de gRPC que realiza llamadas a gRPC solo necesita el cliente concreto generado:

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

Imposibilidad para los proyectos de WPF de generar recursos de C# para gRPC a partir de archivos .proto

Los proyectos de WPF tienen un problema conocido que impide que la generación de código de gRPC se realice correctamente. Los tipos de gRPC generados en un proyecto de WPF haciendo referencia a Grpc.Tools y a archivos .proto producirán errores de compilación cuando se usen:

Error CS0246: No se ha encontrado el tipo o el nombre del espacio de nombres "MyGrpcServices" (¿falta una directiva "using" o una referencia de ensamblado?).

Para solucionar este problema:

  1. Cree un proyecto de biblioteca de clases .NET Core.
  2. En el nuevo proyecto, agregue referencias para habilitar la generación de código de C# a partir de archivos .proto:
  3. En la aplicación de WPF, agregue una referencia al nuevo proyecto.

La aplicación de WPF puede usar los tipos generados por gRPC a partir del nuevo proyecto de biblioteca de clases.

Llamada a servicios de gRPC hospedados en un subdirectorio

Advertencia

Muchas herramientas de gRPC de terceros no admiten servicios hospedados en subdirectorios. Considere la posibilidad de buscar una manera de hospedar gRPC como directorio raíz.

El componente de ruta de acceso de la dirección de un canal de gRPC se pasa por alto al realizar llamadas gRPC. Por ejemplo, GrpcChannel.ForAddress("https://localhost:5001/ignored_path") no se utilizará ignored_path al enrutar llamadas gRPC del servicio.

Se omite la ruta de acceso de la dirección porque gRPC tiene una estructura de dirección preceptiva normalizada. Una dirección gRPC combina los nombres de paquete, servicio y método: https://localhost:5001/PackageName.ServiceName/MethodName.

Hay algunos escenarios en los que una aplicación necesita incluir una ruta de acceso con llamadas gRPC. Por ejemplo, cuando una aplicación gRPC de ASP.NET Core está hospedada en un directorio de IIS y el directorio debe incluirse en la solicitud. Cuando se requiera una ruta de acceso, se puede agregar a la llamada gRPC mediante el valor de SubdirectoryHandler personalizado especificado a continuación:

/// <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 se usa cuando se crea el canal gRPC.

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" });

El código anterior:

  • Crea un objeto SubdirectoryHandler con la ruta de acceso /MyApp.
  • Configura un canal para que use SubdirectoryHandler.
  • Llama al servicio gRPC con SayHelloAsync. La llamada gRPC se envía a https://localhost:5001/MyApp/greet.Greeter/SayHello.

Como alternativa, se puede configurar una fábrica de cliente con SubdirectoryHandler mediante AddHttpMessageHandler.

Tiempo de espera de llamada de gRPC desde HttpClient.Timeout

HttpClient se configura con un tiempo de espera de 100 segundos de forma predeterminada. Si un GrpcChannel está configurado para usar un HttpClient, las llamadas de streaming de gRPC de ejecución prolongada se cancelan si no se completan dentro del límite de tiempo de espera.

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

Hay un par de maneras de corregir este error. La primera consiste en configurar HttpClient.Timeout en un valor mayor. Timeout.InfiniteTimeSpan deshabilita el tiempo de espera:

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 crear HttpClient y establezca GrpcChannel.HttpHandler en su lugar:

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);

En este documento se describen los problemas que suelen surgir al desarrollar aplicaciones de gRPC en .NET.

Falta de coincidencia entre la configuración de cliente y servicio de SSL/TLS

La plantilla y los ejemplos de gRPC usan Seguridad de la capa de transporte (TLS) de forma predeterminada para proteger los servicios gRPC. Los clientes de gRPC deben usar una conexión segura para llamar correctamente a los servicios gRPC protegidos.

Puede comprobar si el servicio gRPC de ASP.NET Core usa TLS en los registros que se escriben al iniciar la aplicación. El servicio escuchará en un punto de conexión 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

El cliente de .NET Core debe usar https en la dirección del servidor para realizar llamadas con una conexión segura:

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 las implementaciones de cliente de gRPC admiten TLS. Los clientes de gRPC de otros lenguajes de programación normalmente requieren que el canal esté configurado con SslCredentials. SslCredentials especifica el certificado que usará el cliente, y debe usarse en lugar de credenciales no seguras. Para obtener ejemplos de configuración de las distintas implementaciones de cliente de gRPC para usar TLS, vea Autenticación en gRPC.

Llamada a un servicio gRPC con un certificado no válido o que no es de confianza

El cliente de gRPC de .NET requiere que el servicio tenga un certificado de confianza. Cuando se llama a un servicio gRPC sin un certificado de confianza, se devuelve el siguiente mensaje de error:

Excepción no controlada. System.Net.Http.HttpRequestException: No se ha podido establecer la conexión SSL, vea la excepción interna. ---> System.Security.Authentication.AuthenticationException: el certificado remoto no es válido de acuerdo con el procedimiento de validación.

Puede aparecer este error si está probando la aplicación de forma local y el certificado de desarrollo HTTPS de ASP.NET Core no es de confianza. Para instrucciones sobre cómo corregir este problema, consulte Confiar en el certificado de desarrollo de ASP.NET Core HTTPS en Windows y macOS.

Si está llamando a un servicio gRPC en otro equipo y no puede confiar en el certificado, el cliente de gRPC se puede configurar para que omita el certificado no válido. En el código siguiente se usa HttpClientHandler.ServerCertificateCustomValidationCallback para permitir llamadas sin un certificado de confianza:

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);

La fábrica cliente gRPC permite llamadas sin un certificado de confianza. Use el método de extensión ConfigurePrimaryHttpMessageHandler para configurar el controlador en el cliente:

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

        return handler;
    });

Advertencia

Los certificados que no son de confianza solo se deben usar durante el desarrollo de aplicaciones. Las aplicaciones de producción siempre deben usar certificados válidos.

Llamada a servicios gRPC no seguros con el cliente de .NET Core

El cliente de gRPC de .NET puede llamar a servicios de gRPC no seguros al especificar http en la dirección del servidor. Por ejemplo, GrpcChannel.ForAddress("http://localhost:5000").

Hay algunos requisitos adicionales para llamar a los servicios de gRPC no seguros en función de la versión de .NET que use una aplicación:

  • .NET 5 y versiones posteriores requieren la versión 2.32.0 u otra posterior de Grpc.Net.Client.

  • .NET Core 3.x requiere una configuración adicional. La aplicación debe establecer el conmutador System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport en 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);
    

El conmutador System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport solo es necesario para .NET Core 3.x. No afecta a .NET 5 y no es necesario.

Importante

Los servicios gRPC no seguros deben hospedarse en un puerto solo HTTP/2. Para más información, consulte Negociación de protocolos de ASP.NET Core.

No se puede iniciar la aplicación gRPC de ASP.NET Core en macOS

Kestrel no admite HTTP/2 con TLS en macOS antes de .NET 8. La plantilla y los ejemplos de gRPC de ASP.NET Core usan TLS de forma predeterminada. Al intentar iniciar el servidor de gRPC, verá el siguiente mensaje de error:

No se puede enlazar a https://localhost:5001 en la interfaz de bucle invertido de IPv4: "No se admite HTTP/2 a través de TLS en macOS debido a la falta de compatibilidad con ALPN".

Para solucionar esta incidencia en .NET 7, configure Kestrel y el cliente de gRPC para que use HTTP/2 sin TLS. Esto solo debe realizarse durante el desarrollo. Si no se usa TLS, se enviarán mensajes gRPC sin cifrado.

Kestrel debe configurar un punto de conexión HTTP/2 sin TLS en 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>();
        });

Si un punto de conexión HTTP/2 se configura sin TLS, el valor ListenOptions.Protocols del punto de conexión debe establecerse en HttpProtocols.Http2. HttpProtocols.Http1AndHttp2 no se puede usar porque se requiere TLS para negociar HTTP/2. Sin TLS, se usa HTTP/1.1 de forma predeterminada para todas las conexiones al punto de conexión y se produce un error en las llamadas a gRPC.

El cliente de gRPC también debe estar configurado para no usar TLS. Para obtener más información, consulte la sección Llamada a servicios gRPC no seguros con el cliente de .NET Core.

Advertencia

Solo se debe usar HTTP/2 sin TLS durante el desarrollo de aplicaciones. Las aplicaciones de producción siempre deben usar seguridad de transporte. Para obtener más información, vea Consideraciones de seguridad en gRPC para ASP.NET Core.

Recursos de C# para gRPC no generados a partir de archivos .proto

Para generar código de gRPC de clientes concretos y clases base de servicio es necesario hacer referencia a los archivos y las herramientas de Protobuf desde un proyecto. Debe incluir:

  • Los archivos .proto que quiere usar en el grupo de elementos <Protobuf>. El proyecto debe hacer referencia a los archivos .proto importados.
  • Una referencia de paquete al paquete de herramientas de gRPC Grpc.Tools.

Para más información sobre la generación de recursos de C# para gRPC, consulte Servicios gRPC con C#.

Una aplicación web de ASP.NET Core que hospeda servicios gRPC solo necesita la clase base de servicio generada:

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

Una aplicación cliente de gRPC que realiza llamadas a gRPC solo necesita el cliente concreto generado:

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

Imposibilidad para los proyectos de WPF de generar recursos de C# para gRPC a partir de archivos .proto

Los proyectos de WPF tienen un problema conocido que impide que la generación de código de gRPC se realice correctamente. Los tipos de gRPC generados en un proyecto de WPF haciendo referencia a Grpc.Tools y a archivos .proto producirán errores de compilación cuando se usen:

Error CS0246: No se ha encontrado el tipo o el nombre del espacio de nombres "MyGrpcServices" (¿falta una directiva "using" o una referencia de ensamblado?).

Para solucionar este problema:

  1. Cree un proyecto de biblioteca de clases .NET Core.
  2. En el nuevo proyecto, agregue referencias para habilitar la generación de código de C# a partir de archivos .proto:
  3. En la aplicación de WPF, agregue una referencia al nuevo proyecto.

La aplicación de WPF puede usar los tipos generados por gRPC a partir del nuevo proyecto de biblioteca de clases.

Llamada a servicios de gRPC hospedados en un subdirectorio

Advertencia

Muchas herramientas de gRPC de terceros no admiten servicios hospedados en subdirectorios. Considere la posibilidad de buscar una manera de hospedar gRPC como directorio raíz.

El componente de ruta de acceso de la dirección de un canal de gRPC se pasa por alto al realizar llamadas gRPC. Por ejemplo, GrpcChannel.ForAddress("https://localhost:5001/ignored_path") no se utilizará ignored_path al enrutar llamadas gRPC del servicio.

Se omite la ruta de acceso de la dirección porque gRPC tiene una estructura de dirección preceptiva normalizada. Una dirección gRPC combina los nombres de paquete, servicio y método: https://localhost:5001/PackageName.ServiceName/MethodName.

Hay algunos escenarios en los que una aplicación necesita incluir una ruta de acceso con llamadas gRPC. Por ejemplo, cuando una aplicación gRPC de ASP.NET Core está hospedada en un directorio de IIS y el directorio debe incluirse en la solicitud. Cuando se requiera una ruta de acceso, se puede agregar a la llamada gRPC mediante el valor de SubdirectoryHandler personalizado especificado a continuación:

/// <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 se usa cuando se crea el canal gRPC.

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" });

El código anterior:

  • Crea un objeto SubdirectoryHandler con la ruta de acceso /MyApp.
  • Configura un canal para que use SubdirectoryHandler.
  • Llama al servicio gRPC con SayHelloAsync. La llamada gRPC se envía a https://localhost:5001/MyApp/greet.Greeter/SayHello.

Como alternativa, se puede configurar una fábrica de cliente con SubdirectoryHandler mediante AddHttpMessageHandler.

Tiempo de espera de llamada de gRPC desde HttpClient.Timeout

HttpClient se configura con un tiempo de espera de 100 segundos de forma predeterminada. Si un GrpcChannel está configurado para usar un HttpClient, las llamadas de streaming de gRPC de ejecución prolongada se cancelan si no se completan dentro del límite de tiempo de espera.

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

Hay un par de maneras de corregir este error. La primera consiste en configurar HttpClient.Timeout en un valor mayor. Timeout.InfiniteTimeSpan deshabilita el tiempo de espera:

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 crear HttpClient y establezca GrpcChannel.HttpHandler en su lugar:

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);