Zum Inhalt

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 über TargetSystem aufgebauten 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:

var client = ZedalClientFactory.Default.CreateClient(
    system:        TargetSystem.Test,
    clientName:    "MyIntegrationApp",
    clientVersion: "1.0.0",
    transport:     ZedalTransport.GrpcWeb);

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.