Problembehandlung für gRPC in .NET

Von James Newton-King

Hinweis

Dies ist nicht die neueste Version dieses Artikels. Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.

Warnung

Diese Version von ASP.NET Core wird nicht mehr unterstützt. Weitere Informationen finden Sie in der Supportrichtlinie für .NET und .NET Core. Informationen zum aktuellen Release finden Sie in der .NET 8-Version dieses Artikels.

Wichtig

Diese Informationen beziehen sich auf ein Vorabversionsprodukt, das vor der kommerziellen Freigabe möglicherweise noch wesentlichen Änderungen unterliegt. Microsoft gibt keine Garantie, weder ausdrücklich noch impliziert, hinsichtlich der hier bereitgestellten Informationen.

Die aktuelle Version finden Sie in der .NET 9-Version dieses Artikels.

In diesem Dokument werden häufig auftretende Probleme bei der Entwicklung von gRPC-Apps in .NET behandelt.

Nichtübereinstimmung der SSL-/TLS-Konfiguration von Client und Dienst

In der gRPC-Vorlage und den gRPC-Beispielen werden gRPC-Dienste standardmäßig durch Transport Layer Security (TLS) gesichert. gRPC-Clients müssen eine sichere Verbindung verwenden, um gesicherte gRPC-Dienste aufzurufen zu können.

Sie können in den Protokollen, die beim Starten der App geschrieben werden, überprüfen, ob ein ASP.NET Core-gRPC-Dienst TLS verwendet. Der Dienst lauscht in diesem Fall auf einen HTTPS-Endpunkt:

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

Der .NET Core-Client muss in der Serveradresse https verwenden, um Aufrufe über eine sichere Verbindung auszuführen:

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

Alle gRPC-Clientimplementierungen unterstützen TLS. Bei gRPC-Clients mit anderen Sprachen ist üblicherweise ein mit SslCredentials konfigurierter Kanal erforderlich. SslCredentials gibt das Zertifikat an, das der Client verwendet, und muss anstelle von unsicheren Anmeldeinformationen verwendet werden. Beispiele für das Konfigurieren der verschiedenen gRPC-Clientimplementierungen für die Verwendung von TLS finden Sie unter gRPC-Authentifizierung.

Aufrufen eines gRPC-Diensts mit einem nicht vertrauenswürdigen/ungültigen Zertifikat

Der .NET-gRPC-Client erfordert, dass der Dienst über ein vertrauenswürdiges Zertifikat verfügt. Die folgende Fehlermeldung wird zurückgegeben, wenn ein gRPC-Dienst ohne vertrauenswürdiges Zertifikat aufgerufen wird:

Ausnahmefehler. System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception. ---> System.Security.Authentication.AuthenticationException: Das Remotezertifikat ist gemäß dem Überprüfungsverfahren ungültig.

Dieser Fehler wird möglicherweise angezeigt, wenn Sie Ihre App lokal testen und das ASP.NET Core-HTTPS-Entwicklungszertifikat nicht vertrauenswürdig ist. Anweisungen zum Beheben dieses Problems finden Sie unter Einstufen des ASP.NET Core-HTTPS-Entwicklungszertifikats als vertrauenswürdig unter Windows und macOS.

Wenn Sie einen gRPC-Dienst auf einem anderen Computer aufrufen und das Zertifikat nicht als vertrauenswürdig einstufen können, kann der gRPC-Client so konfiguriert werden, dass er das ungültige Zertifikat ignoriert. Im folgenden Code wird HttpClientHandler.ServerCertificateCustomValidationCallback verwendet, um Aufrufe ohne vertrauenswürdiges Zertifikat zuzulassen:

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

Die gRPC-Clientfactory lässt Aufrufe ohne vertrauenswürdiges Zertifikat zu. Verwenden Sie die Erweiterungsmethode ConfigurePrimaryHttpMessageHandler, um den Handler auf dem Client zu konfigurieren:


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

Warnung

Nicht vertrauenswürdige Zertifikate sollten nur während der App-Entwicklung verwendet werden. Produktions-Apps sollten stets gültige Zertifikate verwenden.

Aufrufen unsicherer gRPC-Dienste mit dem .NET Core-Client

Der .NET-gRPC-Client kann unsichere gRPC-Dienste durch Angabe von http in der Serveradresse aufrufen. Beispiel: GrpcChannel.ForAddress("http://localhost:5000").

Je nach .NET-Version, die von einer App verwendet wird, bestehen einige zusätzliche Anforderungen, um unsichere gRPC-Dienste aufrufen zu können:

Wichtig

Unsichere gRPC-Dienste müssen auf einem reinen HTTP/2-Port gehostet werden. Weitere Informationen finden Sie unter ASP.NET Core: Protokollaushandlung.

ASP.NET Core-gRPC-App kann unter macOS nicht gestartet werden

Kestrel unterstützt HTTP/2 mit TLS unter macOS vor .NET 8 nicht. In der ASP.NET Core-gRPC-Vorlage und den ASP.NET Core-gRPC-Beispielen wird standardmäßig TLS verwendet. Wenn Sie versuchen, den gRPC-Server zu starten, wird folgende Fehlermeldung angezeigt:

Unable to bind to https://localhost:5001 on the IPv4 loopback interface: 'HTTP/2 over TLS is not supported on macOS due to missing ALPN support.'. (Eine Bindung an „https://localhost:5001“ auf der IPv4-Loopback-Schnittstelle kann nicht erfolgen: „HTTP/2 über TLS wird unter macOS aufgrund von fehlender ALPN-Unterstützung nicht unterstützt.“)

Um dieses Problem in .NET 7 und früheren Versionen zu umgehen, konfigurieren Sie Kestrel und den gRPC-Client so, dass HTTP/2 ohne TLS verwendet wird. Dies sollten Sie nur während der Entwicklung tun. Wenn TLS nicht verwendet wird, führt dies dazu, dass gRPC-Nachrichten unverschlüsselt gesendet werden. Weitere Informationen finden Sie unter ASP.NET Core 7.0: gRPC-App kann unter macOS nicht gestartet werden.

Kein Generieren von Code aus .proto-Dateien bei gRPC-C#-Objekten

Für das Generieren von gRPC-Code für konkrete Clients und Dienstbasisklassen muss in einem Projekt auf Protobuf-Dateien und -Tools verwiesen werden. Folgendes muss enthalten sein:

  • .proto-Dateien, die Sie in der Elementgruppe <Protobuf> verwenden möchten. Auf importierte .proto-Dateien muss im Projekt verwiesen werden.
  • Paketverweis auf das gRPC-Toolpaket Grpc.Tools

