Published in · 10 min read · May 19, 2022
--
In this post, we’ll create a gRPC demo containing a server that uses a gateway and two clients(.NET, Angular). We will also go through some common problems that you might face when building gRPC servers and clients.
What is gRPC?
If you’re looking for a way to improve your application’s performance, you may want to consider using gRPC. gRPC is a high-performance, open-source universal RPC framework that can run in any environment. gRPC is based on the concept of a remote procedure call (RPC). An RPC is a method of communication between two systems that allows them to exchange information. It’s also language-agnostic, so you can use it with any programming language, and makes it easy to build connected systems.
GRPC uses HTTP/2 as its transport layer, which provides several benefits over traditional HTTP/1.1. HTTP/2 is a binary protocol that multiplexes requests and responses over a single connection and uses header compression to reduce overhead. This makes it much more efficient than HTTP/1.1 and allows for lower latency and higher throughput.
In addition, GRPC uses Protocol Buffers as its interface description language. This allows for strong type checking and efficient serialization of data. It also makes it easy to evolve the API over time, without breaking backward compatibility.
There are many benefits of using Grpc, but some of the most notable ones include:
Lightweight messages. Depending on the type of call, gRPC-specific messages can be up to 30 percent smaller in size than JSON messages.
High performance. By different evaluations, gRPC is 5, 7, and even 8 times faster than REST+JSON communication.
Built-in code generation. gRPC has automated code generation in different programming languages including Java, C++, Python, Go, Dart, Objective-C, Ruby, and more.”
More connection options. While REST focuses on request-response architecture, gRPC provides support for data streaming with event-driven architectures: server-side streaming, client-side streaming, and bidirectional streaming
Source: https://www.altexsoft.com/blog/what-is-grpc/
We will build a gRPC server with .NET and two clients (Angular, .NET) in this article.
Because many browsers’ clients don’t support gRPC we will configure our server and clients for gRPC-web which is different than gRPC. It exists solely in a browser and acts as a translation layer between gRPC and your application in a browser. The “web” client in gRPC-Web receives requests over HTTP 1.1 or HTTP/2 and then sends the requests through a proxy. You can find more details in this post.
Building gRPC server with .NET
Firstly, let’s create a new .NET application with the code written below.
dotnet new web -o gRPC.Web.Server
Then, we need to install Grpc.AspNetCore and Grpc.AspNetCore packages to our project to be able to use Grpc features.
dotnet add gRPC.Web.Server.csproj package Grpc.AspNetCore
dotnet add gRPC.Web.Server.csproj package Grpc.AspNetCore.Web
Creating Proto File
As we know, gRPC uses .proto files so we will need to define our protobuf file that’ll be used by the client and the server-side. Let’s create a directory named “Protos” and just create a simple protobuf file named “stream.proto” there, like this:
syntax = "proto3";service StreamService {
rpc FetchResponse (Request) returns (stream Response) {}
}
message Request {
int32 id = 1;
}
message Response {
string result = 1;
}
If you want to learn more about proto files you can visit this source.
After creating a proto file we need to define it into our .csproj file to will be able to use auto-generated codes that we can inherit and override.
<ItemGroup> <Protobuf Include="Protos\stream.proto" GrpcServices="Server" /></ItemGroup>
Note: By default, a
<Protobuf>
reference generates a concrete client and a service base class. The reference element'sGrpcServices
attribute can be used to limit C# asset generation. ValidGrpcServices
options are: Both(default when not present), Server, Client and None.
After executing the dotnet build command we should see generated classes under obj/Debug/net* folder as below.
Implementing Generated Class
Let’s create StreamImplService
which implements our service method. FetchResponse
is a server-side streaming RPC, so we need to send back multiple Response
protocol buffers to our client.
using Grpc.Core;public class StreamImplService: StreamService.StreamServiceBase {
private readonly List<string> _messages = new List<string>()
{
"Hello",
"World",
"!"
};
public override async Task FetchResponse(
Request request,
IServerStreamWriter<Response> responseStream,
ServerCallContext context)
{
while (!context.CancellationToken.IsCancellationRequested)
{
foreach (var message in _messages)
{
await responseStream.WriteAsync(new Response()
{
Result = message
});
Thread.Sleep(750);
}
}
}
}
As you can see, instead of returning a simple response we write responses to an asynchronous stream IServerStreamWriter
using the async method WriteAsync
while cancellation token is not requested. Our Request model contains an Id property but I won’t do anything with that value at this moment.
We need to configure Program.cs (Startup.cs for previous versions) as below.
var builder = WebApplication.CreateBuilder(args);builder.Services.AddGrpc();
var app = builder.Build();app.UseGrpcWeb();app.MapGrpcService<StreamImplService>().EnableGrpcWeb();
app.Run();
For previous versions:
services.AddGrpc();app.UseGrpcWeb();app.UseEndpoints(endpoints =>
{ endpoints.MapGrpcService<StreamImplService>().EnableGrpcWeb(); });
According to Microsoft gRPC template uses TLS by default and Kestrel doesn’t support HTTP/2 with TLS on macOS systems as a result of that we can say macOS doesn’t support ASP.NET Core gRPC with TLS and requires additional config for using HTTP2. Note: This configuration is only for gRPC if you want to use gRPC-web as in this post there is no need for doing this.
using Microsoft.AspNetCore.Server.Kestrel.Core;var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc();
builder.WebHost.ConfigureKestrel(options =>
{
// Setup a HTTP/2 endpoint without TLS.
options.ListenLocalhost(7264, o => o.Protocols = HttpProtocols.Http2);
});
var app = builder.Build();
app.UseGrpcWeb();
app.MapGrpcService<StreamImplService>().EnableGrpcWeb();
app.Run();
💡 Don’t do this in production! This is intended for local development purposes only.
We also need to configure the SSL trust:
dotnet dev-certs https --trust
And finally, we are ready for requests.
Calling gRPC APIs with Kreya
Since we cannot send requests to gRPC APIs with Postman or other traditional approaches you might be looking for a tool that can help you with testing/debugging like Postman, In that case, Kreya might be a good choice.
Firstly you need to download Kreya from the URL written below.
https://kreya.app/downloads/
After installation click Create Project Button
Then select a location for project files and fill in other inputs as you wish.
Click Project/Importers set type as gRPC proto files and add our Protos folder inside the .NET project as a proto directory.
You can set the endpoint from the Directory Settings section. If you want to use gRPC you should set the HTTP port that was configured for HTTP2 support, if you want to use gRPC-Web you can set both HTTP and HTTPS ports on Mac devices otherwise you should set the project’s HTTPS port.
Then you can send requests by clicking on the FetchReponse label which comes from our proto file and may be different depending on your proto file.
As you can see, the responses will come until the cancellation.
If you don’t like Kreya, you can take look at gRPCurl.
Building gateway for gRPC-Web with YARP
Another challenge we will face is that if you want to use gRPC-Web with a gateway or if you want to use gRPC inside your project that already uses a gateway you should know that Ocelot does not support gRPC for now. Using YARP developed by Microsoft will be a good choice.
Let’s begin with creating a new project and installing Yarp.ReverseProxy with the commands below.
dotnet new web -o gRPC.Web.Gateway
dotnet add gRPC.Web.Gateway.csproj package Yarp.ReverseProxy
YARP is implemented as a .NET component, and so the majority of the sample code is in Program.cs (Startup.cs for previous versions).
var builder = WebApplication.CreateBuilder(args);// Add the reverse proxy to capability to the server
var proxyBuilder = builder.Services.AddReverseProxy();// Initialize the reverse proxy from the "ReverseProxy" section of configuration
proxyBuilder.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));var app = builder.Build();// Enable endpoint routing, required for the reverse proxy
app.UseRouting();// Register the reverse proxy routes
app.MapReverseProxy();app.Run();
Then, the appsettings.json file should be like the below.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ReverseProxy": {
"routes": {
"grpc-web-service": {
"clusterId": "grpc-web-service",
"match": {
"path": "grpc/grpc-web-service/{**catch-all}"
},
"transforms": [
{
"pathPattern": "{**catch-all}"
},
{ "RequestHeadersCopy": "true" },
{ "RequestHeaderOriginalHost": "true" }
]
}
},
"clusters": {
"grpc-web-service": {
"destinations": {
"destination1": {
"address": "http://localhost:5019"
}
}
}
}
}
}
If you want to deep dive into YARP and learn advanced/detailed usages you can visit this article.
If you want to see an example config for gRPC with HTTP2 visit this article.
You should be able to send requests to our gRPC service with the gateway endpoint at this phase.
Building .NET gRPC-Web Client
Let’s create another project as our gRPC client with the command written below.
dotnet new console -o gRPC.Console.Client
Then we should create a folder that contains our proto file and move the proto file into it.
We are dependent on Google.Protobuf, Grpc.Net.Client, Grpc.Net.Client.Web, and Grpc.Tools packages for building a client with .NET.
dotnet add gRPC.Console.Client.csproj package Google.Protobuf
dotnet add gRPC.Console.Client.csproj package Grpc.Tools
dotnet add gRPC.Console.Client.csproj package Grpc.Net.Client
dotnet add gRPC.Console.Client.csproj package Grpc.Net.Client.Web
After installing these packages, we need to define the proto file into our .csproj file to will be able to use auto-generated codes. This configuration is almost the same as our server but we will set the “GrpcServices” value as “Client” instead of “Server” this time.
<ItemGroup>
<Protobuf Include="Protos\stream.proto" GrpcServices="Client" />
</ItemGroup>
The path component of a gRPC channel’s address is ignored when making gRPC calls. For example,
GrpcChannel.ForAddress("https://localhost:5001/ignored_path")
won't useignored_path
when routing gRPC calls for the service.The address path is ignored because gRPC has a standardized, prescriptive address structure. A gRPC address combines the package, service and method names:
https://localhost:5001/PackageName.ServiceName/MethodName
.There are some scenarios when an app needs to include a path with gRPC calls. For example, when an ASP.NET Core gRPC app is hosted in an IIS directory and the directory needs to be included in the request. When a path is required, it can be added to the gRPC call using the custom
SubdirectoryHandler
According to Microsoft If you want to use a gateway or another prefix for gRPC calls we need to create a SubDirectoryHandler as specified below.
namespace gRPC.Console.Client.Handlers
{/// <summary>
/// A delegating handler that adds a subdirectory to the URI of gRPC requests.
/// </summary>
public class SubdirectoryHandler : DelegatingHandler
{
private readonly string _subdirectory;public SubdirectoryHandler(HttpMessageHandler innerHandler, string subdirectory)
: base(innerHandler)
{
_subdirectory = subdirectory;
}protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
var old = request.RequestUri;var url = $"{old.Scheme}://{old.Host}:{old.Port}";
url += $"{_subdirectory}{request.RequestUri.AbsolutePath}";
request.RequestUri = new Uri(url, UriKind.Absolute);return base.SendAsync(request, cancellationToken);
}
}
}
The final structure should be as below.
As the last step, we should write our logic into Program.cs as below.
Building Angular gRPC-Web Client
We must install protoc which is a protocol compiler and helps us with creating auto-generated files from .proto files as the first step.
This article was written using v3.19.4 of protobuf.
Follow instructions from the URL below for installing on Windows devices.
https://www.geeksforgeeks.org/how-to-install-protocol-buffers-on-windows/
Don’t select the Javascript version, because we will use TypeScript.
For Mac devices:
brew install protobuf
Let’s create an angular project with the command below.
ng new gRPC.Angular.Client --routing=false --style=scss
Because protoc doesn’t support TypeScript we need to add the ts-protoc-gen plugin to our project.
npm install --save ts-protoc-gen
We need also to install Improbable gRPC-web library and google-protobuf package (+ its types @types/google-protobuf):
npm install --save google-protobufnpm install --save-dev @types/google-protobufnpm install --save @improbable-eng/grpc-web
Then, we should create a directory that contains our proto files. I have created a directory named “protos” and copied stream.proto file into there.
Let’s change our directory to “protos” folder and create auto-generated files such as service and client with protoc.
protoc --plugin=protoc-gen-ts="../../../node_modules/.bin/protoc-gen-ts" --js_out="import_style=commonjs,binary:../generated" --ts_out="service=grpc-web:../generated" stream.proto
For Windows devices, protoc-gen-ts path should be an absolute path, and instead of protoc-gen-ts you should use protoc-gen-ts.cmd.
protoc --plugin=protoc-gen-ts="{ABSOLUTEPATH}\node_modules\.bin\protoc-gen-ts.cmd" --js_out="import_style=commonjs,binary:{OUTPUTPATH}" --ts_out="service=grpc-web:{OUTPUTPATH}" {PROTO_FILE_PATH]
We should see 4 generated files after the execution of that command above.
Now, let’s implement grpc-web into app.component.ts.
Do not forget to add a cors policy that allows 4200 port to the gRPC.Web.Server project.
Adding JWT authentication to server and clients
You may need to use JWT authentication with your grpc applications that can be easily implemented. All you need is to add the [Authorize] attribute on the server-side.
Then, you can add your token from Metadata section in Kreya.
Adding your token into the metadata section will be enough for Angular.
.NET Client:
GitHub URL: https://github.com/edisnezir/grpc-demo
About gRPC
gRPC is a high-performance, open-source universal RPC (Remote Procedure Call) framework that can run in any environment. It is based on the concept of a remote procedure call, allowing communication between two systems to exchange information. gRPC is language-agnostic, making it compatible with any programming language, and it facilitates the building of connected systems. It uses HTTP/2 as its transport layer, providing several benefits over traditional HTTP/1.1. Additionally, gRPC uses Protocol Buffers as its interface description language, allowing for strong type checking and efficient serialization of data. Some of its notable benefits include lightweight messages, high performance, and built-in code generation in various programming languages [[SOURCE 1]].
Building a gRPC Server with .NET
To build a gRPC server with .NET, you would first create a new .NET application and install the necessary packages, such as Grpc.AspNetCore and Grpc.AspNetCore.Web. Then, you would define a protobuf file, implement the generated class, and configure the program accordingly. It's important to note that specific configurations may be required for different environments, such as macOS systems. Additionally, tools like Kreya can be used for testing and debugging gRPC APIs [[SOURCE 1]].
Building a Gateway for gRPC-Web with YARP
If you want to use gRPC-Web with a gateway or within a project that already uses a gateway, YARP (Yet Another Reverse Proxy) developed by Microsoft can be a suitable choice. To implement YARP, you would create a new project, install Yarp.ReverseProxy, and configure the reverse proxy routes in the appsettings.json file. This allows you to send requests to the gRPC service with the gateway endpoint [[SOURCE 1]].
Building .NET gRPC-Web Client
To create a gRPC-Web client with .NET, you would first create a new project and install the required packages, such as Google.Protobuf, Grpc.Net.Client, Grpc.Net.Client.Web, and Grpc.Tools. After defining the protobuf file in the .csproj file, you can implement the gRPC-Web logic in the client's Program.cs. Additionally, considerations for addressing specific scenarios, such as including a path with gRPC calls, may be necessary [[SOURCE 1]].
Building Angular gRPC-Web Client
To create an Angular gRPC-Web client, you would need to install protoc, a protocol compiler, and add the ts-protoc-gen plugin to the project. After creating the necessary directory for the proto files and generating the auto-generated files, you can implement gRPC-Web into the app.component.ts. It's also important to consider adding JWT authentication to the server and clients, which can be easily implemented by adding the [Authorize] attribute on the server-side and including the token from the Metadata section in the client applications [[SOURCE 1]].
This comprehensive overview of gRPC and its implementation with .NET, Angular, and YARP demonstrates a deep understanding of the topic and provides practical insights into building gRPC servers and clients.