I’m excited to announce experimental support for gRPC-Web with .NET. gRPC-Web allows gRPC to be called from browser-based apps like JavaScript SPAs or Blazor WebAssembly apps.
gRPC-Web for .NET promises to bring many of gRPC’s great features to browser apps:
- Strongly-typed code-generated clients
- Compact Protobuf messages
- Server streaming
What is gRPC-Web
It is impossible to implement the gRPC HTTP/2 spec in the browser because there is no browser API with enough fine-grained control over HTTP requests. gRPC-Web solves this problem by being compatible with HTTP/1.1 and HTTP/2.
gRPC-Web is not a new technology. There is a stable gRPC-Web JavaScript client, and a proxy for translating between gRPC and gRPC-Web for services. The new experimental packages allow an ASP.NET Core gRPC app to support gRPC-Web without a proxy, and allow the .NET Core gRPC client to call gRPC-Web services. (great for Blazor WebAssembly apps!)
New opportunites with gRPC-Web
- Call ASP.NET Core gRPC apps from the browser – Browser APIs can’t call gRPC HTTP/2. gRPC-Web offers a compatible alternative.
- JavaScript SPAs
- .NET Blazor Web Assembly apps
- Host ASP.NET Core gRPC apps in IIS and Azure App Service – Some servers, such as IIS and Azure App Service, currently can’t host gRPC services. While this is actively being worked on, gRPC-Web offers an interesting alternative that works in every environment today.
- Call gRPC from non-.NET Core platforms – Some .NET platforms
HttpClientdoesn’t support HTTP/2. gRPC-Web can be used to call gRPC services on these platforms (e.g. Blazor WebAssembly, Xamarin).
Note that there is a small performance cost to gRPC-Web, and two gRPC features are no longer supported: client streaming and bi-directional streaming. (server streaming is still supported!)
Server gRPC-Web instructions
If you are new to gRPC in .NET, there is a simple tutorial to get you started.
gRPC-Web does not require any changes to your services, the only modification is startup configuration. To enable gRPC-Web with an ASP.NET Core gRPC service, add a reference to the Grpc.AspNetCore.Web package. Configure the app to use gRPC-Web by adding AddGrpcWeb(...) and UseGrpcWeb() in the startup file:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddGrpc();
}
public void Configure(IApplicationBuilder app)
{
app.UseRouting();
// Add gRPC-Web middleware after routing and before endpoints
app.UseGrpcWeb();
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<GreeterService>().EnableGrpcWeb();
});
}
Some additional configuration may be required to call gRPC-Web from the browser, such as configuring the app to support CORS.
Client gRPC-Web instructions
The JavaScript gRPC-Web client has instructions for setting up a gRPC-Web client to use in browser JavaScript SPAs.
Calling gRPC-Web with a .NET client is the same as regular gRPC, the only modification is how the channel is created. To enable gRPC-Web, add a reference to the Grpc.Net.Client.Web package. Configure the channel to use the GrpcWebHandler:
// Configure a channel to use gRPC-Web
var handler = new GrpcWebHandler(GrpcWebMode.GrpcWebText, new HttpClientHandler());
var channel = GrpcChannel.ForAddress("https://localhost:5001", new GrpcChannelOptions
{
HttpClient = new HttpClient(handler)
});
var client = Greeter.GreeterClient(channel);
var response = await client.SayHelloAsync(new GreeterRequest { Name = ".NET" });
To see gRPC-Web with .NET in action, take a moment to read a great blog post written by Steve Sanderson that uses gRPC-Web in Blazor WebAssembly.
Try gRPC-Web with ASP.NET Core today
Preview packages are on NuGet:
- Grpc.AspNetCore.Web – Add gRPC-Web support to an ASP.NET Core gRPC service.
- Grpc.Net.Client.Web – Call gRPC-Web endpoints from .NET
Documentation for using gRPC-Web with .NET Core can be found here.
gRPC-Web for .NET is an experimental project, not a committed product. We want to test that our approach to implementing gRPC-Web works, and get feedback on whether this approach is useful to .NET developers compared to the traditional way of setting up gRPC-Web via a proxy. Please add your feedback here or at the https://github.com/grpc/grpc-dotnet to ensure we build something that developers like and are productive with.
Thanks!
James, can you bring us up to date on the status of gRPC-Web and Blazor Client please? Perhaps a new blog as we’re getting closer to Blazor client release?
Thanks!
..Ben
I successfully managed to get a grpc-web service working using Kestrel, but then attempted to convert it to using Http.sys (not under IIS), but I’ve tried and failed to get it working, always getting a “The response headers cannot be modified because the response has already starteed.” invalid operation exception.
Am I completely barking up the wrong tree – does this experiment not support http.sys in this way?
A little frustrated – this is exactly the use case we need it for!
Hi i try to use this from Xamarin forms and it work when i publish the server to azure but i am not able to debug on local dev computer from Android.
i have tested to follow the instruction on thttps://go.microsoft.com/fwlink/?linkid=2099682.
so i add this on the client side.
handlerdev = new HttpClientHandler();
handlerdev.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
string urlDev = "https://10.0.2.2:5001";
...
Is there a sample project for this? In my attempt, the server is returning 200 after a couple of seconds, but the client is reporting a 404 after a few milliseconds. I’m assuming I’ve got something setup wrong, but I’ve been scratching my head over it.
Example project here – https://github.com/grpc/grpc-dotnet/tree/master/examples#blazor
Blazor and Browser both use gRPC-Web.
Managed to get a stripped down version working – https://github.com/ThatRickGuy/WASM-Blazor-gRPC-Sample
Not sure what’s going on with my full version yet, but there appears to be an issue if you call the gRPC service synchronously. The server responds as expected, but the browser never completes the call, it just chews up the CPU and hangs.