Weitere Informationen zum Generieren von gRPC-C#-Ressourcen finden Sie unter gRPC-Dienste mit C#.

Bei einer ASP.NET Core-Web-App, die gRPC-Dienste hostet, muss nur die Dienstbasisklasse generiert werden:

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

Bei einer gRPC-Client-App, die gRPC-Aufrufe ausführt, muss nur der konkrete Client generiert werden:

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

Generieren von gRPC-C#-Objekten aus .proto-Dateien in WPF-Projekten nicht möglich

Es gibt ein bekanntes Problem bei WPF-Projekten, das verhindert, dass das Generieren von gRPC-Code richtig funktioniert. Bei der Verwendung aller gRPC-Typen, die in einem WPF-Projekt durch Verweise auf Grpc.Tools und .proto-Dateien generiert werden, treten Kompilierungsfehler auf:

Fehler CS0246: Der Typ- oder Namespacename „MyGrpcServices“ wurde nicht gefunden (möglicherweise fehlt eine using-Direktive oder ein Assemblyverweis).

So umgehen Sie dieses Problem:

  1. Erstellen Sie ein neues .NET Core-Klassenbibliotheksprojekt.
  2. Fügen Sie im neuen Projekt Verweise hinzu, um das Generieren von C#-Code aus .proto-Dateien zu ermöglichen:
  3. Fügen Sie in der WPF-Anwendung einen Verweis auf das neue Projekt hinzu.

Die WPF-Anwendung kann die in gRPC generierten Typen aus dem neuen Klassenbibliotheksprojekt verwenden.

Aufrufen von in einem Unterverzeichnis gehosteten gRPC-Diensten

Warnung

Viele gRPC-Tools von Drittanbietern unterstützen in Unterverzeichnissen gehostete Dienste nicht. Ziehen Sie in Betracht, eine Möglichkeit zum Hosten von gRPC als Stammverzeichnis zu finden.

Die Pfadkomponente der Adresse eines gRPC-Kanals wird beim Ausführen von gRPC-Aufrufen ignoriert. Beispielsweise verwendet GrpcChannel.ForAddress("https://localhost:5001/ignored_path") nicht ignored_path, wenn gRPC-Aufrufe für den Dienst weitergeleitet werden.

Der Adresspfad wird ignoriert, da gRPC eine standardisierte, vorgeschriebene Adressstruktur besitzt. Eine gRPC-Adresse kombiniert die Paket-, Dienst- und Methodennamen: https://localhost:5001/PackageName.ServiceName/MethodName.

Es gibt einige Szenarien, in denen eine App einen Pfad mit gRPC-Aufrufen einschließen muss. Wenn beispielsweise eine ASP.NET Core gRPC-App in einem IIS-Verzeichnis gehostet wird und das Verzeichnis in die Anforderung aufgenommen werden muss. Wenn ein Pfad erforderlich ist, kann er dem gRPC-Aufruf mithilfe des unten angegebenen benutzerdefinierten SubdirectoryHandler hinzugefügt werden:

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 wird verwendet, wenn der gRPC-Kanal erstellt wird.

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

Der vorangehende Code:

  • Erstellt einen SubdirectoryHandler mit dem Pfad /MyApp.
  • Konfiguriert einen Kanal für die Verwendung von SubdirectoryHandler.
  • Ruft den gRPC-Dienst mit SayHelloAsync auf. Der gRPC-Aufruf wird an https://localhost:5001/MyApp/greet.Greeter/SayHello gesendet.

Alternativ kann eine Clientfactory durch Verwendung von AddHttpMessageHandler mit SubdirectoryHandler konfiguriert werden.

Konfigurieren eines gRPC-Clients für die Verwendung von HTTP/3

Der .NET-gRPC-Client unterstützt HTTP/3 mit .NET 6 oder höher. Wenn der Server einen alt-svc-Antwortheader an den Client sendet, der angibt, dass der Server HTTP/3 unterstützt, aktualisiert der Client seine Verbindung automatisch auf HTTP/3. Weitere Informationen finden Sie unter Verwenden von HTTP/3 mit dem ASP.NET Core-Kestrel-Webserver.

Ein DelegatingHandler kann verwendet werden, um die Verwendung von HTTP/3 durch einen gRPC-Client zu erzwingen. Durch das Erzwingen von HTTP/3 entfällt der Aufwand für das Upgrade der Anforderung. Erzwingen Sie HTTP/3 mit Code ähnlich dem folgenden:

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 wird verwendet, wenn der gRPC-Kanal erstellt wird. Mit dem folgenden Code wird ein Kanal erstellt, der für die Verwendung von Http3Handler konfiguriert ist.

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

Alternativ kann eine Clientfactory durch Verwendung von AddHttpMessageHandler mit Http3Handler konfiguriert werden.

Erstellen von gRPC unter Alpine Linux

Das Grpc.Tools-Paket generiert .NET-Typen aus .proto-Dateien mithilfe einer gebündelten nativen Binärdatei namens protoc. Es sind zusätzliche Schritte erforderlich, um gRPC-Apps auf Plattformen zu erstellen, die von den nativen Binärdateien in Grpc.Toolsnicht unterstützt werden, z. B. Alpine Linux.

Generieren von Code im Voraus

Eine Lösung besteht darin, Code im Voraus zu generieren.

  1. Verschieben sie .proto-Dateien und den Grpc.Tools-Paketverweis in ein neues Projekt.
  2. Veröffentlichen Sie das Projekt als NuGet-Paket, und laden Sie es in einen NuGet-Feed hoch.
  3. Aktualisieren Sie die App so, dass sie auf das NuGet-Paket verweist.

Mit den vorangegangenen Schritten muss die Anwendung nicht mehr mit Grpc.Tools erstellt werden, da der Code bereits im Voraus generiert wird.

Anpassen nativer Grpc.Tools-Binärdateien

Grpc.Tools unterstützt die Verwendung benutzerdefinierter nativer Binärdateien. Dieses Feature ermöglicht die Ausführung von gRPC-Tools in Umgebungen, die von den gebündelten nativen Binärdateien nicht unterstützt werden.

Erstellen oder erwerben Sie native protoc- und grpc_csharp_plugin-Binärdateien, und konfigurieren Sie Grpc.Tools so, dass es sie verwendet. Konfigurieren Sie native Binärdateien, indem Sie die folgenden Umgebungsvariablen festlegen:

  • PROTOBUF_PROTOC – Vollständiger Pfad zum Protokollpuffercompiler
  • GRPC_PROTOC_PLUGIN – Vollständiger Pfad zum grpc_csharp_plugin

Für Alpine Linux gibt es von der Community bereitgestellte Pakete für den Protokollpuffercompiler und gRPC-Plug-Ins unter 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

