Introduction

Protocol buffers, or Protobuf, are a binary format created by Google to serialize data between different services. Google made this protocol open source and now it provides support, out of the box, to the most common languages, like JavaScript, Java, C#, Ruby and others.

Protocol buffers, usually referred as Protobuf, are a protocol developed by Google to allow serialization and deserialization of structured data. In this article, we will demonstrate to use protocol buffer with Ajax.

 

Define a protocol buffer file

We will be using latest version of protocol buffer (proto3) for this demo. Here, we have created a proto file ‘person.proto’.

 

syntax="proto3";

package personpackage;

message Person {
      string name = 1;
      int32 id = 2;
      string email = 3;
}

We have defined the version of protocol buffers as proto3 through

syntax=proto3;

In given proto file, we have created a Person message with three field: name, id and email.

Creating batch file to compile protocol buffers file

In next step, we will compile ‘person.proto’ into javascript file. Following batch code is used to compile the proto file.

setlocal

set PROTOC=%UserProfile%\.nuget\packages\Google.Protobuf.Tools\3.6.1\tools\windows_x64\protoc.exe

set PersonPath=person.proto

set outputPath=./

%PROTOC% -I.\ --js_out %outputPath% %PersonPath%

PROTOC variable refers to the location of protoc.exe, which is generated when we install Grpc.Tools library through nuget package manager. Similary, PersonPath refers to the path where proto file is located. And outputPath is a path where generated output file is saved. Note that since we want the output in JavaScript output (for Ajax request.), we have used –js_out instead of csharp_out as we used in previous articles. For more details of compiling protobuf file, you can visit this URL .

Project Setup

Create a new ASP.NET Core Web Application with MVC template.

1_Project_Setup

 

Once installed, we will need to install two nuget packages

2_Nuget_Packages

 

For client side, we will need to ensure that following libraries are available.

3_Js_Project_Setup

 

We will save these entire JavaScript file including ‘person.js’ in wwwroot/js path of the project. For simplicity, we will load these entire JavaScript file in _ViewStart.cshtml.

4_Js_Script_Include

 

And lastly, we will be using a protobuf extension formatter to enable protobuf request and response through Web API in project.

In Startup.cs, we will have to enable the extension with addition of AddProtobufFormatters() in ConfigureService.

5_StartupConfiguration

 

 

Decorate classes and members

In order to bind data of proto request sent from Ajax, we will need to decorate a class similar to proto file. We will create a class PersonPoco.cs in Models directory.

 

using System;
using ProtoBuf;

namespace ProtoBinding.Models
{
  [ProtoContract, Serializable]
  public class PersonPoco
  {

    [ProtoMember(2)]
    public int Id { get; set; }

    [ProtoMember(1)]
    public string Name { get; set; }

    [ProtoMember(3)]
    public string Email { get; set; }
  }
}

A class name should be decorated with ProtoContract attribute. It indicates that a type is defined for protocol-buffer serialization.

Similarly, all properties should be decorated with ProtoMember attribute. Note that a tag in its parameter must match with a proto file.

 

syntax="proto3";

package personpackage;

message Person {
      string name = 1;
      int32 id = 2;
      string email = 3;
}

 

 

POST request with Ajax

Let’ s start by creating an API Controller named as ProtoController.

namespace ProtoBinding.Controllers

{

  [ApiController]

  [Route("/api/{controller}/{action}")]

  public class ProtoController : Controller

  {
    [HttpPost]

    public IActionResult PostProto([FromBody] PersonPoco person)
    {
      return Ok(person);
    }
  }
}

Here PostProto method will receive an object of protocol buffer which is deserialized and then wired with PersonPoco class. In Views directory, we will add a new folder ‘Proto’ and add new .cshtml file ‘ProtoForm’ with following code.

 

@model PersonPoco
@{
    ViewData["Title"] = "ProtoForm";
}

<h1>ProtoForm</h1>

<hr />

<div class="row">

    <div class="col-md-4">

        <form asp-action="ProtoForm" id="protoForm">

            <div asp-validation-summary="ModelOnly" class="text-danger"></div>

            <div class="form-group">

                <label asp-for="Id" class="control-label"></label>

                <input asp-for="Id" class="form-control" />

                <span asp-validation-for="Id" class="text-danger"></span>

            </div>

            <div class="form-group">

                <label asp-for="Name" class="control-label"></label>

                <input asp-for="Name" class="form-control" />

                <span asp-validation-for="Name" class="text-danger"></span>

            </div>

            <div class="form-group">

                <label asp-for="Email" class="control-label"></label>

                <input asp-for="Email" class="form-control" />

                <span asp-validation-for="Email" class="text-danger"></span>

            </div>

            <div class="form-group">

                <input onclick="protoSubmit()" value="Create" class="btn btn-primary" />

            </div>

        </form>

    </div>

</div>

Above markup will create a form as displayed below.

 

6_ProtoForm

Then in same file, we will add a script to call Ajax request based on field of above form.

 

