ASP.NET Core gRPC-Web

The architectural constraints of modern web browsers create a significant barrier for high-performance communication protocols. Specifically, the inability of browser-based applications to initiate raw HTTP/2 calls—which are mandatory for standard gRPC—necessitates a specialized translation layer. ASP.NET Core gRPC-Web serves as this critical bridge, enabling JavaScript and Blazor applications to interact with gRPC services by leveraging a protocol that is compatible with browser environments. By integrating gRPC-Web, developers can maintain the strong typing and contract-first nature of Protocol Buffers while overcoming the networking limitations inherent in the client-side web ecosystem.

Architectural Paradigms for gRPC-Web Implementation

When integrating gRPC-Web into an ASP.NET Core ecosystem, there are two distinct architectural paths. Each path determines where the translation from the gRPC-Web protocol to the standard gRPC HTTP/2 protocol occurs.

Native ASP.NET Core Middleware Approach

The first option involves the use of the Grpc.AspNetCore.Web package. This method implements the translation logic directly within the ASP.NET Core request pipeline via middleware.

  • Direct Fact: This approach utilizes the Grpc.AspNetCore.Web package to provide native support.
  • Impact Layer: For the developer, this means the application remains self-contained. There is no need to deploy, manage, or configure external proxy servers, which reduces the operational overhead and simplifies the deployment pipeline.
  • Contextual Layer: This is categorized as a basic solution that only requires the ASP.NET Core runtime, making it ideal for projects that do not already have a complex infrastructure involving sidecars or API gateways.

Envoy Proxy Translation Approach

The second option utilizes the Envoy proxy to handle the gRPC-Web translation before the request ever reaches the ASP.NET Core application.

  • Direct Fact: Envoy acts as a proxy that translates gRPC-Web calls into standard gRPC HTTP/2 calls.
  • Impact Layer: This offloads the translation CPU cycles to the proxy layer, which can be scaled independently of the application logic. It is highly efficient in microservices architectures where Envoy is already used as a service mesh or ingress controller.
  • Contextual Layer: While the native middleware is simpler for small apps, Envoy is the preferred choice for enterprise-grade environments already utilizing proxy-based architectures.

Technical Configuration and Implementation

Implementing gRPC-Web in ASP.NET Core does not require any modifications to the actual service logic. The transformation is handled entirely within the startup configuration and the middleware pipeline.

Package Dependencies

To begin the implementation, the project must include the necessary NuGet package:

  • Grpc.AspNetCore.Web

Middleware Configuration in Startup.cs

For applications utilizing the older Startup.cs pattern, the configuration is split between service registration and the request pipeline.

  • Direct Fact: The UseGrpcWeb middleware must be placed specifically after UseRouting and before UseEndpoints.
  • Impact Layer: Incorrect placement of this middleware will result in the application failing to intercept the gRPC-Web requests, leading to 404 or 405 errors when browser clients attempt to call the service.
  • Contextual Layer: The order of middleware in ASP.NET Core is critical because it defines how the HTTP request is processed sequentially.

The implementation follows this structure:

```csharp
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc();
}

public void Configure(IApplicationBuilder app)
{
app.UseRouting();
app.UseGrpcWeb();
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService().EnableGrpcWeb();
});
}
```

Middleware Configuration in Program.cs (Minimal APIs)

In modern .NET versions using the Program.cs minimal hosting model, the setup is streamlined.

  • Direct Fact: The UseGrpcWeb method is called on the app instance, and EnableGrpcWeb is chained to the service mapping.
  • Impact Layer: This reduces boilerplate code and provides a more readable configuration for the developer.

The implementation follows this structure:

csharp using GrpcGreeter.Services; var builder = WebApplication.CreateBuilder(args); builder.Services.AddGrpc(); var app = builder.Build(); app.UseGrpcWeb(); app.MapGrpcService<GreeterService>().EnableGrpcWeb(); app.MapGet("/", () => "This gRPC service is gRPC-Web enabled and is callable from browser apps using the gRPC-Web protocol"); app.Run();

Global gRPC-Web Enablement

Developers may find it tedious to call .EnableGrpcWeb() on every individual service mapping. ASP.NET Core provides a mechanism to enable this globally.

  • Direct Fact: By specifying new GrpcWebOptions { DefaultEnabled = true } during the addition of the middleware, all services support gRPC-Web by default.
  • Impact Layer: This eliminates the need for explicit .EnableGrpcWeb() calls on every single service endpoint, reducing the risk of forgetting to enable it for new services.
  • Contextual Layer: This is the most efficient configuration for applications where every gRPC service is intended to be consumed by a web client.

The implementation in Startup.cs looks as follows:

csharp public void Configure(IApplicationBuilder app) { app.UseRouting(); app.UseGrpcWeb(new GrpcWebOptions { DefaultEnabled = true }); app.UseEndpoints(endpoints => { endpoints.MapGrpcService<GreeterService>(); }); }

