Bisher spricht die Kladde mit der Welt über HTTP und JSON: Browser, curl, der Client aus Lektion 2.12.2. Zwischen zwei eigenen Diensten gibt es einen zweiten Weg, der in .NET zu Hause ist: gRPC. Der Vertrag ist dort keine Beschreibung, die man nachträglich erzeugt, sondern der Ausgangspunkt, aus dem der Code entsteht; die Nachrichten sind binär und klein; und jeder Aufruf trägt eine Frist. In dieser Lektion lösen Sie die Berechnung der freien Zeiten aus Lektion 2.5.6 probeweise als eigenen kleinen Dienst Kladde.Slots heraus, den die Webanwendung über gRPC fragt. Es ist eine Übung: Die Kladde braucht für sich allein keinen zweiten Dienst. In K3 nehmen Sie den Dienst wieder auf.

Der Vertrag in Protobuf

Ein gRPC-Dienst beginnt mit einer .proto-Datei. Sie beschreibt die Methoden des Dienstes und die Nachrichten, die sie austauschen, in der Sprache Protocol Buffers (Protobuf). Legen Sie im Branch spike-grpc ein neues Projekt an und darin Protos/slots.proto:

PowerShell
dotnet new web -n Kladde.Slots -o src/Kladde.Slots -f net10.0
dotnet solution Kladde.slnx add src/Kladde.Slots/Kladde.Slots.csproj
dotnet add src/Kladde.Slots package Grpc.AspNetCore --version 2.85.0
protobuf
syntax = "proto3";

option csharp_namespace = "Kladde.Slots";

package kladde.slots.v1;

import "google/protobuf/timestamp.proto";

// The free slots of the office, computed from its opening hours and appointments.
service SlotService {
  // The free slots of one day.
  rpc GetFreeSlots (FreeSlotsRequest) returns (FreeSlotsReply);
  // The free slots of several days, one message per day as soon as it is computed.
  rpc StreamFreeDays (FreeDaysRequest) returns (stream FreeDay);
}

message FreeSlotsRequest {
  string day = 1;      // yyyy-MM-dd in the office's time zone
  int32 minutes = 2;   // length of one appointment
}

message FreeSlotsReply {
  repeated FreeSlot slots = 1;
}

message FreeDaysRequest {
  string first_day = 1;
  int32 days = 2;
  int32 minutes = 3;
}

message FreeDay {
  string day = 1;
  repeated FreeSlot slots = 2;
}

message FreeSlot {
  google.protobuf.Timestamp start = 1;
  int32 minutes = 2;
}
  • service nennt die Methoden. GetFreeSlots ist unär: eine Anfrage, eine Antwort. StreamFreeDays liefert mit stream eine Folge von Antworten, eine je Tag: Server-Streaming.
  • message sind die Nachrichten, mit Typen wie string, int32 und repeated für Listen.
  • Die Zahl hinter jedem Feld ist seine Feldnummer. Auf der Leitung steht nicht der Name minutes, sondern die Nummer 2. Darauf beruht alles, was weiter unten zur Weiterentwicklung kommt.
  • google.protobuf.Timestamp ist ein fertiger Typ für Zeitpunkte. Er kennt keine Zeitzone, nur einen Zeitpunkt in UTC, wie ein DateTimeOffset ohne Versatz. Den Tag des Büros schicken Sie deshalb als Text, so wie die API ihn in Lektion 2.3.9 als DateOnly kennt.
  • package kladde.slots.v1 und csharp_namespace: der Name des Dienstes auf der Leitung und der Namensraum im C#-Code. Das v1 im Paketnamen ist eine Konvention für den Tag, an dem es doch einmal eine unverträgliche Fassung braucht.