Advertisement
Поиск  
Always will be ready notify the world about expectations as easy as possible: job change page
месяц назад

Create a gRPC server in .NET Core

Create a gRPC server in .NET Core
Автор:
Источник:
Просмотров:
1865

Remote Procedure Calls

gRPC is a powerful framework for working with Remote Procedure Calls. RPCs allow you to write code as though it will be run on a local computer, even though it may be executed on another computer.

One of the key advantages of gRPC is its use of HTTP/2, which enables efficient and multiplexed communication between clients and servers. This allows gRPC to handle multiple requests and responses simultaneously, which can significantly improve performance and reduce latency.

In this blog post, I will add a gRPC Server to my BlogPost Project. You can follow this tutorial to setup the API Project.

• • •

Initial Setup and Configuration

Open your solution and add a new ASP.NET Core gRPC Service. I will name the project SeeSharp.gRPC

Add a new project

Let’s get to know protobuf files.

In the greet.proto file you can see a service and some messages.

Protobuf file

The file is a template file that defines a Greeter Service, and has one method “SayHello” that takes in a HelloRequest message and returns a HelloReply message. The numbers you see next to “name” and “message” are nothing but the order of the properties, so adding a new property “mobile” for example would look like this.

Message file

As you might have noticed, this proto file does not include any implementation, its just an interface or a contract.

Let’s make sure that everything is working before we start customizing.

  • Set the gRPC project as a StartUp Project and run your solution.
  • Open Postman and create a new gRPC Request.
  • Select the greet.proto file and import it (remember, the proto file is the blueprint of the service).

Postman request

Implement the Service

Now that we completed the initial setup, let’s integrate the gRPC service with our BlogPosts Api.

  • Add a project reference from gRPC to SeeSharp.Application project
  • Add a project reference from gRPC to SeeSharp.Infrastructure project

in the program.cs file register the Application and Infrastructure Services.

builder.Services.AddApplicationServices();
builder.Services.AddInfrastructureServices(builder.Configuration);

Next, add the connection string and any other configuration that was added in the REST api project. You can copy the full appsettings.json from the Api project to the gRPC project.

At this stage we have setup a gRPC service and wired up all necessary services that will be used.

Next, we will define the protobuf file. Right click on the gRPC project and add a File of type protobuf. name it “blogposts.proto”.

syntax = "proto3";
option csharp_namespace = "SeeSharp.gRPC.Protos";
package blogpost;

Add the below code to the blogposts.proto file.

message CreateBlogPostRequest{
    string title = 1;
    string category = 2;
    string author = 3;
    string content = 4;
}
message CreateBlogPostResponse{
    string id = 1;
}

In the code above, I defined two messages, a CreateBlogPostRequest message, and a CreateBlogPostResponse message.

The numbers next to each property is the order of that property. So here, I am saying that the CreateBlogPostRequest has 4 properties title, category, author, and content.

Next we will define the service contract. To do so, add the below code.

service BlogPosts{
    // Create
    rpc CreateBlogPost(CreateBlogPostRequest) returns (CreateBlogPostResponse) {}
}

The work here is repetitive, for each method we need to define a request and response message.

Here is the full protofile.

syntax = "proto3";

option csharp_namespace = "SeeSharp.gRPC.Protos";

package blogposts;

service BlogPosts{
    // Create
    rpc CreateBlogPost(CreateBlogPostRequest) returns (CreateBlogPostResponse) {}
    // Read List
    rpc GetBlogPosts(GetBlogPostsRequest) returns (GetBlogPostsResponse) {}
    // Read Single
    rpc GetBlogPostById(GetBlogPostRequest) returns (GetBlogPostReponse){}
    // Update
    rpc UpdateBlogPost(UpdateBlogPostRequest) returns(empty) {}
    // Delete
    rpc DeleteBlogPost(DeleteBlogPostRequest) returns(empty){}
}

message CreateBlogPostRequest{
    string title = 1;
    string category = 2;
    string author = 3;
    string content = 4;
}

message CreateBlogPostResponse{
    string id = 1;
}

message GetBlogPostRequest{
    string id = 1;
}

message GetBlogPostReponse{
    string id = 1;
    string title = 2;
    string category = 3;
    string author = 4;
    string content = 5;
    string dateCreated = 6;
}

message GetBlogPostsRequest{}

message GetBlogPostsResponse{
    repeated GetBlogPostReponse blogPosts = 1;
}

message UpdateBlogPostRequest{
    string id = 1;
    string title = 2;
    string category = 3;
    string author = 4;
    string content = 5;
}

message DeleteBlogPostRequest{
    string id = 1;
}

message empty{
}

Now we need to add this proto file to out project. To do so, just add the below lines in the csproj file.

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

Remember, any change to the protofile requires a rebuild of the project. Once the project is rebuilt, the classes (messages) will be constructed and will be available for use.

