Использование gRPC в приложениях на основе браузера

Автор: Джеймс Ньютон-Кинг (James Newton-King)

Узнайте, как настроить существующую службу ASP.NET Core gRPC для вызовов из приложений браузера с использованием протокола gRPC-Web. gRPC-Web позволяет приложениям JavaScript и Blazor на основе браузера вызывать службы gRPC. Вызвать службу HTTP/2 gRPC из приложения на основе браузера нельзя. Службы gRPC, размещенные в ASP.NET Core, можно настроить на поддержку gRPC-Web вместе с HTTP/2 gRPC.

См. раздел Добавление служб gRPC в приложение ASP.NET Core.

Инструкции по созданию проекта gRPC см. здесь: Создание клиента и сервера gRPC .NET Core в ASP.NET Core.

gRPC-Web в ASP.NET Core или Envoy

Существует два способа добавления gRPC-Web в приложение ASP.NET Core.

  • Поддержка gRPC-Web вместе с gRPC HTTP/2 в ASP.NET Core. Этот параметр использует ПО промежуточного слоя, предоставленное пакетом Grpc.AspNetCore.Web.
  • Используйте поддержку gRPC-Web в прокси-сервере Envoy, чтобы транслировать gRPC-Web в gRPC HTTP/2. Затем переведенный вызов перенаправляется в приложение ASP.NET Core.

Есть свои плюсы и минусы этого подхода. Если среда приложения уже использует Envoy в качестве прокси-сервера, имеет смысл использовать Envoy и для предоставления поддержки gRPC-Web. Если требуется простое решение для gRPC-Web, для которого требуется только ASP.NET Core, используйте Grpc.AspNetCore.Web.

Настройка gRPC-Web в ASP.NET Core

Службы gRPC, размещенные в ASP.NET Core, можно настроить на поддержку gRPC-Web вместе с HTTP/2 gRPC. gRPC-Web не требует вносить изменения в службы. Единственного изменения потребует конфигурация запуска.

Чтобы включить gRPC-Web со службой gRPC ASP.NET Core, выполните следующие действия.

  • Добавьте ссылку на пакет Grpc.AspNetCore.Web.
  • Настройте приложение на использование gRPC-Web, добавив UseGrpcWeb и EnableGrpcWeb в файл 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();
    });
}

Предыдущий код:

  • Добавляет ПО промежуточного слоя gRPC-Web (UseGrpcWeb) после маршрутизации и перед конечными точками.
  • Указывает метод endpoints.MapGrpcService<GreeterService>() с поддержкой gRPC-Web с EnableGrpcWeb.

Также можно настроить ПО промежуточного слоя gRPC-Web, чтобы все службы поддерживали gRPC-Web по умолчанию и не нужно было использовать EnableGrpcWeb. Укажите new GrpcWebOptions { DefaultEnabled = true } при добавлении ПО промежуточного слоя.

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

Примечание

Существует известная ошибка, приводящая к сбою gRPC-Web при размещении с помощью HTTP.sys в .NET Core 3.x.

Временное решение для запуска gRPC-Web на HTTP. sys доступно здесь.

gRPC-Web и CORS

Система безопасности браузера предотвращает запросы веб-страницы к другому домену, отличному от того, который обслуживает веб-страницу. Это ограничение применяется к вызовам gRPC-Web с приложениями браузера. Например, приложение браузера, обслуживаемое https://www.contoso.com, блокирует вызов служб gRPC-Web, размещенных на https://services.contoso.com. Можно использовать общий доступ к ресурсам независимо от источника (CORS), чтобы ослабить это ограничение.

Чтобы разрешить приложению браузера вызывать gRPC-Web независимо от источника, настройте CORS в ASP.NET Core. Используйте встроенную поддержку CORS и предоставьте заголовки, относящиеся к gRPC, с помощью 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");
    });
}

Предыдущий код:

  • Вызывает AddCors для добавления служб CORS и настраивает политику CORS, которая предоставляет заголовки, относящиеся к gRPC.
  • Вызывает UseCors для добавления ПО промежуточного слоя CORS после маршрутизации и перед конечными точками.
  • Указывает метод endpoints.MapGrpcService<GreeterService>() с поддержкой CORS с RequiresCors.

gRPC-Web и потоковая передача

