Abhinandan's Blog
ABHINANDAN ARYAL

anudwigna[at]gmail.com

HOME

A complete walk-through of using gRPC web using .NET Core server and VueJS client

May 18, 2020

C# ASP.NET Core gRPC VueJS

gRPC is a new technology that deals witht the communication between two systems. The official definition gRPC is a modern open source high performance RPC framework that can run in any environment says it all. The communication between two services / systems is challenging due to several aspects. One of them being the different technical stack. In times like these, the RPC frameworks play a pivotal role for communication between these systems. gRPC aims to bring uniformity and consistency in these type of communications using protobuf. A proto file acts as a blueprint for communication between any two systems. In this article, I am going to dicuss gRPC and how it can be used in ASP .NET Core applications. Microsoft has made gRPC a first class citizen for ASP .NET Core applications and has also created a project type for simple creation of gRPC Projects. I will create a gRPC server project and will also create a web client for this server. The client can be system using any technology stack. Here, I will mainly focus on web client and how gRPC can be used instead of REST.

The tools and technology I will be using in this article:

  1. ASP.NET Core 3.1 (gRPC Server)
  2. VueJS (integrated with gRPC Web)
  3. Visual Studio 2019 Community Edition
  4. Visual Studio Code (for client)
  5. Windows 10

Creation of a gRPC Server

Fire up your Visual Studio and create a new project. Search for "gRPC" and you will find a "gRPC Service" template. Use this template and create a project. Here, I have named my project grpc-web-asp-core. After the creation of project, you will find a greet.proto file inside Protos folder and GreetService inside Services folder as shown below:

Project setup for gRPC Web Server

I will not use any of these files. Instead, I will be creating my own proto file along with the service. This will help us understand how the system works. Also, using Visual Studio will help you start the project easily otherwise several dependencies are needed to properly compile the proto files. Here, Microsoft has already embedded those dependencies in the project template and have helped us to getting started easily. You can also verify this by viewing the properties of greet.proto protobuf file.

Proto File Properties

Here, I will be building a simple application which just creates a service to serve name of cities. At first, create a proto file called city.proto. Search for "proto" while adding item to project and you will find "protocol buffer file" template. Add file selecting this template.

Adding a proto file

Now paste the following code in proto file.

syntax = "proto3";

option csharp_namespace = "GrpcWebAspCore.Protos";

package city;

service City {
  rpc GetAllCities (CityRequest) returns (stream CityReply);
  rpc GetCityById (CityRequest) returns (CityReply);
}

message CityRequest {
    int32 id = 1;
    string name = 2;
}

message CityResponse {
    int32 id = 1;
    string name = 2;
}

The proto file is very readable and self explanatory. I have created a service called "City" which contains two methods "GetAllCities" and "GetCityById". Also, I have created Request Type of "CityRequest" and Response Type of "CityResponse". The fields inside these types consist of "id" and "name" which are again of scalar type supported by protobuf. Read this article to see what's supported by protobuf. Also there is numbering for each field. This is important in protobuf definition and it should be unique for eah field. The method "GetAllCities" is returning a stream of "CityRequest". This also demonstrates that how easy it is to send stream of data using gRPC.

Only adding the protobuf file is not enough for compilation of the file, also modify the project file and add the proto file information there to properly compile these files then only a service class is auto generated by Visual Studio and we need to inherit that class so that we can define our service logic. If proto file is not properly setup, then this auto generation process is not done.

The code to add to project file is:

<Protobuf Include="Protos\greet.proto" GrpcServices="Server" />

Now, add a service file inside the Services folder, name it "CityService" and paste the following code:

using Grpc.Core;
using GrpcWebAspCore.Protos;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace GrpcWebAspCore.Services
{
    public class CityService : City.CityBase
    {
        private readonly ILogger<CityService> _logger;
        List<CityResponse> _theList;
        public CityService(ILogger<CityService> logger)
        {
            _logger = logger;
            _theList = new List<CityResponse>()
            {
                new CityResponse(){ Id = 1, Name = "Cat"},
                new CityResponse(){ Id = 2, Name = "Dog"},
                new CityResponse(){ Id = 3, Name = "Mouse"},
                new CityResponse(){ Id = 4, Name = "Cow"},
            };
        }

        public override Task<CityResponse> GetCityById(CityRequest request, ServerCallContext context)
        {
            return Task.FromResult(_theList.ElementAt(0));
        }

        public async override Task GetAllCities(CityRequest request, IServerStreamWriter<CityResponse> responseStream, ServerCallContext context)
        {
            int i = 0;
            while (!context.CancellationToken.IsCancellationRequested && i < _theList.Count())
            {
                foreach (var item in _theList)
                {
                    await Task.Delay(1000); //waiting one second for each response

                    _logger.LogInformation("Sending City Data");

                    i++;

                    await responseStream.WriteAsync(item);
                }
            }
        }
    }
}

