gRPC-Web in ASP.NET Core app gRPC

Di James Newton-King

Informazioni su come configurare un ASP.NET Core servizio gRPC esistente da chiamare dalle app del browser usando il protocollo gRPC-Web. gRPC-Web consente ai browser JavaScript e Blazor alle app di chiamare i servizi gRPC. Non è possibile chiamare un servizio HTTP/2 gRPC da un'app basata su browser. I servizi gRPC ospitati in ASP.NET Core possono essere configurati per supportare gRPC-Web insieme a HTTP/2 gRPC.

Per istruzioni sull'aggiunta di un servizio gRPC a un'app ASP.NET Core esistente, vedere Aggiungere servizi gRPC a un'app ASP.NET Core.

Per istruzioni sulla creazione di un progetto gRPC, vedere Creare un client e un server GRPC .NET Core in ASP.NET Core.

ASP.NET Core gRPC-Web rispetto a Envoy

Esistono due opzioni per aggiungere gRPC-Web a un'app ASP.NET Core:

  • Supportare gRPC-Web insieme a gRPC HTTP/2 in ASP.NET Core. Questa opzione usa il Grpc.AspNetCore.Web middleware fornito dal pacchetto.
  • Usare il supporto gRPC-Web del proxy di Envoy per tradurre gRPC-Web in gRPC HTTP/2. La chiamata tradotta viene quindi inoltrata all'app ASP.NET Core.

Esistono vantaggi e svantaggi per ogni approccio. Se l'ambiente di un'app usa già Envoy come proxy, potrebbe essere opportuno usare Anche Envoy per fornire supporto gRPC-Web. Per una soluzione di base per gRPC-Web che richiede solo ASP.NET Core, Grpc.AspNetCore.Web è una buona scelta.

Configurare gRPC-Web in ASP.NET Core

I servizi gRPC ospitati in ASP.NET Core possono essere configurati per supportare gRPC-Web insieme a HTTP/2 gRPC. gRPC-Web non richiede modifiche ai servizi. L'unica modifica è la configurazione di avvio.

Per abilitare gRPC-Web con un servizio gRPC ASP.NET Core:

  • Aggiungere un riferimento al Grpc.AspNetCore.Web pacchetto.
  • Configurare l'app per usare gRPC-Web aggiungendo UseGrpcWeb e EnableGrpcWeb con Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
    services.AddGrpc();
}

public void Configure(IApplicationBuilder app)
{
    app.UseRouting();

    app.UseGrpcWeb(); // Must be added between UseRouting and UseEndpoints

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGrpcService<GreeterService>().EnableGrpcWeb();
    });
}

Il codice precedente:

  • Aggiunge il middleware gRPC-Web, , UseGrpcWebdopo il routing e prima degli endpoint.
  • Specifica che il endpoints.MapGrpcService<GreeterService>() metodo supporta gRPC-Web con EnableGrpcWeb.

In alternativa, il middleware gRPC-Web può essere configurato in modo che tutti i servizi supportino gRPC-Web per impostazione predefinita e EnableGrpcWeb non siano necessari. Specificare new GrpcWebOptions { DefaultEnabled = true } quando viene aggiunto il middleware.

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddGrpc();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseRouting();

        app.UseGrpcWeb(new GrpcWebOptions { DefaultEnabled = true });

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapGrpcService<GreeterService>();
        });
    }
}

Nota

Si verifica un problema noto che causa l'esito negativo di gRPC-Web quando è ospitato da HTTP.sys in .NET Core 3.x.

Una soluzione alternativa per ottenere l'uso di gRPC-Web su HTTP.sys è disponibile in grpc-web sperimentale e UseHttpSys()? (grpc/grpc-dotnet #853).

gRPC-Web e CORS

La sicurezza del browser impedisce a una pagina Web di effettuare richieste a un dominio diverso rispetto a quello che ha servito la pagina Web. Questa restrizione si applica all'esecuzione di chiamate gRPC-Web con app browser. Ad esempio, un'app del browser gestita da https://www.contoso.com viene bloccata chiamando i servizi gRPC-Web ospitati in https://services.contoso.com. La condivisione delle risorse tra origini (CORS) può essere usata per ridurre questa restrizione.

Per consentire a un'app browser di effettuare chiamate gRPC-Web tra origini, configurare CORS in ASP.NET Core. Usare il supporto CORS predefinito ed esporre intestazioni specifiche di gRPC con WithExposedHeaders.

public void ConfigureServices(IServiceCollection services)
{
    services.AddGrpc();

    services.AddCors(o => o.AddPolicy("AllowAll", builder =>
    {
        builder.AllowAnyOrigin()
               .AllowAnyMethod()
               .AllowAnyHeader()
               .WithExposedHeaders("Grpc-Status", "Grpc-Message", "Grpc-Encoding", "Grpc-Accept-Encoding");
    }));
}

public void Configure(IApplicationBuilder app)
{
    app.UseRouting();

    app.UseGrpcWeb();
    app.UseCors();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapGrpcService<GreeterService>().EnableGrpcWeb()
                                                  .RequireCors("AllowAll");
    });
}

Il codice precedente:

  • Chiamate AddCors per aggiungere servizi CORS e configurare un criterio CORS che espone intestazioni specifiche di gRPC.
  • Chiama UseCors per aggiungere il middleware CORS dopo la configurazione di routing e prima della configurazione degli endpoint.
  • Specifica che il endpoints.MapGrpcService<GreeterService>() metodo supporta CORS con RequireCors.

gRPC-Web e streaming

