Home .NET Communication between C# and C#:REST,gRPC and everything in between

Communication between C# and C#:REST,gRPC and everything in between

by admin

There are many ways to communicate between a C# client and a C# server.Some of them are reliable, others are not.Some are very fast, others are not. It is important to know the different options so that you can decide what is best for you. This article will look at the most popular technologies today and why they are so widely used. We’ll talk about REST, gRPC and everything in between.

Optimal scenario

Let’s consider what we would like our client-servercommunication to look like in the real world. I imagine something like this :

// on client sidepublic void Foo(){var server = new MyServer(new Uri("https://www.myserver.com/");)int sum = server.Calculator.SumNumbers(12, 13);}

// on server sideclass CalculatorController: Controller{public int SumNumbers(int a, int b){return a + b;}}

I would like to have full support for Intellisense. When I press server and I want Visual Studio to show all the controllers. And when I click. CalculatorController and , I want to see all methods of action. I also want the highest performance, very low network load, and bi-directional data transfer. And I want a reliable system that does a great job with version control so that I can deploy new versions of clients and new versions of servers without problems.
Is this too much?
Note that I am talking about the API here without saving state This is equivalent to the C # project, where there are only two types of classes :

  • Static classes with static methods only.
  • POCO classes that have nothing but fields and properties whose type is a primitive or other POCO class.

Having a state in the API introduces complexity, and that is the root of all evil. So, for the sake of this article, let’s make things nice and not save states.

Traditional RESTapproach

REST API appeared in the early 2000s and conquered the Internet. Now it is by far the most popular way to create web services.
REST defines a fixed set of operations GET , POST , PUT and DELETE for requests from the client to the server. For each request we get a response containing a payload (usually JSON). Requests include parameters in the request itself or as a payload (usually JSON) when it’s a POST or PUT request.
There is a standard RESTful API which defines the following rules (which you don’t really need):

  • GET is used to get the resource
  • PUT is used to change the state of a resource
  • POST is used to create a resource
  • DELETE is used to delete a resource

If you’re not familiar with REST so far, the above explanation probably won’t help, so here’s an example. .NET has built-in support for REST. In fact, the ASP.NET Web API is by default a REST Web service. Here is what a typical ASP.NET client and server looks like:
On the server :

