Its no secret by now that there has been a lot of excitement about gRPC recently in the .Net community. gRPC is a modern high-performance RPC (Remote Procedure Call) framework. It is based on HTTP/2, Protocol Buffers and other modern standard-based technologies. It is also an open standard and is supported by many programming languages, including .NET.
It is currently impossible to implement the gRPC HTTP/2 spec in the browser because there are no browser APIs with enough fine-grained control over requests. Well the good news is that gRPC-Web is here for the rescue!!!
gRPC-Web is a standardized protocol that solves this problem and makes gRPC usable in the browser. gRPC-Web brings many of gRPC’s great features, like small binary messages and contract-first APIs, to modern browser apps. In this post I will demonstrate consuming a gRPC-Web endpoint from within a Blazor WebAssembly application. Along the way I will also demonstrate how gRPC-Web can achieve more efficient payload sizes compared to Restful endpoints.
The source code is hosted on my Github repository which can be found here.
The solution I provided includes three projects. The Blazor WebAssembly project is the front end which will consume both a gRPC-Web endpoint as well as Restful endpoint.
Adding a gRPC-Web Endpoint
In order to add a gRPC-Web endpoint you actually have to start by adding a regular gRPC project.
gRPC services hosted in ASP.NET Core can be configured to support gRPC-Web alongside HTTP/2 gRPC. gRPC-Web does not require any changes to services. The only modification is startup configuration.
To enable gRPC-Web with an ASP.NET Core gRPC service follow the instructions included in the documentation here.
Consuming The gRPC-Web Endpoint From Blazor
Similar to consuming a Restful endpoint, you will need to register the gRPC-Web service in the service container inside the Program.cs file. The service creates a channel with a GrpcWebHandler that is addressed to the backend server.
Note: The GrpcWebMode was set to GrpcWeb. It is recommended to stick with this mode (vs. GrpcWebText) if you are not doing any server streaming calls as it leads to reductions of of the message size upwards of 30%. More information can be found here.
Next I had to copy the Proto files from the Server project into the Blazor project and generate the client stubs (right click on the Proto file and click properties).
Note: You can improve the solution by storing the Proto files in a centralized location to be consumed by both the client and server applications.
Note: You need to install the Grpc.Tools nuget package in order to get the above window. For sake of completion I am sharing all the nuget packages I had to install within the Blazor WebAssembly project to consume the gRPC-Web endpoint.
Now that I have all the settings in place to communicate with a gRPC-Web endpoint its time to wire the Blazor component to consume it. The following code shows the FetchData component consuming a gRPC-Web endpoint.
For comparison, here is the same component consuming a Restful implementation of the same service.
Note: In the case of FetchDataGrpc.razor the WeatherForecast model is automatically generated from the Proto file. gRPC services require a service contract usually defined using the Protocol Buffer Language in Proto files. The service contract is then used to generate your C# (or language of your choice) server-side classes and your client side proxies
Finally, the moment of truth. Does gRPC-Web delivers the promised optimizations compared to Restful endpoints as a result of using a binary format instead of JSON? Well I am happy to share that the answer is a yes. Here is the result of running the application with two components hitting both types of services and returning the same data. As you can see the gRPC-Web endpoint sends 427 Bytes compared to Rest’s 655 Bytes. I imagine the savings will be more pronounced when you send large amounts of data.