Next, lets create a BlogPostService.cs

In the services folder, add a new file and call it BlogPostsService.cs

using SeeSharp.gRPC.Protos;

namespace SeeSharp.gRPC.Services;

public class BlogPostsService : BlogPosts.BlogPostsBase
{

}

Notice how we inherited from BlogPosts.BlogPostsBase which was constructed when we built our project based on the blogposts.proto file.

In the service class, we need to inject IMediatR and override the methods implementations.

Add the below code.

public override async Task<CreateBlogPostResponse> CreateBlogPost(CreateBlogPostRequest request, ServerCallContext context)
{
    var command = new CreateBlogPostCommand
    {
        Title = request.Title,
        Author = request.Author,
        Category = request.Category,
        Content = request.Content
    };
    var result = await _mediator.Send(command);

    return await Task.FromResult(new CreateBlogPostResponse { Id = result.ToString() });
}

Let’s test. open postman, and create a new gRPC request. Alternatively, you can change the protobuf file from greet.proto to blogposts.proto and reload the methods.

Postman gRPC request

SQL table records

Success! We were able to create a blogpost using gRPC. Let’s implement the other endpoints.

GetAllBlogPosts

public override async Task<GetBlogPostsResponse> GetBlogPosts(GetBlogPostsRequest request, ServerCallContext context)
{
    var result = await _mediator.Send(new GetBlogPostsQuery());

    var response = new GetBlogPostsResponse();

    response.BlogPosts.AddRange(result.Select(blogpost => new GetBlogPostReponse
    {
        Id = blogpost.Id.ToString(),
        Title = blogpost.Title,
        Author = blogpost.Author,
        Category = blogpost.Category,
        Content = blogpost.Content,
        DateCreated = blogpost.DateCreated
    }));

    return await Task.FromResult(response);
}

and the result?

Blog posts response

The same goes for GetBlogPost, UpdateBlogPost, and DeleteBlogPost. Below is the full BlogPostService.cs file.

using Grpc.Core;
using MediatR;
using SeeSharp.Application.Features.BlogPosts.Commands.CreateBlogPost;
using SeeSharp.Application.Features.BlogPosts.Commands.DeleteBlogPost;
using SeeSharp.Application.Features.BlogPosts.Commands.UpdateBlogPost;
using SeeSharp.Application.Features.BlogPosts.Queries;
using SeeSharp.gRPC.Protos;

namespace SeeSharp.gRPC.Services;

public class BlogPostsService : BlogPosts.BlogPostsBase
{
    private readonly IMediator _mediator;

    public BlogPostsService(IMediator mediator)
    {
        _mediator = mediator;
    }

    public override async Task<CreateBlogPostResponse> CreateBlogPost(CreateBlogPostRequest request, ServerCallContext context)
    {
        var command = new CreateBlogPostCommand
        {
            Title = request.Title,
            Author = request.Author,
            Category = request.Category,
            Content = request.Content
        };
        var result = await _mediator.Send(command);
        return await Task.FromResult(new CreateBlogPostResponse { Id = result.ToString() });
    }

    public override async Task<GetBlogPostReponse> GetBlogPostById(GetBlogPostRequest request, ServerCallContext context)
    {
        var query = new GetBlogPostByIdQuery(Guid.Parse(request.Id));
        var result = await _mediator.Send(query);
        var response = new GetBlogPostReponse
        {
            Id = result.Id.ToString(),
            Title = result.Title,
            Category = result.Category,
            Author = result.Author,
            Content = result.Content,
            DateCreated = result.DateCreated
        };
        return await Task.FromResult(response);
    }

    public override async Task<GetBlogPostsResponse> GetBlogPosts(GetBlogPostsRequest request, ServerCallContext context)
    {
        var result = await _mediator.Send(new GetBlogPostsQuery());
        var response = new GetBlogPostsResponse();
        response.BlogPosts.AddRange(result.Select(blogpost => new GetBlogPostReponse
        {
            Id = blogpost.Id.ToString(),
            Title = blogpost.Title,
            Author = blogpost.Author,
            Category = blogpost.Category,
            Content = blogpost.Content,
            DateCreated = blogpost.DateCreated
        }));
        return await Task.FromResult(response);
    }

    public override async Task<empty> UpdateBlogPost(UpdateBlogPostRequest request, ServerCallContext context)
    {
        var command = new UpdateBlogPostCommand
        {
            Id = Guid.Parse(request.Id),
            Title = request.Title,
            Category = request.Category,
            Author = request.Author,
            Content = request.Content,
        };
        await _mediator.Send(command);
        return new empty();
    }