<script>
        function protoSubmit() {
            var isValid = $("#protoForm").valid();

            if (isValid) {
                var id = $("#Id").val();
                var name = $("#Name").val();
                var email = $("#Email").val();

                var person = createPerson(id, name, email);

                postPerson(person);
            }
        }

        function createPerson(id, name, email) {
            var person = new window.proto.personpackage.Person();
            person.setId(id);
            person.setName(name);
            person.setEmail(email);

            return person;
        }

        function postPerson(person) {
            $.ajax({
                url: 'postproto',
                type: 'POST',
                data: person.serializeBinary(),
                contentType: "application/x-protobuf;",
                success: function (data) {
                    console.log(data);
                    alert('success');
                },
                error: function (err) {
                    console.log(err);
                    alert(`Error`);
                },
                processData: false
            });
        }
    </script>

7_ProtoForm_Input

Once we fill up a form and click a Create button, protoSubmit() function will be triggered. Here, we will firstly validate a form. If valid, then we will retrieve all three values from field and then trigger createPerson() function.

In createPerson() function, we will create an object of Person() which defined in person.js. Then, we will set its values using setId(), setName, and setEmail(). Lastly, it will return the created object.

And with returned object from createPerson(), we will trigger postPerson() function, which will implement Ajax to post given object.

Ajax properties to post protocol buffers

url: API’s action name

type: ‘POST’ since we are posting a request

data: We will pass a binary serialized content. The content will be deserialized and wired in server side at ReadRequestBodyAsync() method of ProtobufInputFormatter.cs

processData: false is necessary to avoid jQuery serializing the data in the background.

contentType: It can be either of “application/x-protobuf”, “application/protobuf”, “application/x-google-protobuf” as defined in SupportedContentTypes in ProtobufFormatterOptions.cs

Once we click ‘Create’ button, then serialize binary data will be deserialized and wired with the desired model type (PersonPoco in this case as defined in PostProto method’s parameter), we will get following output. As we can see, the protocol buffer data is now bound with PersonPoco class.

8_ProtoForm_Output

GET Request with Protocol Buffers using Ajax

Firstly, let’s create a method with HttpGet attribute in our ProtoController.

 

[HttpGet]

    public IActionResult ProtoDetail(int id)
    {
      var person = new PersonPoco
      {
        Id = id,
        Name = "Susen",
        Email = "susen.maharjan@javra.com"
      };
      return Ok(person);
    }

Firstly, we will test HTTP GET method by passing simple integer as parameter by requesting from Postman.

9_Protobuf_Get

You can see that output is being handled by custom output formatter and response is serialized to Protobuf instead of JSON format. For output, we are going to request protobuf with JSON accept headers.

10_Protobuf_Get

In the case if we want to wire Protocol buffers in Ajax with Protobuf.js, then we will need to auto-generate proto file into C# format. The syntaxes to create batch file is similar to the previous one used to create a JavaScript protocol buffer file. The only difference is that we will use –csharp_out instead of —js_out.

 

setlocal

set PROTOC=%UserProfile%\.nuget\packages\Google.Protobuf.Tools\3.6.1\tools\windows_x64\protoc.exe

set PersonPath=person.proto

set outputPath=./

%PROTOC% -I.\ --csharp_out %outputPath% %PersonPath%

 

This batch file will auto-generate a c-sharp file for a provided person.proto.

11_Person.cs

 

So let’s create another HTTP GET method in out ProtoController.

 

[HttpGet]

    public IActionResult GetProto()

    {
      var person = new Person
      {
        Id = 1001,
        Name = "Susen",
        Email = "susen.maharjan@javra.com"
      };
      return Ok(person.ToByteArray());
    }

Here, we have created an object of person and sent response of its byte array. In ProtoForm.cshtml, we will add few HTML tags as follow.

<div>
    <a onclick="getPerson()" href="#"> GET Request</a>
</div>

<div id="personList">
    <ul class="card-header-tabs"></ul>
</div>

And we will add new function in our script to call out HTTP GET method.

 

function getPerson() {
            $.get({
                headers: {
                    Accept: 'application/json'
                },
                url: 'GetProto',
                contentType: 'application/x-protobuf;',
                success: function(data) {
                    console.log(`before deserializing`);
                    console.log(data);
                    console.log(`after deserializing`);

                    var person = window.proto.personpackage.Person.deserializeBinary(data);

                    console.log(person);

                    var personId = person.getId();
                    var personName = person.getName();
                    var personEmail = person.getEmail();

                    $("#personList ul").append(`<li>${personId} ${personName} ${personEmail}</li>`);
                },
                error: function(result) {
                    alert('error');
                    console.log(result);
                }
            });

        }

Note that we are accepting JSON as accept header since auto-generated c-sharp cod of person.js will not have a ProtoContract attribute (because it is attribute of protobuf-net library).

In the above HTML tags and script function, we have created a div where a list of person details will be appended. Firstly, when we click ‘Get Request’ button, it will trigger GetProto() function in out ProtoController. Here a new object of person will be created and will return its ByteArray (ToByteArray() is an prebuilt extension of Google.Protobuf).

Then in our client side, we will deserialize the received byte array into Person object of Protobuf.js and we will get its values and append it on personList.

12_Proto_Get

13_Proto_Get

 

When to use Protobuf over JSON

 

When to use JSON over Protobuf

 

All the codes used for this article can be accessed from provided Github URL .

 

 

References

Leave a Reply

Your email address will not be published. Required fields are marked *