Protobuf Web API in C#
Using Google Protocol Buffers (protobuf) for serialization over a Web API (or REST API) is simple. Most developers use JSON as the go-to transfer protocol for services even though it is needlessly verbose, slow to serialize, and lacks the kind of functionality that Google added to protobuf. This article briefly talks about the advantages of services with protobuf, when to use them, and how to create and consume them with ASP.NET Core and RestClient.Net.
Advantages
Protocol buffers are Google’s language-neutral, platform-neutral, extensible mechanism for serializing structured data.
If you’re reading this, I hope I don’t have to convince you about the performance and bandwidth saving benefits of binary serialization over JSON. There is a myriad of articles that compare the performance of JSON and protobuf. Here is one for good measure. I think I would be flogging a dead horse if I talked about the performance advantages, but what other advantages are there?
Protobuf allows developers to define message type in a programming language agnostic markup language called .proto files. It means payload model objects generate automatically on both server and client-side, which saves time. It also makes up for the lack of human readability by guaranteeing that messages are compatible between client and server as long as the .proto is the same on both sides. Protobuf is the backbone of gRPC, so it’s safe to assume that Google’s investment in protobuf is for the long-run. Protobuf has other features like Reflection, which make it easy to inspect the structure of messages within code and Any message types that allow any known message type exchange.
When should I use Protobuf?
Developers should use protobuf as often as possible. There are other kinds of serialization out there, but there are few with the full weight of Google behind them. Protobuf is bound to shake up the world of services, and it is quickly becoming the most recommended serialization method after JSON. Even Microsoft is getting behind it.
Protobuf may not be appropriate when the audience reach is the most critical factor. Web developers are used to consuming JSON services and may not know how to generate code for protobuf serialization. However, things are changing. When low latency and efficiency are the focus, Protobuf is the obvious choice, so consider implementing services with protobuf wherever possible. Who wouldn’t want to use less bandwidth and make apps snappier?
Creating the Service
It is possible to create a gRPC service on ASP.Net Core. Here is an earlier article I wrote on this. But, developers may want to implement a standard ASP.NET Core Web API with protobuf serialization. Firstly, create a Web API project and add the gRPC NuGet packages. The samples can be cloned from this repo.
These NuGets allow defining message types with proto files. Just add a .proto file and change the properties of the file as below. It is possible to put the .proto file in a project that is shared between client and server. This is not always possible, but it’s recommended. Otherwise, the outputted C# code can be copied and pasted into the client-side project. This article may be useful for managing versions between the back-end and front-end.
Here is a controller that receives protobuf data via PUT and returns protobuf data via GET. Person is a message type. It can be downloaded with the RestClient.Net samples. The unit tests directly consume the service. ToByteArray converts the message into a byte array to be transferred across the wire.
[HttpGet]
public IActionResult Get()
{
var person = new Person
{
FirstName = "Sam",
BillingAddress = new Address
{
StreeNumber = "100",
Street = "Somewhere",
Suburb = "Sometown"
},
Surname = "Smith"
};
var data = person.ToByteArray();
return File(data, "application/octet-stream");
}
[HttpPut]
public async Task<IActionResult> Put()
{
var stream = Request.BodyReader.AsStream();
var person = Person.Parser.ParseFrom(stream);
if (!Request.Headers.ContainsKey("PersonKey")) throw new Exception("No key");
person.PersonKey = Request.Headers["PersonKey"];
var data = person.ToByteArray();
return File(data, "application/octet-stream");
}
Consuming the Service
The service can be consumed from any .NET application with RestClient.Net. Firstly, add the RestClient.Net NuGet package and the same gRPC / protobuf packages. This will work on .NET Framework, .NET Core, UWP, and Xamarin projects. Documentation can be found here. Here is an example from the RestClient.Net .NET Core sample. It posts a person to the server in protobuf binary and then retrieves that person back. More examples can be found in the Unit Tests.
var person = new Person { FirstName = "Bob", Surname = "Smith" };
var client = new Client(new ProtobufSerializationAdapter(), new Uri("http://localhost:42908/person"));
Console.WriteLine($"Sending a POST with body of person {person.FirstName} {person.Surname} serialized to binary with Google Protobuffers");
person = await client.PostAsync<Person, Person>(person);
Wrap Up
Feel free to take this sample and modify it to build your service. You can modify the .proto file to add, modify, or remove message types using this reference. ASP.NET Core is a great base for serving up services with protobuf and C#. If you’ve got difficulties, feel free to reach out on the Github issues section.