Client-Side Integration and the Grpc.Net.Client.Web Package

The communication between the browser and the server is facilitated by specific client-side libraries that handle the encoding of gRPC calls into a browser-compatible format.

GrpcWebMode Enumeration

The Grpc.Net.Client.Web package provides the GrpcWebMode enumeration, which controls how the Content-Type header is handled.

Mode Content-Type Description Use Case
GrpcWebMode.GrpcWeb application/grpc-web Content is sent without encoding (Default). Standard unary and server streaming calls.
GrpcWebMode.GrpcWebText application/grpc-web-text Content is base64 encoded. Required for server streaming calls in browsers.
  • Direct Fact: GrpcWebMode.GrpcWeb is the default and sends data without encoding.
  • Impact Layer: Using GrpcWebText increases the payload size due to base64 encoding, which may impact latency and bandwidth consumption.
  • Contextual Layer: The choice of mode is often dictated by the specific requirements of the browser's handling of binary data in streaming scenarios.

Protocol Configuration

The client can further refine the connection using the following options:

  • GrpcChannelOptions.HttpVersion: Used to specify the HTTP protocol version.
  • GrpcChannelOptions.HttpVersionPolicy: Used to define how the version should be handled during the handshake.

Cross-Origin Resource Sharing (CORS) in gRPC-Web

Because gRPC-Web calls are made from the browser, they are subject to the Same-Origin Policy.

  • Direct Fact: A browser app served from https://www.contoso.com cannot call a gRPC-Web service at https://services.contoso.com without explicit permission.
  • Impact Layer: Without a properly configured CORS policy, the browser will block the request before it even reaches the gRPC-Web middleware, resulting in a CORS error in the browser console.
  • Contextual Layer: This security restriction is a standard web browser feature and is not specific to gRPC, but it is a mandatory configuration step for any distributed gRPC-Web architecture.

To resolve this, CORS must be configured within the ASP.NET Core application to allow requests from the specific origins where the client apps are hosted.

Streaming Capabilities and Limitations

One of the most significant departures from standard gRPC (over HTTP/2) is the limitation of streaming modes when using gRPC-Web.

Supported and Unsupported Streaming Modes

Traditional gRPC supports bidirectional streaming, but gRPC-Web is constrained by the browser's request/response model.

  • Direct Fact: gRPC-Web browser clients do not support client streaming or bidirectional streaming.
  • Direct Fact: .NET clients using gRPC-Web over HTTP/1.1 do not support client streaming or bidirectional streaming.
  • Direct Fact: Services hosted on Azure App Service and IIS do not support bidirectional streaming for gRPC-Web.
  • Impact Layer: Developers must design their API contracts to use only unary methods (single request, single response) or server streaming (single request, multiple responses). Any attempt to use client-to-server streaming will fail.
  • Contextual Layer: This makes gRPC-Web less suitable for real-time, two-way communication apps (like chat apps) compared to standard gRPC or WebSockets.

Recommended Usage Patterns

Based on these limitations, the recommended patterns for gRPC-Web are:

  • Unary methods: The most common pattern where a client sends one request and receives one response.
  • Server streaming methods: Where the client sends one request and the server streams back multiple responses.

Compatibility Matrix

The Grpc.Net.Client.Web package supports a wide range of .NET target frameworks.

Target Framework Compatibility Status
net5.0 Compatible
net5.0-windows Compatible
net6.0 Compatible
net6.0-android Compatible
net6.0-ios Compatible
net6.0-maccatalyst Compatible
net6.0-macos Compatible
net6.0-tvos Compatible
net6.0-windows Compatible
net7.0 Compatible
net7.0-android Compatible
net7.0-ios Compatible
net7.0-maccatalyst Compatible
net7.0-macos Compatible

Conclusion

The integration of gRPC-Web into ASP.NET Core represents a strategic compromise between the high-performance requirements of gRPC and the restrictive environment of modern web browsers. By utilizing the Grpc.AspNetCore.Web middleware, developers can expose their services to JavaScript and Blazor clients without altering the core service logic. While the transition from standard gRPC to gRPC-Web introduces certain limitations—most notably the loss of client and bidirectional streaming—it provides a robust, type-safe alternative to REST for web-based frontends. The choice between native middleware and an Envoy proxy depends on the existing infrastructure: native middleware offers simplicity and rapid deployment, whereas Envoy provides scalability and consistency in complex microservices environments. Ultimately, the ability to support both HTTP/2 gRPC and gRPC-Web simultaneously allows a single ASP.NET Core backend to serve both high-performance backend-to-backend traffic and flexible frontend-to-backend communication.

Sources

  1. Learn how to configure an existing ASP.NET Core gRPC service to be callable from browser apps, using the gRPC-Web protocol
  2. Grpc.Net.Client.Web NuGet Package

Related Posts