Skip to main content

GPRC

Setup

  1. Create a directory

    mkdir gRPC
    cd gRPC
  2. Create a new ASP.NET Core gRPC Service project

    dotnet new grpc -n GrpcHero --framework net9.0
    cd GrpcHero
  3. Open in IDE

    rider .

Grpc Hello World

  1. Duplicate greet.proto as hero.proto

  2. Update hero.proto:

    syntax = "proto3";

    option csharp_namespace = "GrpcHero";

    package hero;

    // The greeting service definition.
    service HeroGreeter {
    // Sends a greeting
    rpc CallForHelp (HelpRequest) returns (HelpReply);
    }

    message HelpRequest {
    string problem = 1;
    }

    message HelpReply {
    string message = 1;
    }
  3. Update csproj to add the following to the existing itemgroup:

    <Protobuf Include="Protos\hero.proto" GrpcServices="Server" />
    warning

    If you forget to add this step your service won't get source generated.

  4. Duplicate GreeterService to HeroService and fix up

    using Grpc.Core;
    using GrpcHero;

    public class HeroService : HeroGreeter.HeroGreeterBase
    {
    private readonly ILogger<HeroService> _logger;
    public HeroService(ILogger<HeroService> logger)
    {
    _logger = logger;
    }

    public override Task<HelpReply> CallForHelp(HelpRequest request, ServerCallContext context)
    {
    return Task.FromResult(new HelpReply()
    {
    Message = "Superman is on the way 💪!"
    });
    }
    }
  5. Update Program.cs to add the new service

    app.MapGrpcService<HeroService>();

Reflection

  1. Run project

  2. Open Insomnia Scratchpad

  3. Create a new gRPC request

  4. Set the URL to https://localhost:5000

  5. Show that the server doesn't support reflection so we can't see what services are available

    note

    We COULD manually add the proto file, but let's use reflection instead.

  6. Add the following package

    dotnet add package Grpc.AspNetCore.Server.Reflection
  7. Register reflection in Program.cs:

    builder.Services.AddGrpcReflection();
    app.MapGrpcReflectionService();
  8. Run app again

  9. Refresh proto definitions in Insomnia via 'reflection'

Test

  1. Select HeroGreeter/CallForHelp as the function

  2. Set unary body to

    {
    "problem": "I need help!"
    }
  3. Hit send

Grpc Streaming

  1. Add a new method to the hero.proto file for streaming:

    // Sends a stream of hero's greetings to a user
    rpc PlanMission (MissionRequest) returns (stream MissionReply);
  2. Add new messages

    message MissionRequest {
    string problem = 1;
    }

    message MissionReply{
    string plan = 1;
    }
  3. Build the project

  4. Add a mission plan

    public class MissionPlan
    {
    public static string[] GetSupermanAndWonderWomanPlan()
    {
    return new string[]
    {
    "Superman and Wonder Woman will conduct reconnaissance from the air to assess the situation.",
    "Wonder Woman will use her Lasso of Truth to extract crucial information from key individuals.",
    "Superman will use his super speed to evacuate civilians from the danger zone.",
    "Wonder Woman will deflect incoming projectiles with her indestructible bracelets.",
    "Superman will use his freeze breath to neutralize any immediate threats.",
    "Wonder Woman will establish a secure perimeter using her Amazon training.",
    "Superman will use his x-ray vision to locate hidden enemies or devices.",
    "Together they will execute a coordinated attack on the main threat.",
    "Wonder Woman will secure any dangerous artifacts with her divine knowledge.",
    "Superman will use his strength to restore damaged infrastructure."
    };
    }
    }
  5. Update HeroService to implement the new streaming method:

    public override async Task PlanMission(MissionRequest request, IServerStreamWriter<MissionReply> responseStream, ServerCallContext context)
    {
    _logger.LogInformation($"Planning mission for problem: {request.Problem}");

    string[] missionSteps = MissionPlan.GetSupermanAndWonderWomanPlan();

    foreach (var step in missionSteps)
    {
    await responseStream.WriteAsync(new MissionReply { Plan = step });
    await Task.Delay(TimeSpan.FromSeconds(1));
    }
    }

Test the streaming method

  1. Update gRPC definition via reflection

  2. Test as above

  3. Observe streamed responses

presenter

Stop here during the demo. The attendees can try out the other demos on their own.

Grpc JsonTranscoding

  1. Add a package reference to Microsoft.AspNetCore.Grpc.JsonTranscoding

    dotnet add package Microsoft.AspNetCore.Grpc.JsonTranscoding
  2. Register transcoding in server startup code by adding AddJsonTranscoding:

    • In the Program.cs file, change builder.Services.AddGrpc(); to builder.Services.AddGrpc().AddJsonTranscoding();.
  3. Add <IncludeHttpRuleProtos>true</IncludeHttpRuleProtos> to the property group in the .csproj project file:

    <Project Sdk="Microsoft.NET.Sdk.Web">

    <PropertyGroup>
    <TargetFramework>net9.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <InvariantGlobalization>true</InvariantGlobalization>
    <IncludeHttpRuleProtos>true</IncludeHttpRuleProtos>
    </PropertyGroup>
  4. Annotate gRPC methods in your .proto files with HTTP bindings and routes:

    // 👇👇👇
    import "google/api/annotations.proto";
    // 👆👆👆

    service Hero {
    rpc SayHello (HelloRequest) returns (HelloReply) {
    // 👇👇👇
    option (google.api.http) = {
    get: "/v1/hero/{name}"
    };
    // 👆👆👆
    }

    rpc SayHelloStream (HelloStreamRequest) returns (stream HelloReply) {
    // 👇👇👇
    option (google.api.http) = {
    post: "/v1/hero"
    body: "*"
    };
    // 👆👆👆
    }
    }

GrpcSwagger

  1. Add a package reference to Microsoft.AspNetCore.Grpc.Swagger

    dotnet add package Microsoft.AspNetCore.Grpc.Swagger
  2. Add Swagger to services

    using HeroRelay.Services;

    var builder = WebApplication.CreateBuilder(args);

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

    // 👇👇👇
    builder.Services.AddGrpcSwagger();
    builder.Services.AddSwaggerGen(c =>
    {
    c.SwaggerDoc("v1",
    new OpenApiInfo { Title = "gRPC Hero transcoding", Version = "v1" });
    });
    // 👆👆👆

    var app = builder.Build();

    // Configure the HTTP request pipeline.

    // 👇👇👇 - Add Swagger UI Middleware
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
    c.SwaggerEndpoint("/swagger/v1/swagger.json", "My Hero API V1");
    });
    // 👆👆👆

    app.MapGrpcService<GreeterService>();
    app.MapGrpcReflectionService();

    app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");

    app.Run();
  3. Enable the XML documentation file in the server project with:

    <GenerateDocumentationFile>true</GenerateDocumentationFile>
  4. Configure AddSwaggerGen to read the generated XML file. Pass the XML file path to IncludeXmlComments and IncludeGrpcXmlComments, as in the following example:

    var filePath = Path.Combine(System.AppContext.BaseDirectory, "GrpcHero.xml");
    c.IncludeXmlComments(filePath);
    c.IncludeGrpcXmlComments(filePath, includeControllerXmlComments: true);

Docs