    public override async Task<empty> DeleteBlogPost(DeleteBlogPostRequest request, ServerCallContext context)
    {
        var command = new DeleteBlogPostCommand(Guid.Parse(request.Id));
        await _mediator.Send(command);
        return new empty();
    }
}

Enable JsonTranscoding

The gRPC Service is ready, but how do we enable communication over http? We need to enable JsonTranscoding in our project.

To do so, install the below package:

dotnet add package Microsoft.AspNetCore.Grpc.JsonTranscoding

Once installed, it needs to be registered in the services pool. Open your program.cs file and add the below code.

// Add services to the container.
builder.Services.AddGrpc().AddJsonTranscoding();

In the project, create 2 files in the below directory (those are settings files)

API protobuf files

Now we need to add Service Annotations for each rpc service in our protofile.

rpc CreateBlogPost(CreateBlogPostRequest) returns (CreateBlogPostResponse) {
  option (google.api.http) = {
    post: "/v1/blogposts",
    body: "*"
  };
}

The blogposts.proto file will look like this.

syntax = "proto3";

option csharp_namespace = "SeeSharp.gRPC.Protos";

import "google/api/annotations.proto";

package blogposts;

service BlogPosts{
    // Create
    rpc CreateBlogPost(CreateBlogPostRequest) returns (CreateBlogPostResponse) {
        option (google.api.http) = {
            post: "/v1/blogposts",
            body: "*"
        };
    }

    // Read List
    rpc GetBlogPosts(GetBlogPostsRequest) returns (GetBlogPostsResponse) {
        option (google.api.http) = {
            get: "/v1/blogposts"
        };
    }

    // Read Single
    rpc GetBlogPostById(GetBlogPostRequest) returns (GetBlogPostReponse){
        option (google.api.http) = {
            get: "/v1/blogposts/{id}"
        };
    }

    // Update
    rpc UpdateBlogPost(UpdateBlogPostRequest) returns(empty) {
        option (google.api.http) = {
            put : "/v1/blogposts",
            body: "*"
        };
    }

    // Delete
    rpc DeleteBlogPost(DeleteBlogPostRequest) returns(empty){
        option (google.api.http) = {
            delete: "/v1/blogposts/{id}"
        };
    }
}

message CreateBlogPostRequest{
    string title = 1;
    string category = 2;
    string author = 3;
    string content = 4;
}

message CreateBlogPostResponse{
    string id = 1;
}

message GetBlogPostRequest{
    string id = 1;
}

message GetBlogPostReponse{
    string id = 1;
    string title = 2;
    string category = 3;
    string author = 4;
    string content = 5;
    string dateCreated = 6;
}

message GetBlogPostsRequest{}

message GetBlogPostsResponse{
    repeated GetBlogPostReponse blogPosts = 1;
}

message UpdateBlogPostRequest{
    string id = 1;
    string title = 2;
    string category = 3;
    string author = 4;
    string content = 5;
}

message DeleteBlogPostRequest{
    string id = 1;
}

message empty{
}

As a result, we are able to call gRPC service over http/s.

1.5 - Testing

Open the browser, or Postman, and browse https://localhost:[port]/v1/blogposts

Testing

Testing

Source Code

You can find the source code under this Github Repo. Use the ‘Feature-gRPC’ Branch.

• • •

Stay tuned! :)

Похожее
Dec 1, 2022
Author: George Dyrrachitis
Having a system which is composed by distributed applications is a great idea, but a way to communicate with each other is required. A very popular architecture is the so called MDA or Message Driven Architecture, where a system is...
Nov 16, 2022
Author: Vipin Sharma
Need to communicate with multiple DBs in one application??Usually we don’t face such scenarios. However if such requirement comes in picture, we will see how easy it is to communicate with multiple DBs using repository pattern in .Net Core Web...
Jan 31, 2023
Author: Arkaprava Sinha
Today we will see, how to enable Logging directly to Loki via Serilog for a .NET Core Microservice and visualize the logs in Grafana.Before we start,What is Loki?Loki is a horizontally scalable, highly available, multi-tenant log aggregation system inspired by...
Aug 26, 2022
Author: Jaydeep Patil
We are going to discuss the RabbitMQ Message Queue and its implementation using .NET Core 6 API as Message Producer and Console Application as a Message Consumer.Agenda Introduction of RabbitMQ Benefits of using RabbitMQ...
Написать сообщение
Почта
Имя
*Сообщение


© 1999–2024 WebDynamics
1980–... Sergey Drozdov
Area of interests: .NET Framework | .NET Core | C# | ASP.NET | Windows Forms | WPF | HTML5 | CSS3 | jQuery | AJAX | Angular | React | MS SQL Server | Transact-SQL | ADO.NET | Entity Framework | IIS | OOP | OOA | OOD | WCF | WPF | MSMQ | MVC | MVP | MVVM | Design Patterns | Enterprise Architecture | Scrum | Kanban