Weitere Informationen zur Verwendung von Grpc.Tools mit nicht unterstützten Architekturen finden Sie in der gRPC-Buildintegrationsdokumentation.

gRPC-Aufruftimeout von HttpClient.Timeout

HttpClient ist standardmäßig mit einem Timeout von 100 Sekunden konfiguriert. Wenn ein GrpcChannel für die Verwendung eines HttpClient konfiguriert ist, werden zeitintensive gRPC-Streamingaufrufe abgebrochen, falls sie nicht innerhalb des Timeouts abgeschlossen werden.

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

Es gibt mehrere Möglichkeiten, diesen Fehler zu beheben. Der erste besteht darin, HttpClient.Timeout mit einem größeren Wert zu konfigurieren. Timeout.InfiniteTimeSpan deaktiviert das Timeout:

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

Alternativ können Sie das Erstellen von HttpClient vermeiden und stattdessen GrpcChannel.HttpHandler festlegen:

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

In diesem Dokument werden häufig auftretende Probleme bei der Entwicklung von gRPC-Apps in .NET behandelt.

Nichtübereinstimmung der SSL-/TLS-Konfiguration von Client und Dienst

In der gRPC-Vorlage und den gRPC-Beispielen werden gRPC-Dienste standardmäßig durch Transport Layer Security (TLS) gesichert. gRPC-Clients müssen eine sichere Verbindung verwenden, um gesicherte gRPC-Dienste aufzurufen zu können.

Sie können in den Protokollen, die beim Starten der App geschrieben werden, überprüfen, ob ein ASP.NET Core-gRPC-Dienst TLS verwendet. Der Dienst lauscht in diesem Fall auf einen HTTPS-Endpunkt:

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

Der .NET Core-Client muss in der Serveradresse https verwenden, um Aufrufe über eine sichere Verbindung auszuführen:

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

Alle gRPC-Clientimplementierungen unterstützen TLS. Bei gRPC-Clients mit anderen Sprachen ist üblicherweise ein mit SslCredentials konfigurierter Kanal erforderlich. SslCredentials gibt das Zertifikat an, das der Client verwendet, und muss anstelle von unsicheren Anmeldeinformationen verwendet werden. Beispiele für das Konfigurieren der verschiedenen gRPC-Clientimplementierungen für die Verwendung von TLS finden Sie unter gRPC-Authentifizierung.

Aufrufen eines gRPC-Diensts mit einem nicht vertrauenswürdigen/ungültigen Zertifikat

Der .NET-gRPC-Client erfordert, dass der Dienst über ein vertrauenswürdiges Zertifikat verfügt. Die folgende Fehlermeldung wird zurückgegeben, wenn ein gRPC-Dienst ohne vertrauenswürdiges Zertifikat aufgerufen wird:

Ausnahmefehler. System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception. ---> System.Security.Authentication.AuthenticationException: Das Remotezertifikat ist gemäß dem Überprüfungsverfahren ungültig.

Dieser Fehler wird möglicherweise angezeigt, wenn Sie Ihre App lokal testen und das ASP.NET Core-HTTPS-Entwicklungszertifikat nicht vertrauenswürdig ist. Anweisungen zum Beheben dieses Problems finden Sie unter Einstufen des ASP.NET Core-HTTPS-Entwicklungszertifikats als vertrauenswürdig unter Windows und macOS.

Wenn Sie einen gRPC-Dienst auf einem anderen Computer aufrufen und das Zertifikat nicht als vertrauenswürdig einstufen können, kann der gRPC-Client so konfiguriert werden, dass er das ungültige Zertifikat ignoriert. Im folgenden Code wird HttpClientHandler.ServerCertificateCustomValidationCallback verwendet, um Aufrufe ohne vertrauenswürdiges Zertifikat zuzulassen:

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

Die gRPC-Clientfactory lässt Aufrufe ohne vertrauenswürdiges Zertifikat zu. Verwenden Sie die Erweiterungsmethode ConfigurePrimaryHttpMessageHandler, um den Handler auf dem Client zu konfigurieren:

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

        return handler;
    });

Warnung

Nicht vertrauenswürdige Zertifikate sollten nur während der App-Entwicklung verwendet werden. Produktions-Apps sollten stets gültige Zertifikate verwenden.

Aufrufen unsicherer gRPC-Dienste mit dem .NET Core-Client

Der .NET-gRPC-Client kann unsichere gRPC-Dienste durch Angabe von http in der Serveradresse aufrufen. Beispiel: GrpcChannel.ForAddress("http://localhost:5000").

Je nach .NET-Version, die von einer App verwendet wird, bestehen einige zusätzliche Anforderungen, um unsichere gRPC-Dienste aufrufen zu können:

  • Für .NET 5 oder höher ist die Grpc.Net.Client-Version 2.32.0 oder höher erforderlich.

  • Für .NET Core 3.x ist eine zusätzliche Konfiguration erforderlich. Der System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport-Switch muss durch die App auf true festgelegt werden:

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

Der System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport-Switch ist nur für .NET Core 3.x erforderlich. Bei .NET 5 hat er keine Funktion und ist nicht erforderlich.

Wichtig

Unsichere gRPC-Dienste müssen auf einem reinen HTTP/2-Port gehostet werden. Weitere Informationen finden Sie unter ASP.NET Core: Protokollaushandlung.

ASP.NET Core-gRPC-App kann unter macOS nicht gestartet werden

Kestrel unterstützt HTTP/2 mit TLS unter macOS vor .NET 8 nicht. In der ASP.NET Core-gRPC-Vorlage und den ASP.NET Core-gRPC-Beispielen wird standardmäßig TLS verwendet. Wenn Sie versuchen, den gRPC-Server zu starten, wird folgende Fehlermeldung angezeigt:

Unable to bind to https://localhost:5001 on the IPv4 loopback interface: 'HTTP/2 over TLS is not supported on macOS due to missing ALPN support.'. (Eine Bindung an „https://localhost:5001“ auf der IPv4-Loopback-Schnittstelle kann nicht erfolgen: „HTTP/2 über TLS wird unter macOS aufgrund von fehlender ALPN-Unterstützung nicht unterstützt.“)

Um dieses Problem in .NET 7 und früheren Versionen zu umgehen, konfigurieren Sie Kestrel und den gRPC-Client so, dass HTTP/2 ohne TLS verwendet wird. Dies sollten Sie nur während der Entwicklung tun. Wenn TLS nicht verwendet wird, führt dies dazu, dass gRPC-Nachrichten unverschlüsselt gesendet werden.