[Route("People")]public class PeopleController : Controller{[HttpGet]public Person GetPersonById(int id){Person person = _db.GetPerson(id);return person;//Automatically serialized to JSON}}

On client :

var client = new HttpClient();string resultJson = await client.GetStringAsync("https://www.myserver.com/People/GetPersonById?id=123");Person person = JsonConvert.DeserializeObject<Person> (resultJson);

REST is pretty darn handy, but it doesn’t fit the optimal scenario. So, let’s see if we can do it better.

ReFit

ReFit is not an alternative to REST. Instead, it is built on top of REST and allows us to call the server endpoints as if they were a simple method. This is accomplished by separating the interface between the client and the server. On the server side, your controller will implement the interface :

public interface IMyEmployeeApi{[Get("/employee/{id}")]Task<Employee> GetEmployee(string id);}

Then on the client side you will need to enable the same interface and use the following code :

var api = RestService.For<IMyEmployeeApi> ("https://www.myserver.com");var employee = await api.GetEmployee("abc");

It’s that simple. No need to run complicated automation or use third-party tools other than a couple of NuGet packages.
This gets a lot closer to an optimal scenario. Now we have IntelliSense and a reliable contract between client and server. But there is another option, which is even better in some respects.

Swagger

Like ReFit, Swaggeris also built on top of REST. OpenAPI , or Swagger , is a REST API specification. It describes the REST web service in simple JSON files. These files are the schema of the web service API. They include :

  • All paths (URLs) in API.
  • Expected operations (GET, POST, …) for each path. Each path can handle different operations. For example, the same path mystore.com/Product can accept a POST operation, which adds a product, and a GET operation, which returns a product.
  • Expected parameters for each path and operation.
  • Expected responses for each path.
  • The types of each parameter and response object.

This JSON file is essentially a contract between the clients and the server. Here is an example of a swagger file describing a web service called Swagger Petstore (I deleted some parts for clarity):
JSON scheme

{"swagger":"2.0", "info":{"version":"1.0.0", "title":"Swagger Petstore", "description":"A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", }, "host":"petstore.swagger.io", "basePath":"/api", "schemes":["http"], "consumes":["application/json"], "produces":["application/json"], "paths":{"/pets":{"get":{"description":"Returns all pets from the system that the user has access to", "operationId":"findPets", "produces":["application/json", "application/xml", ], "parameters":[{"name":"tags", "in":"query", "description":"tags to filter by", "required":false, "type":"array", "items":{"type":"string"}, "collectionFormat":"csv"}, {"name":"limit", "in":"query", "description":"maximum number of results to return", "required":false, "type":"integer", "format":"int32"}], "responses":{"200":{"description":"pet response", "schema":{"type":"array", "items":{"$ref":"#/definitions/Pet"}}}, ...

Let’s look at the implications of this. With a JSON file like the one above, you could potentially create a C# client with full IntelliSense. After all, you know all the paths, the operations, what parameters they expect, what types of parameters, what the responses are.
There are several tools that do just that. On the server side, you can use Swashbuckle.AspNetCore. to add Swagger to your ASP.NET and create the specified JSON files. For the client side, you can use swagger-codegen and AutoRest to process these JSON files and generate the client. Let’s see an example of how to do this :

Adding Swagger to your Server ASP.NET

Start by adding the NuGet package Swashbuckle.AspNetCore In ConfigureServices , register the Swagger generator:

services.AddSwaggerGen(options =>options.SwaggerDoc("v1", new OpenApiInfo {Title = "My Web API", Version = "v1"}));

In the file Startup.cs in the method Configure add :

app.UseSwagger();

Finally, the action(s) methods within the controller must be labeled [HttpXXX] and [FromXXX] attributes :

[HttpPost]public async Task AddEmployee([FromBody]Employee employee){//...}[HttpGet]public async Task<Employee> Employee([FromQuery]string id){//...}

This is so simple for the server part. When you start the project, the file will be generated swagger.json , which you can use to generate your client.

Generating a client from Swagger with AutoRest

In order to start using AutoRest , install it with npm : npm install -g autorest After installation, you will need to use the AutoRest command line interface to create a C# client from the swagger.json Here is an example :

autorest --input-file="./swagger.json" --output-folder="GeneratedClient" --namespace="MyClient" --override-client-name="MyClient" --csharp

This will create a folder GeneratedClient with the generated C# files. Note that the namespace and client name are overridden. Add this folder to your client project in Visual Studio as shown below.
Communication between C# and C#:REST,gRPC and everything in between
You will need to install Microsoft.Rest.ClientRuntime NuGet package, because the generated code depends on it. Once installed, you can use the API like a normal C# class :

var client = new MyClient();Employee employee = client.Employee(id: "abc");

There are some subtleties, which you can read about in documentation AutoRest. And you will need to automate this process, so I suggest you read manual Patrick Svensson for some helpful tips, and this article Peter Jausowitz.
My problem with Swagger is that the JSON file is created at runtime, so it makes it a bit difficult to automate the CI/CD process.

Traditional REST vs Swagger vs ReFit

Here are a few things to consider when choosing.

  • If you have a very simple private REST API, you probably don’t need to worry about generating clients and generic interfaces.A small task doesn’t justify the extra effort.
  • Swagger supports many languages, while ReFit only supports .NET. Swagger is also the basis for many tools, tests, automation tools, and user interface tools. It is probably the best choice if you are building a large public API.
  • Swagger is much more complicated than ReFit. With ReFit, it’s just a matter of adding a single interface to both your server and client projects. With ReFit, on the other hand, you have to create new interfaces for each controller, whereas Swagger takes care of that automatically.

But before you decide anything, check option 4, which has nothing to do with REST.

gRPC

gRPC (gRPC – remote procedure call) is an open-source remote procedure call system developed by Google. It’s a bit like REST in the sense that it provides a way to send requests from the client to the server. But it’s different in many ways; here are the similarities and differences :

  • Like REST, gRPC is language-independent. There are tools for all popular languages, including C#.
  • gRPC is contract-based and uses proto files to define the contract. This is somewhat similar to Swagger swagger.json and the common ReFit interface. Any programming language client can be generated from these files.
  • gRPC uses binary serialization Protocol Buffer (Protobuf) This is different from REST, which is usually serialized in JSON or XML. Binary serialization is smaller and therefore faster.
  • gRPC is designed to create long-lasting connections using the HTTP/2 protocol. This protocol is simpler and more compact. Given that REST uses the HTTP 1.x protocol (usually HTTP 1.1).
  • HTTP 1.1 requires a TCP handshake for each request, whereas HTTP/2 keeps the connection open.
  • An HTTP/2 connection uses multiplexed streams. This means that a single TCP connection can support multiple threads. These threads can run in parallel without waiting for each other, as in HTTP 1.1.
  • gRPC allows bidirectional streaming.

There are two ways to use gRPC. For .NET Core 3.0 there is a fully managed library gRPC for .NET You can also use gRPC C# This does not mean that gRPC for .NET replaces gRPC C# Let’s look at an example with the newer gRPC for .NET

Server part of gRPC for .NET

This is not a guide, but rather a general idea of what to expect. Here is what an example of a controller in gRPC would look like:

public class GreeterService :Greeter.GreeterBase{public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context){_logger.LogInformation("Saying hello to {Name}", request.Name);return Task.FromResult(new HelloReply{Message = "Hello " + request.Name});}}

You need to add the following to Configure in the file Startup.cs :

app.UseEndpoints(endpoints =>{endpoints.MapGrpcService<GreeterService> ();});

The API is described in proto file, which is part of the project :

syntax = "proto3";service Greeter {rpc SayHello (HelloRequest) returns (HelloReply);}message HelloRequest {string name = 1;}message HelloReply {string message = 1;}

This one proto file has been added to the .csproj file:

<ItemGroup><Protobuf Include="Protosgreet.proto"/></ItemGroup>

Client part of gRPC for .NET

The client is generated from proto files. The code itself is very simple :

var channel = GrpcChannel.ForAddress("https://localhost:5001");var client = new Greeter.GreeterClient(channel);var response = await client.SayHello(new HelloRequest { Name = "World" });Console.WriteLine(response.Message);

gRPC vs REST

GRPC sounds like a nice deal. It’s faster and easier under the hood. So should we all switch from REST to gRPC? The answer is that it is. depends on Here are some considerations :
In my experience, there is still not much work with gRPC and ASP.NET. You’ll be better off with mature REST support. As far as contract communication is concerned, this is good, except that you have similar alternatives in REST, which we already talked about : Swagger and ReFit.
The biggest advantage is performance. In most cases, According to these criteria. , gRPC is much faster. Especially for large payloads, for which Protobuf serialization really matters. This means that it is a huge advantage for a high load server.
The transition from REST to gRPC in a large ASP.NET application will be difficult. However, if you have a microservices-based architecture, this transition becomes much easier to make gradually.

Other ways to communicate

There are several other ways of communicating that I haven’t mentioned at all, but it’s worth knowing that they exist :

  • GraphQL – is a query language for APIs developed by Facebook. It allows the client to request exactly the data it needs from the server. This way you can create only one endpoint on the server that is extremely flexible and returns only the data the client needs. GraphQL has become very popular in recent years.
  • TcpClient and TcpListener (in System.Net.Sockets ) provide a low-level TCP connection. Essentially, you’re going to set up a connection and pass byte arrays. This is not ideal for a large application where you can use ASP.NET controllers and action methods to put a big API in order.
  • UdpClient provides a way of communicating using the UDP protocol. TCP establishes a connection and then sends data, whereas UDP simply sends data. TCP ensures that there are no errors in the data, while UDP does not. UDP is more efficient for fast data transmission because you don’t care enough about it being reliable and error free. Here are some examples : video streaming, live streaming and voice over IP (VoIP).
  • WCF – is an old technology that basically uses SOAP-based communication between processes. It’s a huge framework, which I won’t go into, except to say that it’s lost its popularity for REST and JSON payloads.

That’s it. Hope this article was interesting! Have a good code, everybody!

You may also like