GRPC tradizionale su HTTP/2 supporta lo streaming client, server e bidirezionale. gRPC-Web offre supporto limitato per lo streaming:

  • I client gRPC-Web browser non supportano i metodi di streaming client e di streaming bidirezionale.
  • I client .NET gRPC-Web non supportano i metodi di streaming client e di streaming bidirezionale tramite HTTP/1.1.
  • ASP.NET Core servizi gRPC ospitati in Servizio app di Azure e IIS non supportano lo streaming bidirezionale.

Quando si usa gRPC-Web, è consigliabile usare solo metodi e metodi di streaming server nonry.

Protocollo HTTP

Il modello di servizio gRPC ASP.NET Core incluso in .NET SDK crea un'app configurata solo per HTTP/2. Si tratta di un buon valore predefinito quando un'app supporta solo gRPC tradizionale su HTTP/2. GRPC-Web, tuttavia, funziona con HTTP/1.1 e HTTP/2. Alcune piattaforme, ad esempio UWP o Unity, non possono usare HTTP/2. Per supportare tutte le app client, configurare il server per abilitare HTTP/1.1 e HTTP/2.

Aggiornare il protocollo predefinito in appsettings.json:

{
  "Kestrel": {
    "EndpointDefaults": {
      "Protocols": "Http1AndHttp2"
    }
  }
}

In alternativa, configurare Kestrel gli endpoint nel codice di avvio.

L'abilitazione di HTTP/1.1 e HTTP/2 nella stessa porta richiede TLS per la negoziazione del protocollo. Per altre informazioni, vedere ASP.NET Core negoziazione del protocollo gRPC.

Chiamare gRPC-Web dal browser

Le app del browser possono usare gRPC-Web per chiamare i servizi gRPC. Esistono alcuni requisiti e limitazioni quando si chiamano i servizi gRPC con gRPC-Web dal browser:

  • Il server deve contenere la configurazione per supportare gRPC-Web.
  • Le chiamate di streaming client e streaming bidirezionali non sono supportate. Il flusso del server è supportato.
  • La chiamata di servizi gRPC in un dominio diverso richiede la configurazione CORS nel server.

Client gRPC-Web JavaScript

Esiste un client gRPC-Web JavaScript. Per istruzioni su come usare gRPC-Web da JavaScript, vedere scrivere codice client JavaScript con gRPC-Web.

Configurare gRPC-Web con il client .NET gRPC

Il client .NET gRPC può essere configurato per eseguire chiamate gRPC-Web. Questo è utile per Blazor WebAssembly le app, ospitate nel browser e hanno le stesse limitazioni HTTP del codice JavaScript. La chiamata a gRPC-Web con un client .NET è uguale a HTTP/2 gRPC. L'unica modifica è la modalità di creazione del canale.

Per usare gRPC-Web:

  • Aggiungere un riferimento al Grpc.Net.Client.Web pacchetto.
  • Assicurarsi che il riferimento al Grpc.Net.Client pacchetto sia versione 2.29.0 o successiva.
  • Configurare il canale per l'uso di GrpcWebHandler:
var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions
    {
        HttpHandler = new GrpcWebHandler(new HttpClientHandler())
    });

var client = new Greeter.GreeterClient(channel);
var response = await client.SayHelloAsync(new HelloRequest { Name = ".NET" });

Il codice precedente:

  • Configura un canale per l'uso di gRPC-Web.
  • Crea un client e effettua una chiamata usando il canale.

GrpcWebHandler include le opzioni di configurazione seguenti:

  • InnerHandler: sottostante HttpMessageHandler che effettua la richiesta HTTP gRPC, ad esempio HttpClientHandler.
  • GrpcWebMode: tipo di enumerazione che specifica se la richiesta Content-Type HTTP gRPC è application/grpc-web o application/grpc-web-text.
    • GrpcWebMode.GrpcWeb configura l'invio di contenuto senza codifica. Valore predefinito.
    • GrpcWebMode.GrpcWebText configura il contenuto con codifica base64. Obbligatorio per le chiamate di streaming server nei browser.
  • HttpVersion: protocollo Version HTTP usato per impostare HttpRequestMessage.Version sulla richiesta HTTP gRPC sottostante. gRPC-Web non richiede una versione specifica e non esegue l'override del valore predefinito a meno che non sia specificato.

Importante

I client gRPC generati hanno metodi sincroni e asincroni per chiamare metodi unari. Ad esempio, SayHello è sincrona ed SayHelloAsync è asincrona. I metodi asincroni sono sempre necessari in Blazor WebAssembly. La chiamata a un metodo sincrono in un'app Blazor WebAssembly causa la mancata risposta dell'app.

Usare gRPC client factory con gRPC-Web

Creare un client .NET compatibile con gRPC-Web usando la factory client gRPC:

  • Aggiungere riferimenti al pacchetto al file di progetto per i pacchetti seguenti:
  • Registrare un client gRPC con l'inserimento delle dipendenze usando il metodo di estensione generico AddGrpcClient . In un'app Blazor WebAssembly i servizi vengono registrati con DI in Program.cs.
  • Configurare usando il metodo di ConfigurePrimaryHttpMessageHandler estensioneGrpcWebHandler.
builder.Services
    .AddGrpcClient<Greet.GreeterClient>(options =>
    {
        options.Address = new Uri("https://localhost:5001");
    })
    .ConfigurePrimaryHttpMessageHandler(
        () => new GrpcWebHandler(new HttpClientHandler()));

Per altre informazioni, vedere integrazione della factory client gRPC in .NET.

Risorse aggiuntive