gRPC for ASP.NET Core 3.0
gRPC is a high performance RPC framework. A faster and more efficient alternative to JSON based REST services. gRPC uses HTTP/2 protocol and by default Google's protocol buffer binary serialisation format to transfer messages. This is mature and a must technology in cloud native products. I will spare you from the introduction as I know you are here to quickly start gRPC using ASP.NET Core.
I have recently upgraded a gRPC service at work (that was using .NET Core 2.2) into .NET Core 3.0 Preview 7. It was such a smooth migration. gRPC in .NET Core 3.0 has first class support by the framework just like using a MVC Controller, SignalR Hub or Razor page.
Starting from ASP.NET Core 3 gRPC has first class support by the framework. It is supported into the framework starting from Kestrel up to the new Endpoint routing. You no longer need to use a separate binary to generate code. It's as simple as building the project. An ASP.NET application can expose gRPC, MVC, WebApi, SignalR Hub endpoint etc all at the same time. It integrates seamlessly into Endpoint routing, built-in IoC container and generated logs are just like any other logs - all out of the box.
A gRPC service has 3 elements. A server, bunch of clients and a .proto
file. If you are from JSON based service background then you know what I mean by server and client. What's new here is the proto file.
Protocol buffer file (protobuf) - The Contract
.proto
file contains the scheme of your service endpoints and models (in protobuf's term - message). For our example we will need two RPC endpoints
SayHello
that takes a name and returns astring
SayHelloToNobody
that does not take any parameter and returns astring
Here is the proto file
If you can't see the code above switch to non-amp version of this post.
Each RPC required to take one message. Therefore we could add one or many properties in the HelloRequest
message and assign an unique number for each property. Since protobuf is designed for speed and efficiency in mind, it only sends the number over the wire in order to identify a property.
You can read more about protobuf files in the Language Guide for proto3 format.
The proto file is then used by gRPC compilers (available for many popular language) to generate code for a Client and a Server. In order to do that, we need to put the proto
file into a shared place to be accessed by both the client and server.
In a non-trivial C# project, you would create a NuGet package that would contain the generated code for Client and Server. For simplicity, we are going to put the .proto
files and generated code into a separate c# project and reference it from both client and server projects.
Here is a netstandard2.0
project that references required NuGet packages and the proto file defined above.
Notice the GrpcServices="Both"
(instead of Client
or Server
- we've asked for Both
). That's why once you build the project - it will generate code for both the client (for consumption) and server (for implementation). Also, by targeting netstandard2.0
we have enabled wider compatibility from entire .NET ecosystem.
Server
The generated code defines the contract defined in the .proto
file into programming language (in our case C#). We need to define the implementation. So we have a ASP.NET Core app that references the Protos.csproj
.
Here is the Program
(the entry-point of a c# app), Startup
(convention based bootstrapping an ASP.NET Web/API) and an implementation of our DemoService
(defined in the DemoService.proto
above)
Once started, the gRPC service will be up in http://localhost:5000
endpoint. You can use any gRPC client like grpcurl CLI or BloomRPC GUI to test the server.
The server automatically generates logs message like this
/usr/local/share/dotnet/dotnet /Users/mustakim/dev/grpc-dotnet-post/Server/bin/Debug/netcoreapp3.0/Server.dll
warn: Microsoft.AspNetCore.Server.Kestrel[0]
Overriding address(es) 'https://localhost:5001, http://localhost:5000'. Binding to endpoints defined in UseKestrel() instead.
info: Microsoft.Hosting.Lifetime[0]
Now listening on: http://0.0.0.0:5000
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
Content root path: /Users/mustakim/dev/grpc-dotnet-post/Server
info: Microsoft.AspNetCore.Hosting.Diagnostics[1]
Request starting HTTP/2 POST http://localhost:5000/GrpcDotNetDemoPackage.DemoService/SayHello application/grpc
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
Executing endpoint 'gRPC - /GrpcDotNetDemoPackage.DemoService/SayHello'
info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint 'gRPC - /GrpcDotNetDemoPackage.DemoService/SayHello'
info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 60.1197ms 200 application/grpc
Client
You are not limited to use a C# client. gRPC gives you the freedom to generate code in many popular programming language like Go, Java etc. All you need is the same proto file and the address of the server (http://localhost:5000) in our case. Here is a c# client that uses a HttpClient
and and extension methods defined in Grpc.Net.Client
nuget package to create a client using the generated client code in our Proto.csproj
project.
Running the client (while the serve is running - of course) works as expected.
Other ways of getting the gRPC client
If the client was an ASP.NET web application (or a console application bootstrapped using .NET Generic Host), you could use IHttpClientFactory to get a HttpClient (instead of new
ing up) - which would be reused for improved performance.
Even better you can use IServiceCollection.AddGrpcClient
extension method like this (need Grpc.Net.ClientFactory
nuget package)
Then you can just request the IoC to inject DemoService.DemoServiceClient
whenever you need to consume the gRPC service.
Securing your gRPC server
You would never deploy a gRPC server (or any service) without securing it with TLS, so here is a quick overview of what needs to be done.
- In Server: Pass the server certificate (in
pfx
form) to kestrel inProgram.cs
macOS does not support TLS over HTTP/2. So you will get an error if you try this. Alternatively you can use Docker.
- The client need to specify the
https://...
endpoint and optionally pass theca
file (if the server certificate is self-signed or signed using your own certification authority).
Go clone yourself
You can find all of these source code in github
https://github.com/mustakimali/grpc-dotnet-demo