Традиционный API gRPC по HTTP/2 поддерживает потоковую передачу во всех направлениях. gRPC-Web имеет ограниченную поддержку потоковой передачи:

  • Клиенты браузера с gRPC-Web не поддерживают вызов методов потоковой передачи клиента и двунаправленной потоковой передачи.
  • Службы gRPC в ASP.NET Core, размещенные в Службе приложений Azure и IIS, не поддерживают двунаправленную потоковую передачу.

При использовании gRPC-Web мы рекомендуем применять только унарные методы и методы серверной потоковой передачи.

Вызов gRPC-Web из браузера

Приложения браузера могут использовать gRPC-Web для вызова служб gRPC. При вызове служб gRPC с помощью gRPC-Web из браузера существует ряд требований и ограничений.

  • Сервер должен быть настроен для поддержки gRPC-Web.
  • Потоковая передача клиента и вызовы двунаправленной потоковой передачи не поддерживаются. Потоковая передача сервера поддерживается.
  • Для вызова служб gRPC в другом домене требуется настроить CORS на сервере.

Клиент gRPC-Web JavaScript

Существует клиент gRPC-Web JavaScript. Инструкции по использованию gRPC-Web из JavaScript см. в статье, посвященной написанию кода клиента JavaScript с gRPC-Web.

Настройка gRPC-Web с помощью клиента .NET gRPC

Клиент .NET gRPC можно настроить для выполнения вызовов gRPC-Web. Это полезно для приложений Blazor WebAssembly, которые размещаются в браузере и имеют те же ограничения HTTP, что и код JavaScript. Вызов gRPC-Web с помощью клиента .NET выполняется так же, как и HTTP/2 gRPC. Единственным изменением является то, как создается канал.

Чтобы использовать gRPC-Web:

  • Добавьте ссылку на пакет Grpc.Net.Client.Web.
  • Убедитесь, что используется ссылка на пакет Grpc.Net.Client версии 2.29.0 или более поздней.
  • Настройте канал на использование 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" });

Предыдущий код:

  • Настраивает канал для использования gRPC-Web.
  • Создает клиент и выполняет вызов с помощью канала.

GrpcWebHandler имеет следующие параметры конфигурации.

  • InnerHandler: базовый HttpMessageHandler, который выполняет HTTP-запрос gRPC, например HttpClientHandler.
  • GrpcWebMode: Тип перечисления, указывающий, является ли HTTP-запрос gRPC Content-Type application/grpc-web или application/grpc-web-text.
    • GrpcWebMode.GrpcWeb настраивает содержимое для отправки без кодировки. Значение по умолчанию.
    • GrpcWebMode.GrpcWebText настраивает содержимое в кодировке Base64. Требуется для вызовов потоковой передачи сервера в браузерах.
  • HttpVersion: Version протокола HTTP, используемая для задания HttpRequestMessage.Version в базовом HTTP-запросе gRPC. gRPC-Web не требует определенной версии и не переопределяет значение по умолчанию, если не указано иное.

Важно!

Созданные клиенты gRPC имеют синхронные и асинхронные методы для вызова унарных методов. Например, SayHello является синхронным, а SayHelloAsync — асинхронным. Вызов синхронного метода в приложении Blazor WebAssembly приведет к тому, что приложение перестанет отвечать на запросы. В Blazor WebAssembly всегда следует использовать асинхронные методы.

Использование фабрики клиента gRPC с gRPC-Web

Клиент .NET, совместимый с gRPC-Web, можно создать с помощью фабрики клиента gRPC.

Чтобы использовать gRPC-Web с фабрикой клиента, выполните следующие действия.

  • Добавьте в файл проекта следующие ссылки на пакеты:
  • Зарегистрируйте клиент gRPC с внедрением зависимостей (DI) с помощью универсального метода расширения AddGrpcClient. В приложении Blazor WebAssembly службы регистрируются с внедрением зависимостей в Program.cs.
  • Настройте GrpcWebHandler с помощью метода расширения ConfigurePrimaryHttpMessageHandler.
builder.Services
    .AddGrpcClient<Greet.GreeterClient>(options =>
    {
        options.Address = new Uri("https://localhost:5001");
    })
    .ConfigurePrimaryHttpMessageHandler(
        () => new GrpcWebHandler(new HttpClientHandler()));

Для получения дополнительной информации см. Интеграция фабрики клиента gRPC в .NET.

Дополнительные ресурсы