The inherited "City.CityBase" is an auto generated class. If you are not able to generate this class, then check with your proto file and also don't forget to modify the project file to include the proto file definition.

Now, in order to support web client, add a nuget package called "Grpc.AspNetCore.Web". It's in pre-release (2.28.0-pre2) so don't forget to check "Include prerelease" check mark while searching for the package. Now, modify your "Startup.cs" file and include GrpcWeb services. The final "Startup.cs" file is like:

using GrpcWebAspCore.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace GrpcWebAspCore
{
    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddGrpc();
            services.AddGrpcWeb(o => o.GrpcWebEnabled = true);
            services.AddCors(o => o.AddPolicy("AllowAll", builder =>
            {
                builder.AllowAnyOrigin()
                       .AllowAnyMethod()
                       .AllowAnyHeader()
                       .WithExposedHeaders("Grpc-Status", "Grpc-Message", "Grpc-Encoding", "Grpc-Accept-Encoding");
            }));
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseRouting();
            app.UseGrpcWeb();
            app.UseCors();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGrpcService<GreeterService>();
                endpoints.MapGrpcService<CityService>();

                endpoints.MapGet("/", async context =>
                {
                    await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
                });
            });
        }
    }
}

This concludes the server side setup for gRPC Project.

Creating a Web Client for gRPC Server

If there is a server, then there must be a client to receive the data served by server. In case of gRPC, your client can be of any system with any tools and technology. You can find a list of tools and technology that have already implemented the support for gRPC in official documentation. The client we are going to create for this article is of type "Web". For this, I am going to create a VueJS project and implement a grpc-web client.

I am going to create a VUE project called "grpc_client" using vue-cli. After the creation of project it can be served using the command "yarn serve".

I am using yarn as my package manager

Client Project Creation

Now, you must have a protobuf compiler referenced in your "PATH" variable. Download the compiler here. As I am on windows, I downloaded the zip file for my 64 bit machine. Create a folder inside your C drive called protobuf and unzip the contents of the zip file. Inside you will find a bin folder which contains protoc.exe which is used to compile the proto files. Modify the PATH variable to include C:\protobuf\bin. This will made the compiler available globally.

Note: This process was not done for gRPC server because all of this is handled by Visual Studio for you.

Also download a generator for grpc-web from here and copy the protoc-gen-grpc-web.exe inside the same bin folder we created early.

Now, after all these setups are done, open up your client project with editor of your choice. In my case, it is Visual Studio Code. You will find a folder named "src". Inside this folder create a folder called "protos". Now, copoy the proto file "city.proto" from server application and paste inside the "protos" folder in client application. This is the beauty of gRPC that the same proto file is used by the server and the client. Now, it's time to generate the code for client using the compiler and generator that we setup earlier.

Fire up terminal in VS Code using Ctrl + ~ and paste the following code:

protoc -I=. src/protos/greet.proto --js_out=import_style=commonjs:. --grpc-web_out=import_style=commonjs,mode=grpcwebtext:.

The documentation for this command can be found here.

If your command ran successfully, then you can find two files generated in the same folder where your ".proto" file is. The two files in our case are city_grpc_web_pb.js and city_pb.js. These auto generated files contain all the code required to call our server. Now, it's time to implement them in our client project.

Now, add two packages called "google-protobuf" and "grpc-web" to your client project using the command:

yarn add google-protobuf grpc-web

You can find the code for HelloWorld Component here.

Here, I am importing the required functions from my generated city_grpc_web_pb.js file. The two services we generated in server side "getCityById" and "getAllCities" are now available in client side as well. I am creating a client in my mounted hook and calling those services. Run your app using yarn serve and you can see the results coming back from server.

Note: The server must be running before starting the client app.

The complete code for this sample application can be found here.