Kestrel muss in Program.cs einen HTTP/2-Endpunkt ohne TLS konfigurieren:

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(options =>
{
    // Setup a HTTP/2 endpoint without TLS.
    options.ListenLocalhost(<5287>, o => o.Protocols =
        HttpProtocols.Http2);
});
  • Ersetzen Sie im obigen Code die Portnummer 5287 von localhost durch die in Properties/launchSettings.json (nicht HTTPS) angegebene Portnummer HTTP innerhalb des gRPC-Dienstprojekts.

Wenn ein HTTP/2-Endpunkt ohne TLS konfiguriert wird, muss bei diesem Endpunkt für ListenOptions.ProtocolsHttpProtocols.Http2 festgelegt werden. HttpProtocols.Http1AndHttp2 kann nicht verwendet werden, da zum Aushandeln von HTTP/2 TLS erforderlich ist. Ohne TLS wird für alle Verbindungen zu diesem Endpunkt standardmäßig HTTP/1.1 festgelegt, und gRPC-Aufrufe schlagen fehl.

Der gRPC-Client muss ebenfalls so konfiguriert sein, dass er kein TLS verwendet. Weitere Informationen finden Sie im Abschnitt Aufrufen unsicherer gRPC-Dienste mit dem .NET Core-Client.

Warnung

HTTP/2 ohne TLS sollte nur während der App-Entwicklung verwendet werden. Produktions-Apps sollten stets Transportsicherheit verwenden. Weitere Informationen finden Sie unter Sicherheitsüberlegungen zu gRPC für ASP.NET Core.

Kein Generieren von Code aus .proto-Dateien bei gRPC-C#-Objekten

Für das Generieren von gRPC-Code für konkrete Clients und Dienstbasisklassen muss in einem Projekt auf Protobuf-Dateien und -Tools verwiesen werden. Folgendes muss enthalten sein:

  • .proto-Dateien, die Sie in der Elementgruppe <Protobuf> verwenden möchten. Auf importierte .proto-Dateien muss im Projekt verwiesen werden.
  • Paketverweis auf das gRPC-Toolpaket Grpc.Tools

Weitere Informationen zum Generieren von gRPC-C#-Ressourcen finden Sie unter gRPC-Dienste mit C#.

Bei einer ASP.NET Core-Web-App, die gRPC-Dienste hostet, muss nur die Dienstbasisklasse generiert werden:

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

Bei einer gRPC-Client-App, die gRPC-Aufrufe ausführt, muss nur der konkrete Client generiert werden:

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

Generieren von gRPC-C#-Objekten aus .proto-Dateien in WPF-Projekten nicht möglich

Es gibt ein bekanntes Problem bei WPF-Projekten, das verhindert, dass das Generieren von gRPC-Code richtig funktioniert. Bei der Verwendung aller gRPC-Typen, die in einem WPF-Projekt durch Verweise auf Grpc.Tools und .proto-Dateien generiert werden, treten Kompilierungsfehler auf:

Fehler CS0246: Der Typ- oder Namespacename „MyGrpcServices“ wurde nicht gefunden (möglicherweise fehlt eine using-Direktive oder ein Assemblyverweis).

So umgehen Sie dieses Problem:

  1. Erstellen Sie ein neues .NET Core-Klassenbibliotheksprojekt.
  2. Fügen Sie im neuen Projekt Verweise hinzu, um das Generieren von C#-Code aus .proto-Dateien zu ermöglichen:
  3. Fügen Sie in der WPF-Anwendung einen Verweis auf das neue Projekt hinzu.

Die WPF-Anwendung kann die in gRPC generierten Typen aus dem neuen Klassenbibliotheksprojekt verwenden.

Aufrufen von in einem Unterverzeichnis gehosteten gRPC-Diensten

Warnung

Viele gRPC-Tools von Drittanbietern unterstützen in Unterverzeichnissen gehostete Dienste nicht. Ziehen Sie in Betracht, eine Möglichkeit zum Hosten von gRPC als Stammverzeichnis zu finden.

Die Pfadkomponente der Adresse eines gRPC-Kanals wird beim Ausführen von gRPC-Aufrufen ignoriert. Beispielsweise verwendet GrpcChannel.ForAddress("https://localhost:5001/ignored_path") nicht ignored_path, wenn gRPC-Aufrufe für den Dienst weitergeleitet werden.

Der Adresspfad wird ignoriert, da gRPC eine standardisierte, vorgeschriebene Adressstruktur besitzt. Eine gRPC-Adresse kombiniert die Paket-, Dienst- und Methodennamen: https://localhost:5001/PackageName.ServiceName/MethodName.

Es gibt einige Szenarien, in denen eine App einen Pfad mit gRPC-Aufrufen einschließen muss. Wenn beispielsweise eine ASP.NET Core gRPC-App in einem IIS-Verzeichnis gehostet wird und das Verzeichnis in die Anforderung aufgenommen werden muss. Wenn ein Pfad erforderlich ist, kann er dem gRPC-Aufruf mithilfe des unten angegebenen benutzerdefinierten SubdirectoryHandler hinzugefügt werden:

/// <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 wird verwendet, wenn der gRPC-Kanal erstellt wird.

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

Der vorangehende Code:

  • Erstellt einen SubdirectoryHandler mit dem Pfad /MyApp.
  • Konfiguriert einen Kanal für die Verwendung von SubdirectoryHandler.
  • Ruft den gRPC-Dienst mit SayHelloAsync auf. Der gRPC-Aufruf wird an https://localhost:5001/MyApp/greet.Greeter/SayHello gesendet.

Alternativ kann eine Clientfactory durch Verwendung von AddHttpMessageHandler mit SubdirectoryHandler konfiguriert werden.

Konfigurieren eines gRPC-Clients für die Verwendung von HTTP/3

Der .NET-gRPC-Client unterstützt HTTP/3 mit .NET 6 oder höher. Wenn der Server einen alt-svc-Antwortheader an den Client sendet, der angibt, dass der Server HTTP/3 unterstützt, aktualisiert der Client seine Verbindung automatisch auf HTTP/3. Informationen zum Aktivieren von HTTP/3 auf dem Server finden Sie unter Verwenden von HTTP/3 mit dem Kestrel-Webserver in ASP.NET Core.

Die HTTP/3-Unterstützung in .NET 8 ist standardmäßig aktiviert. In .NET 6 und .NET 7 muss die HTTP/3-Unterstützung über ein Konfigurationsflag in der Projektdatei aktiviert werden:

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

System.Net.SocketsHttpHandler.Http3Support kann auch über AppContext.SetSwitch festgelegt werden.

Ein DelegatingHandler kann verwendet werden, um die Verwendung von HTTP/3 durch einen gRPC-Client zu erzwingen. Durch das Erzwingen von HTTP/3 entfällt der Aufwand für das Upgrade der Anforderung. Erzwingen Sie HTTP/3 mit Code ähnlich dem folgenden:

