Practical ASP.NET
Defining Your First gRPC Service in an ASP.NET Core 3.0 Project
gRPC services promise a lot: better performance, more sophisticated messaging, and a contract-based approach to Web Service development. If those sound good to you, here's how to get started.
In an earlier column, I talked about why you might want to create your Web Services not by following the REST architectural style but, instead, by using gRPC services. There are lots of good reasons for using gRPC services (better performance being the main one) and some caveats (they require that clients use the HTTP/2 protocol) -- read that previous column for the details.
In in this column, I'm going to ignore all those issues to walk you through the process of setting up a gRPC project and/or adding gRPC support to an existing ASP.NET Core 3.0 project. With that out of the way, I'll also show how to define the contract for a simple gRPC service that accepts a customer id and returns the related Customer object. That contract is essential because it generates the base for any Web Service you want to create.
Before you try this out: I'm using .NET Core 3.0 (the release version that dropped on Sept. 23) and Visual Studio 2019 (version 16.3.1).
Setting Up the Project
You can create a gRPC service in Visual Studio 2019 by selecting File | New | Project and selecting the gRPC template from the Create a new project dialog box. Unlike other project types, you'll notice that there is no option to implement HTTPS -- that's because gRPC services always uses HTTPS.
Two last steps: adding/updating the necessary NuGet packages. Right-click on your project, select Manage NuGet Packages and add the gGPRC.Tools package. Second, check the NuGet Manager's Updates tab. If there are any packages listed on that tab, update them.
You can also add gRPC support to an existing ASP.NET Core 3.0 project by making these changes to your project:
- In your Startup.cs file, in the ConfigureServices method, add this line:
services.AddGrpc();
- Still in the Startup.cs file, in the Configure method, update the options that configure UseEndPoints (you should find that the app.UseEndPoints line is already present). You want to call the endpoints parameter's MapGrpcService method, specifying the name of the gRPC service class you intend to create. I'm going to create a service called CustomerService so I'll modify my code to look like this:
app.UseEndpoints(endpoints =>
{
endpoints.MapGrpcService<CustomerService>();
- As long as you're in the EndPoint options, you can add this line after the MapGrpcService line to warn off clients that attempt to access your service without using HTTP/2 (I modified this from the code in Visual Studio's gRPC project template):
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync("Requires HTTP/2");
});
- By default, the gRPC project template keeps all its services in a folder called Services. That's probably a pretty good convention to follow so, in Solution Explorer, right-click on your project, select Add | New Folder and name the folder Services.
- A gRPC service actually has two parts: the code file and a .proto file that describes the service. Again, the gRPC project template keeps those .proto files in a separate folder called Protos, which is also a pretty good idea. Add a new folder called Protos to your project also.
- Finally, add and update your NuGet packages. Right-click on your project, select Manage NuGet packages, find the Grpc.AspNetCore and Grpc.Tools packages and add them both to your project. Check the Updates tab in NuGet Manager and update any packages listed there.
You're now ready to start defining your gRPC service.
Defining a Service
Your first step in defining a gRPC service is to create the .proto file that holds the description of your service. So right-click on the Protos folder you just added, select Add | New Item and, from the Add New Item dialog, select Protocol Buffer File. Give that file the name of the service you specified back in the Configure method (I used CustomerService).
Now you need to update your project file to let it know about your .proto file. In Solution Explorer, double-click on the project line itself to open your project's .csproj file in an editor window. You need to add an ItemGroup element and inside that element, a Protobuf tag. On the tag, you need to set:
- The Include attribute to the relative path to your .proto file (relative from your project's root)
- The GrpcServices attribute to Server (because you're creating a service rather than a client)
For my CustomerService.proto file I added this:
<ItemGroup>
<Protobuf Include="Protos\CustomerService.proto" GrpcServices="Server" />
</ItemGroup>
Now, open your .proto file. By default, you're provided lines that specify what version of the Protobuffer syntax you're using and set the namespace for your service. My sample file looked like this at the start:
syntax = "proto3";
option csharp_namespace = "<project name>.Protos";
Following those lines, you can add your service's specification. In your first line, you specify the name of your service package, which is the name of your project. I called my project CustomerManagement, so this was my package line:
package CustomerManagement;
After your package name, you can describe your service. The first step in that process is to set up a services block with your service's name. My service is called CustomerService so the first lines following the package line look like this:
service CustomerService{
}
Within that service block you specify that service's methods with their request and response messages. The request message specifies what parameters the method must be passed while the response message describes what the method will return.
I'm going to have a method called GetCustomerById whose request message is called CustomerIdRequest (containing just a customer id) and whose response message is called CustomerResponse (which will be a Customer object). Here's the completed service block:
service CustomerService{
rpc GetCustomerById (CustomerIdRequest) returns (CustomerResponse);
}
All that's left is to define those messages. For my CustomerIdRequest, I'm accepting an integer, which is the first field in my request. You must specify each field's datatype (using the data types defined in the Protobuffer specification), the field's name, and its position. Here's my CustomerRequest with its one field in the first position:
message CustomerIdRequest {
int32 custId = 1;
}
In my CustomerResponse message, I'm returning two strings: an integer called CustId as my first field and a string called FullName as my second field. Here's its definition:
message CustomerResponse {
int32 CustId = 1;
string FullName = 2;
}
Creating the Service
You can now check to see if you've done everything right by building your project. To eliminate any errors from the entry you made in your Startup.cs file, right-click on your Services folder and add the class file whose name you used when configuring the project's EndPoints (I used CustomerService).
If all has gone according to plan, when you build your project the tools you added with the Grpc.Tools package will create a base class for services based on the information in your .proto file. You can check that everything worked by having your service class inherit from that base class (that base class will be called <name of your service class>
Base). In my case, that's a class called CustomerServiceBase. Right now that class is generated in the namespace Protos.<name of your service class>, but you can let Visual Studio figure that out for you and generate the appropriate using statement.
The start of my CustomerService class now looks like this (note the static keyword in the using statement for my base class's namespace):
using static CustomerManagement.Protos.CustomerService;
namespace CustomerManagement.Services
{
public class CustomerService: CustomerServiceBase
{
And, at the risk of ending on a cliffhanger, you'll have to come back for my next column where I'll write the code for my service and its client.
About the Author
Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.