Client und Verbindung¶
Wie du den Client erzeugst, welchen Transport er nutzt und wie du Zeitgrenzen je Aufruf setzt.
Client erzeugen¶
ZedalClientFactory.Default bietet Überladungen für drei Fälle:
| Überladung | Wann |
|---|---|
CreateClient(TargetSystem, clientName, clientVersion, …) |
Regelfall. Die Adresse leitet das SDK aus der Umgebung ab. |
CreateClient(string address, clientName, clientVersion, …) |
Explizite Adresse; wird unverändert benutzt und nicht auf ein Gateway umgeschrieben. |
CreateClient(IZedalConnection, clientName, clientVersion, …) |
Du hast die Verbindung selbst über ZedalConnectionFactory gebaut — etwa mit eigenen Interceptors, oder um mit den Konventionen des SDK (Proxy, Nachrichtengrößen) über Channel / CallInvoker eigene Stubs zu erzeugen. |
Alle Überladungen nehmen dieselben optionalen Parameter:
| Parameter | Bedeutung |
|---|---|
tokenRefresh |
Eigene Implementierung von IJwtTokenRefresh statt der Default-Strategie (siehe Token-Erneuerung) |
transport |
ZedalTransport; Default Auto (siehe Transport) |
configureHandler |
Action<HttpClientHandler>; wird auf jeden Handler angewendet, den die Verbindung erzeugt — Probe und Kanal |
proxyUrl |
SDK-eigener Proxy; hat Vorrang vor der Umgebungsvariablen |
clientName und clientVersion gehen als Header in jeden Aufruf und erscheinen in den serverseitigen Logs. Nimm die Werte, die du bei der Registrierung angegeben hast; das verkürzt Supportanfragen.
TargetSystem ist ein Enum mit Production = 1, Preview = 2 und Test = 3 und liegt im Namespace Zedal.Sdk.Impl. Welche Hosts dahinterstehen und warum du gegen Test entwickelst, steht unter Umgebungen.
Transport¶
Das SDK spricht mit ZEDAL entweder natives gRPC über HTTP/2 oder gRPC-Web über HTTP/1.1. Die beiden Wege liegen auf verschiedenen Hosts: natives gRPC auf api.<umgebung>.zedal.de, gRPC-Web auf dem Gateway openapi.<umgebung>.zedal.de. Das SDK rechnet die Adresse um, indem es https://api. durch https://openapi. ersetzt.
ZedalTransport |
Verhalten |
|---|---|
Auto (Default) |
Prüft beim Erzeugen der Verbindung einmal, welche HTTP-Version der native Host aushandelt, und entscheidet danach. |
Grpc |
Immer natives gRPC über HTTP/2 gegen api.<umgebung>. |
GrpcWeb |
Immer gRPC-Web gegen openapi.<umgebung>; ausgehende Anfragen werden auf HTTP/1.1 festgenagelt. |
Unknown |
Sentinel für „nicht gesetzt”. Kein benutzbarer Transport. |
Die Regeln von Auto im Detail — sie sind bewusst konservativ:
- HTTP/2 verfügbar →
Grpc. - Der Host antwortet, aber nur HTTP/1.x →
GrpcWeb, aber nur bei einer überTargetSystemaufgebauten Verbindung. Das ist der einzige Fall, in dem heruntergeschaltet wird: positiver Nachweis plus bekanntes Gateway. - Keine Antwort (offline, Timeout, DNS-Fehler) → es bleibt bei
Grpc. Ein fehlendes Netz ist kein Beweis, dass HTTP/2 blockiert ist. - Explizite Adresse → bleibt immer bei
Grpc. Die Adresse wird nicht umgeschrieben, und ein beliebiger Host hat kein gRPC-Web-Gegenstück.
client.Connection.Transport liefert danach den aufgelösten Wert und ist nie Auto.
Die Auto-Probe ist ein blockierender Netzwerkaufruf im Konstruktor
CreateClient(...) mit transport: ZedalTransport.Auto schickt eine HTTP-Anfrage, bevor es zurückkehrt (Zeitgrenze der Probe: 5 Sekunden). In Startpfaden, die nicht blockieren dürfen, und in Umgebungen, in denen HTTP/2 ohnehin gesperrt ist, setz den Transport direkt:
Proxy in Firmennetzen¶
Ein prozessweit gesetztes HTTP_PROXY trifft alle Verbindungen der Anwendung und bricht Off-Domain-Szenarien (Home-Office, VPN aus). Das SDK bringt deshalb einen eigenen, auf ZEDAL-Verkehr begrenzten Proxy mit.
| Weg | Wirkung |
|---|---|
Parameter proxyUrl |
Vorrang vor allem anderen |
Umgebungsvariable INFOTECH_GRPC_PROXY_URL |
greift, wenn kein Parameter gesetzt ist |
Wert bypass (Parameter oder Variable, Groß-/Kleinschreibung egal) |
schaltet Proxying komplett ab — auch einen konfigurierten Systemproxy |
Umgebungsvariable INFOTECH_GRPC_NO_PROXY |
veraltet; wirkt wie bypass und schlägt eine gesetzte Proxy-URL. Nutze stattdessen den Sentinel. |
Die Proxy-Angabe wird nachsichtig geparst: host:port genügt (gate:3128), ein fehlendes Schema wird zu http ergänzt. Dieselbe Route benutzt auch die Auto-Probe — sie prüft also den Weg, den die echten Aufrufe nehmen.
Verbindung prüfen¶
Wenn nichts geht, beantwortet CheckConnection die Frage „liegt es am Netz?” ohne fachlichen Aufruf:
var status = await client.Connection.CheckConnection();
Console.WriteLine($"{status.Address} reachable={status.Reachable} " +
$"transport={status.Transport} http={status.HttpVersion} " +
$"proxy={status.ProxyAddress} rtt={status.RoundTripTime}");
ZedalConnectionStatus trägt Reachable, Transport, Address, HttpVersion, RoundTripTime, Error, ProxyAddress und UsesProxy. Der Test ist rein transportbezogen: Er sagt nichts über Authentifizierung und nicht darüber, ob die gewünschten gRPC-Dienste auf dem Endpunkt gemappt sind. Für eine Prüfung auf Dienstebene nimm client.DiscoveryApi.ListProvider() — der Aufruf braucht kein Token.
Nachrichtengröße
Das SDK hebt die gRPC-Nachrichtengrenze für Senden und Empfangen auf 50 MB an. Der Default von Grpc.Net.Client (4 MB Empfang) ist für Dokument- und Anhangs-Payloads zu klein.
Zeitgrenzen: ZedalCallContext¶
Jede API-Methode des SDK nimmt als letzten Parameter einen optionalen ZedalCallContext. Er bündelt Deadline, CancellationToken und zusätzliche gRPC-Header für einen Aufruf.
| Wert | Bedeutung |
|---|---|
client.DefaultTimeout |
Client-weiter Default in Sekunden. Startwert 30. |
ZedalCallContext.ClientDefaultTimeout (0) |
Sentinel „Aufrufer hat nichts gesetzt” → es gilt client.DefaultTimeout. Keine Sekundenangabe und kein „unbegrenzt”. |
ZedalCallContext.NoTimeout (-1) |
Keine Deadline. Nur sinnvoll zusammen mit einem CancellationToken. |
0 ist keine Abkürzung für „unbegrenzt”
client.DefaultTimeout = 0 würde eine Deadline von „jetzt” erzeugen und jeden Aufruf sofort mit DeadlineExceeded scheitern lassen. Deshalb weisen sowohl der Setter als auch der Konstruktor des Kontexts 0 und alle negativen Werte außer NoTimeout mit ArgumentOutOfRangeException ab. Zum Abschalten gibt es NoTimeout bzw. WithoutDeadline().
Die With…-Methoden sind Instanz-Wither: jede liefert eine Kopie mit genau einem geänderten Feld, die Reihenfolge einer Kette ist ohne Bedeutung.
| Methode | Wirkung |
|---|---|
WithTimeout(seconds) |
Deadline in Sekunden |
WithoutDeadline() |
Deadline abschalten |
WithCancellation(token) |
CancellationToken setzen |
WithExtraHeaders(metadata) |
Zusatz-Header ersetzen |
WithAdditionalHeaders(metadata) |
Zusatz-Header ergänzen; bei gleichem Key gewinnt der bestehende Eintrag |
WithFallbackTimeout(seconds) |
Deadline nur setzen, wenn der Aufrufer keine eigene gesetzt hat |
// Generous, finite deadline plus cancellation option
await client.DiwassApi.AttachmentApi.GetAttachment(attachmentId,
ZedalCallContext.Default.WithTimeout(600).WithCancellation(token));
// Only the token cancels
await client.DiwassApi.AttachmentApi.GetAttachment(attachmentId,
ZedalCallContext.Default.WithoutDeadline().WithCancellation(token));
// A CancellationToken alone is converted implicitly
await client.CoreApi.Documents.ExportCurrentDocument(docId, context: token);
Deadline und Abbruch bleiben für den Aufrufer unterscheidbar: Eine gerissene Deadline kommt als RpcException mit StatusCode.DeadlineExceeded, ein Abbruch über den Token als OperationCanceledException. Ein übergebener Token hebt die Deadline nicht auf; wer die Zeitsteuerung ganz selbst übernehmen will, sagt das mit WithoutDeadline().
30 Sekunden reichen für DiWaSS-Pfade nicht
Aufrufe, die serverseitig ans EU-System weitergereicht werden, brauchen ein Vielfaches des Defaults. Setz die Deadline pro Aufruf statt den Client-Default global hochzudrehen — die Budgets unterscheiden sich je Vorgang erheblich. Die Richtwerte stehen bei den Aufrufen, zu denen sie gehören: Lebenszyklus → Versandverfahren.