/// <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 wird verwendet, wenn der gRPC-Kanal erstellt wird. Mit dem folgenden Code wird ein Kanal erstellt, der für die Verwendung von Http3Handler konfiguriert ist.

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

Alternativ kann eine Clientfactory durch Verwendung von AddHttpMessageHandler mit Http3Handler konfiguriert werden.

Erstellen von gRPC unter Alpine Linux

Das Grpc.Tools-Paket generiert .NET-Typen aus .proto-Dateien mithilfe einer gebündelten nativen Binärdatei namens protoc. Es sind zusätzliche Schritte erforderlich, um gRPC-Apps auf Plattformen zu erstellen, die von den nativen Binärdateien in Grpc.Toolsnicht unterstützt werden, z. B. Alpine Linux.

Generieren von Code im Voraus

Eine Lösung besteht darin, Code im Voraus zu generieren.

  1. Verschieben sie .proto-Dateien und den Grpc.Tools-Paketverweis in ein neues Projekt.
  2. Veröffentlichen Sie das Projekt als NuGet-Paket, und laden Sie es in einen NuGet-Feed hoch.
  3. Aktualisieren Sie die App so, dass sie auf das NuGet-Paket verweist.

Mit den vorangegangenen Schritten muss die Anwendung nicht mehr mit Grpc.Tools erstellt werden, da der Code bereits im Voraus generiert wird.

Anpassen nativer Grpc.Tools-Binärdateien

Grpc.Tools unterstützt die Verwendung benutzerdefinierter nativer Binärdateien. Dieses Feature ermöglicht die Ausführung von gRPC-Tools in Umgebungen, die von den gebündelten nativen Binärdateien nicht unterstützt werden.

Erstellen oder erwerben Sie native protoc- und grpc_csharp_plugin-Binärdateien, und konfigurieren Sie Grpc.Tools so, dass es sie verwendet. Konfigurieren Sie native Binärdateien, indem Sie die folgenden Umgebungsvariablen festlegen:

  • PROTOBUF_PROTOC – Vollständiger Pfad zum Protokollpuffercompiler
  • GRPC_PROTOC_PLUGIN – Vollständiger Pfad zum grpc_csharp_plugin

Für Alpine Linux gibt es von der Community bereitgestellte Pakete für den Protokollpuffercompiler und gRPC-Plug-Ins unter 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

Weitere Informationen zur Verwendung von Grpc.Tools mit nicht unterstützten Architekturen finden Sie in der gRPC-Buildintegrationsdokumentation.

gRPC-Aufruftimeout von HttpClient.Timeout

HttpClient ist standardmäßig mit einem Timeout von 100 Sekunden konfiguriert. Wenn ein GrpcChannel für die Verwendung eines HttpClient konfiguriert ist, werden zeitintensive gRPC-Streamingaufrufe abgebrochen, falls sie nicht innerhalb des Timeouts abgeschlossen werden.

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

Es gibt mehrere Möglichkeiten, diesen Fehler zu beheben. Der erste besteht darin, HttpClient.Timeout mit einem größeren Wert zu konfigurieren. Timeout.InfiniteTimeSpan deaktiviert das Timeout:

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

Alternativ können Sie das Erstellen von HttpClient vermeiden und stattdessen GrpcChannel.HttpHandler festlegen:

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

In diesem Dokument werden häufig auftretende Probleme bei der Entwicklung von gRPC-Apps in .NET behandelt.

Nichtübereinstimmung der SSL-/TLS-Konfiguration von Client und Dienst

In der gRPC-Vorlage und den gRPC-Beispielen werden gRPC-Dienste standardmäßig durch Transport Layer Security (TLS) gesichert. gRPC-Clients müssen eine sichere Verbindung verwenden, um gesicherte gRPC-Dienste aufzurufen zu können.

Sie können in den Protokollen, die beim Starten der App geschrieben werden, überprüfen, ob ein ASP.NET Core-gRPC-Dienst TLS verwendet. Der Dienst lauscht in diesem Fall auf einen HTTPS-Endpunkt:

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

Der .NET Core-Client muss in der Serveradresse https verwenden, um Aufrufe über eine sichere Verbindung auszuführen:

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

Alle gRPC-Clientimplementierungen unterstützen TLS. Bei gRPC-Clients mit anderen Sprachen ist üblicherweise ein mit SslCredentials konfigurierter Kanal erforderlich. SslCredentials gibt das Zertifikat an, das der Client verwendet, und muss anstelle von unsicheren Anmeldeinformationen verwendet werden. Beispiele für das Konfigurieren der verschiedenen gRPC-Clientimplementierungen für die Verwendung von TLS finden Sie unter gRPC-Authentifizierung.

Aufrufen eines gRPC-Diensts mit einem nicht vertrauenswürdigen/ungültigen Zertifikat

Der .NET-gRPC-Client erfordert, dass der Dienst über ein vertrauenswürdiges Zertifikat verfügt. Die folgende Fehlermeldung wird zurückgegeben, wenn ein gRPC-Dienst ohne vertrauenswürdiges Zertifikat aufgerufen wird:

Ausnahmefehler. System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception. ---> System.Security.Authentication.AuthenticationException: Das Remotezertifikat ist gemäß dem Überprüfungsverfahren ungültig.

Dieser Fehler wird möglicherweise angezeigt, wenn Sie Ihre App lokal testen und das ASP.NET Core-HTTPS-Entwicklungszertifikat nicht vertrauenswürdig ist. Anweisungen zum Beheben dieses Problems finden Sie unter Einstufen des ASP.NET Core-HTTPS-Entwicklungszertifikats als vertrauenswürdig unter Windows und macOS.

Wenn Sie einen gRPC-Dienst auf einem anderen Computer aufrufen und das Zertifikat nicht als vertrauenswürdig einstufen können, kann der gRPC-Client so konfiguriert werden, dass er das ungültige Zertifikat ignoriert. Im folgenden Code wird HttpClientHandler.ServerCertificateCustomValidationCallback verwendet, um Aufrufe ohne vertrauenswürdiges Zertifikat zuzulassen:

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

Die gRPC-Clientfactory lässt Aufrufe ohne vertrauenswürdiges Zertifikat zu. Verwenden Sie die Erweiterungsmethode ConfigurePrimaryHttpMessageHandler, um den Handler auf dem Client zu konfigurieren:

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

        return handler;
    });

Warnung

Nicht vertrauenswürdige Zertifikate sollten nur während der App-Entwicklung verwendet werden. Produktions-Apps sollten stets gültige Zertifikate verwenden.

Aufrufen unsicherer gRPC-Dienste mit dem .NET Core-Client

Der .NET-gRPC-Client kann unsichere gRPC-Dienste durch Angabe von http in der Serveradresse aufrufen. Beispiel: GrpcChannel.ForAddress("http://localhost:5000").

Je nach .NET-Version, die von einer App verwendet wird, bestehen einige zusätzliche Anforderungen, um unsichere gRPC-Dienste aufrufen zu können:

  • Für .NET 5 oder höher ist die Grpc.Net.Client-Version 2.32.0 oder höher erforderlich.

  • Für .NET Core 3.x ist eine zusätzliche Konfiguration erforderlich. Der System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport-Switch muss durch die App auf true festgelegt werden:

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

Der System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport-Switch ist nur für .NET Core 3.x erforderlich. Bei .NET 5 hat er keine Funktion und ist nicht erforderlich.

Wichtig

Unsichere gRPC-Dienste müssen auf einem reinen HTTP/2-Port gehostet werden. Weitere Informationen finden Sie unter ASP.NET Core: Protokollaushandlung.

ASP.NET Core-gRPC-App kann unter macOS nicht gestartet werden

Kestrel unterstützt HTTP/2 mit TLS unter macOS vor .NET 8 nicht. In der ASP.NET Core-gRPC-Vorlage und den ASP.NET Core-gRPC-Beispielen wird standardmäßig TLS verwendet. Wenn Sie versuchen, den gRPC-Server zu starten, wird folgende Fehlermeldung angezeigt:

Unable to bind to https://localhost:5001 on the IPv4 loopback interface: 'HTTP/2 over TLS is not supported on macOS due to missing ALPN support.'. (Eine Bindung an „https://localhost:5001“ auf der IPv4-Loopback-Schnittstelle kann nicht erfolgen: „HTTP/2 über TLS wird unter macOS aufgrund von fehlender ALPN-Unterstützung nicht unterstützt.“)

Um dieses Problem in .NET 7 und früheren Versionen zu umgehen, konfigurieren Sie Kestrel und den gRPC-Client so, dass HTTP/2 ohne TLS verwendet wird. Dies sollten Sie nur während der Entwicklung tun. Wenn TLS nicht verwendet wird, führt dies dazu, dass gRPC-Nachrichten unverschlüsselt gesendet werden.

Kestrel muss in Program.cs einen HTTP/2-Endpunkt ohne TLS konfigurieren:

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

Wenn ein HTTP/2-Endpunkt ohne TLS konfiguriert wird, muss bei diesem Endpunkt für ListenOptions.ProtocolsHttpProtocols.Http2 festgelegt werden. HttpProtocols.Http1AndHttp2 kann nicht verwendet werden, da zum Aushandeln von HTTP/2 TLS erforderlich ist. Ohne TLS wird für alle Verbindungen zu diesem Endpunkt standardmäßig HTTP/1.1 festgelegt, und gRPC-Aufrufe schlagen fehl.

Der gRPC-Client muss ebenfalls so konfiguriert sein, dass er kein TLS verwendet. Weitere Informationen finden Sie im Abschnitt Aufrufen unsicherer gRPC-Dienste mit dem .NET Core-Client.

Warnung

HTTP/2 ohne TLS sollte nur während der App-Entwicklung verwendet werden. Produktions-Apps sollten stets Transportsicherheit verwenden. Weitere Informationen finden Sie unter Sicherheitsüberlegungen zu gRPC für ASP.NET Core.

Kein Generieren von Code aus .proto-Dateien bei gRPC-C#-Objekten

Für das Generieren von gRPC-Code für konkrete Clients und Dienstbasisklassen muss in einem Projekt auf Protobuf-Dateien und -Tools verwiesen werden. Folgendes muss enthalten sein:

  • .proto-Dateien, die Sie in der Elementgruppe <Protobuf> verwenden möchten. Auf importierte .proto-Dateien muss im Projekt verwiesen werden.
  • Paketverweis auf das gRPC-Toolpaket Grpc.Tools

Weitere Informationen zum Generieren von gRPC-C#-Ressourcen finden Sie unter gRPC-Dienste mit C#.

Bei einer ASP.NET Core-Web-App, die gRPC-Dienste hostet, muss nur die Dienstbasisklasse generiert werden:

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

Bei einer gRPC-Client-App, die gRPC-Aufrufe ausführt, muss nur der konkrete Client generiert werden:

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

Generieren von gRPC-C#-Objekten aus .proto-Dateien in WPF-Projekten nicht möglich

Es gibt ein bekanntes Problem bei WPF-Projekten, das verhindert, dass das Generieren von gRPC-Code richtig funktioniert. Bei der Verwendung aller gRPC-Typen, die in einem WPF-Projekt durch Verweise auf Grpc.Tools und .proto-Dateien generiert werden, treten Kompilierungsfehler auf:

Fehler CS0246: Der Typ- oder Namespacename „MyGrpcServices“ wurde nicht gefunden (möglicherweise fehlt eine using-Direktive oder ein Assemblyverweis).

So umgehen Sie dieses Problem:

  1. Erstellen Sie ein neues .NET Core-Klassenbibliotheksprojekt.
  2. Fügen Sie im neuen Projekt Verweise hinzu, um das Generieren von C#-Code aus .proto-Dateien zu ermöglichen:
  3. Fügen Sie in der WPF-Anwendung einen Verweis auf das neue Projekt hinzu.

Die WPF-Anwendung kann die in gRPC generierten Typen aus dem neuen Klassenbibliotheksprojekt verwenden.

Aufrufen von in einem Unterverzeichnis gehosteten gRPC-Diensten

Warnung

Viele gRPC-Tools von Drittanbietern unterstützen in Unterverzeichnissen gehostete Dienste nicht. Ziehen Sie in Betracht, eine Möglichkeit zum Hosten von gRPC als Stammverzeichnis zu finden.

Die Pfadkomponente der Adresse eines gRPC-Kanals wird beim Ausführen von gRPC-Aufrufen ignoriert. Beispielsweise verwendet GrpcChannel.ForAddress("https://localhost:5001/ignored_path") nicht ignored_path, wenn gRPC-Aufrufe für den Dienst weitergeleitet werden.

Der Adresspfad wird ignoriert, da gRPC eine standardisierte, vorgeschriebene Adressstruktur besitzt. Eine gRPC-Adresse kombiniert die Paket-, Dienst- und Methodennamen: https://localhost:5001/PackageName.ServiceName/MethodName.

Es gibt einige Szenarien, in denen eine App einen Pfad mit gRPC-Aufrufen einschließen muss. Wenn beispielsweise eine ASP.NET Core gRPC-App in einem IIS-Verzeichnis gehostet wird und das Verzeichnis in die Anforderung aufgenommen werden muss. Wenn ein Pfad erforderlich ist, kann er dem gRPC-Aufruf mithilfe des unten angegebenen benutzerdefinierten SubdirectoryHandler hinzugefügt werden:

/// <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 wird verwendet, wenn der gRPC-Kanal erstellt wird.

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

Der vorangehende Code:

  • Erstellt einen SubdirectoryHandler mit dem Pfad /MyApp.
  • Konfiguriert einen Kanal für die Verwendung von SubdirectoryHandler.
  • Ruft den gRPC-Dienst mit SayHelloAsync auf. Der gRPC-Aufruf wird an https://localhost:5001/MyApp/greet.Greeter/SayHello gesendet.

Alternativ kann eine Clientfactory durch Verwendung von AddHttpMessageHandler mit SubdirectoryHandler konfiguriert werden.

gRPC-Aufruftimeout von HttpClient.Timeout

HttpClient ist standardmäßig mit einem Timeout von 100 Sekunden konfiguriert. Wenn ein GrpcChannel für die Verwendung eines HttpClient konfiguriert ist, werden zeitintensive gRPC-Streamingaufrufe abgebrochen, falls sie nicht innerhalb des Timeouts abgeschlossen werden.

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

Es gibt mehrere Möglichkeiten, diesen Fehler zu beheben. Der erste besteht darin, HttpClient.Timeout mit einem größeren Wert zu konfigurieren. Timeout.InfiniteTimeSpan deaktiviert das Timeout:

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

Alternativ können Sie das Erstellen von HttpClient vermeiden und stattdessen GrpcChannel.HttpHandler festlegen:

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

In diesem Dokument werden häufig auftretende Probleme bei der Entwicklung von gRPC-Apps in .NET behandelt.

Nichtübereinstimmung der SSL-/TLS-Konfiguration von Client und Dienst

In der gRPC-Vorlage und den gRPC-Beispielen werden gRPC-Dienste standardmäßig durch Transport Layer Security (TLS) gesichert. gRPC-Clients müssen eine sichere Verbindung verwenden, um gesicherte gRPC-Dienste aufzurufen zu können.

Sie können in den Protokollen, die beim Starten der App geschrieben werden, überprüfen, ob ein ASP.NET Core-gRPC-Dienst TLS verwendet. Der Dienst lauscht in diesem Fall auf einen HTTPS-Endpunkt:

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

Der .NET Core-Client muss in der Serveradresse https verwenden, um Aufrufe über eine sichere Verbindung auszuführen:

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

Alle gRPC-Clientimplementierungen unterstützen TLS. Bei gRPC-Clients mit anderen Sprachen ist üblicherweise ein mit SslCredentials konfigurierter Kanal erforderlich. SslCredentials gibt das Zertifikat an, das der Client verwendet, und muss anstelle von unsicheren Anmeldeinformationen verwendet werden. Beispiele für das Konfigurieren der verschiedenen gRPC-Clientimplementierungen für die Verwendung von TLS finden Sie unter gRPC-Authentifizierung.

Aufrufen eines gRPC-Diensts mit einem nicht vertrauenswürdigen/ungültigen Zertifikat

Der .NET-gRPC-Client erfordert, dass der Dienst über ein vertrauenswürdiges Zertifikat verfügt. Die folgende Fehlermeldung wird zurückgegeben, wenn ein gRPC-Dienst ohne vertrauenswürdiges Zertifikat aufgerufen wird:

Ausnahmefehler. System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception. ---> System.Security.Authentication.AuthenticationException: Das Remotezertifikat ist gemäß dem Überprüfungsverfahren ungültig.

Dieser Fehler wird möglicherweise angezeigt, wenn Sie Ihre App lokal testen und das ASP.NET Core-HTTPS-Entwicklungszertifikat nicht vertrauenswürdig ist. Anweisungen zum Beheben dieses Problems finden Sie unter Einstufen des ASP.NET Core-HTTPS-Entwicklungszertifikats als vertrauenswürdig unter Windows und macOS.

Wenn Sie einen gRPC-Dienst auf einem anderen Computer aufrufen und das Zertifikat nicht als vertrauenswürdig einstufen können, kann der gRPC-Client so konfiguriert werden, dass er das ungültige Zertifikat ignoriert. Im folgenden Code wird HttpClientHandler.ServerCertificateCustomValidationCallback verwendet, um Aufrufe ohne vertrauenswürdiges Zertifikat zuzulassen:

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

Die gRPC-Clientfactory lässt Aufrufe ohne vertrauenswürdiges Zertifikat zu. Verwenden Sie die Erweiterungsmethode ConfigurePrimaryHttpMessageHandler, um den Handler auf dem Client zu konfigurieren:

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

        return handler;
    });

Warnung

Nicht vertrauenswürdige Zertifikate sollten nur während der App-Entwicklung verwendet werden. Produktions-Apps sollten stets gültige Zertifikate verwenden.

Aufrufen unsicherer gRPC-Dienste mit dem .NET Core-Client

Der .NET-gRPC-Client kann unsichere gRPC-Dienste durch Angabe von http in der Serveradresse aufrufen. Beispiel: GrpcChannel.ForAddress("http://localhost:5000").

Je nach .NET-Version, die von einer App verwendet wird, bestehen einige zusätzliche Anforderungen, um unsichere gRPC-Dienste aufrufen zu können:

  • Für .NET 5 oder höher ist die Grpc.Net.Client-Version 2.32.0 oder höher erforderlich.

  • Für .NET Core 3.x ist eine zusätzliche Konfiguration erforderlich. Der System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport-Switch muss durch die App auf true festgelegt werden:

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

Der System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport-Switch ist nur für .NET Core 3.x erforderlich. Bei .NET 5 hat er keine Funktion und ist nicht erforderlich.

Wichtig

Unsichere gRPC-Dienste müssen auf einem reinen HTTP/2-Port gehostet werden. Weitere Informationen finden Sie unter ASP.NET Core: Protokollaushandlung.

ASP.NET Core-gRPC-App kann unter macOS nicht gestartet werden

Kestrel unterstützt HTTP/2 mit TLS unter macOS vor .NET 8 nicht. In der ASP.NET Core-gRPC-Vorlage und den ASP.NET Core-gRPC-Beispielen wird standardmäßig TLS verwendet. Wenn Sie versuchen, den gRPC-Server zu starten, wird folgende Fehlermeldung angezeigt:

Unable to bind to https://localhost:5001 on the IPv4 loopback interface: 'HTTP/2 over TLS is not supported on macOS due to missing ALPN support.'. (Eine Bindung an „https://localhost:5001“ auf der IPv4-Loopback-Schnittstelle kann nicht erfolgen: „HTTP/2 über TLS wird unter macOS aufgrund von fehlender ALPN-Unterstützung nicht unterstützt.“)

Um dieses Problem in .NET 7 und früheren Versionen zu umgehen, konfigurieren Sie Kestrel und den gRPC-Client so, dass HTTP/2 ohne TLS verwendet wird. Dies sollten Sie nur während der Entwicklung tun. Wenn TLS nicht verwendet wird, führt dies dazu, dass gRPC-Nachrichten unverschlüsselt gesendet werden.

Kestrel muss in Program.cs einen HTTP/2-Endpunkt ohne TLS konfigurieren:

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

Wenn ein HTTP/2-Endpunkt ohne TLS konfiguriert wird, muss bei diesem Endpunkt für ListenOptions.ProtocolsHttpProtocols.Http2 festgelegt werden. HttpProtocols.Http1AndHttp2 kann nicht verwendet werden, da zum Aushandeln von HTTP/2 TLS erforderlich ist. Ohne TLS wird für alle Verbindungen zu diesem Endpunkt standardmäßig HTTP/1.1 festgelegt, und gRPC-Aufrufe schlagen fehl.

Der gRPC-Client muss ebenfalls so konfiguriert sein, dass er kein TLS verwendet. Weitere Informationen finden Sie im Abschnitt Aufrufen unsicherer gRPC-Dienste mit dem .NET Core-Client.

Warnung

HTTP/2 ohne TLS sollte nur während der App-Entwicklung verwendet werden. Produktions-Apps sollten stets Transportsicherheit verwenden. Weitere Informationen finden Sie unter Sicherheitsüberlegungen zu gRPC für ASP.NET Core.

Kein Generieren von Code aus .proto-Dateien bei gRPC-C#-Objekten

Für das Generieren von gRPC-Code für konkrete Clients und Dienstbasisklassen muss in einem Projekt auf Protobuf-Dateien und -Tools verwiesen werden. Folgendes muss enthalten sein:

  • .proto-Dateien, die Sie in der Elementgruppe <Protobuf> verwenden möchten. Auf importierte .proto-Dateien muss im Projekt verwiesen werden.
  • Paketverweis auf das gRPC-Toolpaket Grpc.Tools

Weitere Informationen zum Generieren von gRPC-C#-Ressourcen finden Sie unter gRPC-Dienste mit C#.

Bei einer ASP.NET Core-Web-App, die gRPC-Dienste hostet, muss nur die Dienstbasisklasse generiert werden:

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

Bei einer gRPC-Client-App, die gRPC-Aufrufe ausführt, muss nur der konkrete Client generiert werden:

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

Generieren von gRPC-C#-Objekten aus .proto-Dateien in WPF-Projekten nicht möglich

Es gibt ein bekanntes Problem bei WPF-Projekten, das verhindert, dass das Generieren von gRPC-Code richtig funktioniert. Bei der Verwendung aller gRPC-Typen, die in einem WPF-Projekt durch Verweise auf Grpc.Tools und .proto-Dateien generiert werden, treten Kompilierungsfehler auf:

Fehler CS0246: Der Typ- oder Namespacename „MyGrpcServices“ wurde nicht gefunden (möglicherweise fehlt eine using-Direktive oder ein Assemblyverweis).

So umgehen Sie dieses Problem:

  1. Erstellen Sie ein neues .NET Core-Klassenbibliotheksprojekt.
  2. Fügen Sie im neuen Projekt Verweise hinzu, um das Generieren von C#-Code aus .proto-Dateien zu ermöglichen:
  3. Fügen Sie in der WPF-Anwendung einen Verweis auf das neue Projekt hinzu.

Die WPF-Anwendung kann die in gRPC generierten Typen aus dem neuen Klassenbibliotheksprojekt verwenden.

Aufrufen von in einem Unterverzeichnis gehosteten gRPC-Diensten

Warnung

Viele gRPC-Tools von Drittanbietern unterstützen in Unterverzeichnissen gehostete Dienste nicht. Ziehen Sie in Betracht, eine Möglichkeit zum Hosten von gRPC als Stammverzeichnis zu finden.

Die Pfadkomponente der Adresse eines gRPC-Kanals wird beim Ausführen von gRPC-Aufrufen ignoriert. Beispielsweise verwendet GrpcChannel.ForAddress("https://localhost:5001/ignored_path") nicht ignored_path, wenn gRPC-Aufrufe für den Dienst weitergeleitet werden.

Der Adresspfad wird ignoriert, da gRPC eine standardisierte, vorgeschriebene Adressstruktur besitzt. Eine gRPC-Adresse kombiniert die Paket-, Dienst- und Methodennamen: https://localhost:5001/PackageName.ServiceName/MethodName.

Es gibt einige Szenarien, in denen eine App einen Pfad mit gRPC-Aufrufen einschließen muss. Wenn beispielsweise eine ASP.NET Core gRPC-App in einem IIS-Verzeichnis gehostet wird und das Verzeichnis in die Anforderung aufgenommen werden muss. Wenn ein Pfad erforderlich ist, kann er dem gRPC-Aufruf mithilfe des unten angegebenen benutzerdefinierten SubdirectoryHandler hinzugefügt werden:

/// <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 wird verwendet, wenn der gRPC-Kanal erstellt wird.

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

Der vorangehende Code:

  • Erstellt einen SubdirectoryHandler mit dem Pfad /MyApp.
  • Konfiguriert einen Kanal für die Verwendung von SubdirectoryHandler.
  • Ruft den gRPC-Dienst mit SayHelloAsync auf. Der gRPC-Aufruf wird an https://localhost:5001/MyApp/greet.Greeter/SayHello gesendet.

Alternativ kann eine Clientfactory durch Verwendung von AddHttpMessageHandler mit SubdirectoryHandler konfiguriert werden.

gRPC-Aufruftimeout von HttpClient.Timeout

HttpClient ist standardmäßig mit einem Timeout von 100 Sekunden konfiguriert. Wenn ein GrpcChannel für die Verwendung eines HttpClient konfiguriert ist, werden zeitintensive gRPC-Streamingaufrufe abgebrochen, falls sie nicht innerhalb des Timeouts abgeschlossen werden.

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

Es gibt mehrere Möglichkeiten, diesen Fehler zu beheben. Der erste besteht darin, HttpClient.Timeout mit einem größeren Wert zu konfigurieren. Timeout.InfiniteTimeSpan deaktiviert das Timeout:

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

Alternativ können Sie das Erstellen von HttpClient vermeiden und stattdessen GrpcChannel.HttpHandler festlegen:

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