Improve ReCClient static client and documentation
Updated `ReCClientOptions` to include a warning about the `LogSuccessfulRequests` option throwing an `InvalidOperationException` if no `ILogger` is registered via DI. Added validation and thread-safety to `BuildStaticClient` using `Lazy<IServiceProvider>`. Introduced `StaticBuildConfiguration` for callback-based configuration and detailed its properties. Clarified usage patterns, added VB.NET and C# examples, and documented validation rules. Marked older `BuildStaticClient` overloads as `[Obsolete]` while retaining functionality. Expanded context on static client use cases and synchronous wrappers. Improved documentation clarity and consistency.
This commit is contained in:
@@ -118,6 +118,10 @@ services.Configure<ReCClientOptions>(opt =>
|
||||
});
|
||||
{{/code}}
|
||||
|
||||
{{warning}}
|
||||
Wenn `LogSuccessfulRequests = true` gesetzt, aber **kein** `ILogger` über DI registriert ist, wirft der `ReCClient`-Konstruktor eine `InvalidOperationException`. Stellen Sie sicher, dass ein Logging-Provider (z. B. `services.AddLogging(...)`) registriert ist, oder lassen Sie die Option auf `false`.
|
||||
{{/warning}}
|
||||
|
||||
== 3. Überblick über die API-Klassen ==
|
||||
|
||||
`ReCClient` bündelt mehrere thematische API-Klassen als Eigenschaften:
|
||||
@@ -369,7 +373,7 @@ Empfehlungen:
|
||||
|
||||
== 6. Komfort-APIs: statischer Provider und synchrone Wrapper ==
|
||||
|
||||
Neben dem empfohlenen DI-basierten Ansatz bietet **ReC.Client** absichtlich auch einen **statischen Komfort-Pfad** sowie **synchrone Wrapper** an. Diese Bestandteile sind nicht „veraltet im Sinne von eingefroren" – sie werden weiterhin gepflegt und bei Bedarf um neue Funktionen erweitert (Beispiel: der jüngst hinzugefügte optionale `Action<ReCClientOptions>`-Parameter in `BuildStaticClient`). Sie sind jedoch bewusst mit `[Obsolete]` markiert, damit Aufrufer sie nicht „aus Versehen" auswählen, sondern eine bewusste Entscheidung treffen.
|
||||
Neben dem empfohlenen DI-basierten Ansatz bietet **ReC.Client** absichtlich auch einen **statischen Komfort-Pfad** sowie **synchrone Wrapper** an. Diese Bestandteile sind nicht „veraltet im Sinne von eingefroren" – sie werden weiterhin gepflegt und bei Bedarf um neue Funktionen erweitert. Sie sind jedoch bewusst mit `[Obsolete]` markiert, damit Aufrufer sie nicht „aus Versehen" auswählen, sondern eine bewusste Entscheidung treffen.
|
||||
|
||||
Hintergrund
|
||||
|
||||
@@ -396,64 +400,129 @@ Wann besser DI verwenden
|
||||
|
||||
=== 6.1 Statischer Client mit BuildStaticClient / Create ===
|
||||
|
||||
`ReCClient.BuildStaticClient(...)` baut intern eine `IServiceCollection` auf, ruft `AddRecClient(...)` auf und legt einen statischen `IServiceProvider` ab. Anschließend liefert `ReCClient.Create()` Instanzen aus diesem Provider.
|
||||
`ReCClient.BuildStaticClient(...)` baut intern eine `IServiceCollection` auf, ruft `AddRecClient(...)` auf und legt einen **statischen, thread-safen `Lazy<IServiceProvider>`** an. Der eigentliche `IServiceProvider` wird **erst beim ersten Aufruf von `ReCClient.Create()`** und genau einmal gebaut.
|
||||
|
||||
Wichtig: `BuildStaticClient` darf **nur einmal** beim Anwendungsstart aufgerufen werden.
|
||||
Wichtig:
|
||||
|
||||
Variante mit Basis-URL:
|
||||
* `BuildStaticClient` darf **nur einmal** beim Anwendungsstart aufgerufen werden. Ein zweiter Aufruf – egal welcher Overload – wirft `InvalidOperationException("Static Provider is already built.")`.
|
||||
* Die Konstruktion des `IServiceProvider` ist **threadsicher** (`Lazy<T>` mit `LazyThreadSafetyMode.ExecutionAndPublication`).
|
||||
|
||||
==== 6.1.1 Empfohlene Form: BuildStaticClient mit StaticBuildConfiguration ====
|
||||
|
||||
Da der statische Pfad mehrere optionale Bestandteile hat (Basis-URL **oder** `HttpClient`-Konfiguration, `ReCClientOptions`, eigener `ILogger`, zusätzliche Service-Registrierungen), gibt es einen Callback-basierten Overload, der alle Optionen in einem `StaticBuildConfiguration`-Objekt bündelt:
|
||||
|
||||
{{code language="vb.net"}}
|
||||
' Einmalig beim Anwendungsstart
|
||||
ReCClient.BuildStaticClient("https://ihre-rec-api-adresse.com/")
|
||||
Imports Microsoft.Extensions.DependencyInjection
|
||||
Imports Microsoft.Extensions.Logging
|
||||
|
||||
' Später irgendwo im Code
|
||||
ReCClient.BuildStaticClient(
|
||||
Sub(cfg)
|
||||
cfg.BaseAddress = "https://ihre-rec-api-adresse.com/"
|
||||
|
||||
cfg.ConfigureOptions = Sub(opt)
|
||||
opt.LogSuccessfulRequests = True
|
||||
End Sub
|
||||
|
||||
' Optional: eigene zusätzliche Registrierungen, z. B. Logging-Provider
|
||||
cfg.ConfigureServices = Sub(services)
|
||||
services.AddLogging(Sub(b) b.AddConsole())
|
||||
End Sub
|
||||
End Sub)
|
||||
{{/code}}
|
||||
|
||||
{{code language="csharp"}}
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
ReCClient.BuildStaticClient(cfg =>
|
||||
{
|
||||
cfg.BaseAddress = "https://ihre-rec-api-adresse.com/";
|
||||
|
||||
cfg.ConfigureOptions = opt =>
|
||||
{
|
||||
opt.LogSuccessfulRequests = true;
|
||||
};
|
||||
|
||||
// Optional: additional service registrations, e.g. logging
|
||||
cfg.ConfigureServices = services =>
|
||||
{
|
||||
services.AddLogging(b => b.AddConsole());
|
||||
};
|
||||
});
|
||||
{{/code}}
|
||||
|
||||
Eigenschaften von `StaticBuildConfiguration`:
|
||||
|
||||
* `BaseAddress` – Basis-URI der ReC.API. **Schließt sich gegenseitig** mit `ConfigureClient` aus.
|
||||
* `ConfigureClient` – Delegate zum direkten Konfigurieren des `HttpClient` (z. B. `BaseAddress` + `Timeout` + Header). Schließt sich gegenseitig mit `BaseAddress` aus.
|
||||
* `ConfigureOptions` – Optionaler Delegate für `ReCClientOptions` (z. B. `LogSuccessfulRequests`).
|
||||
* `Logger` – Optionale `ILogger`-Instanz, die als Singleton in die interne `IServiceCollection` registriert wird.
|
||||
* `ConfigureServices` – Optionaler Delegate, mit dem der Aufrufer beliebige zusätzliche Registrierungen auf der internen `IServiceCollection` vornehmen kann (z. B. `AddLogging(...)` oder eigene Abhängigkeiten).
|
||||
|
||||
Validierung beim Aufruf:
|
||||
|
||||
* `BuildStaticClient` wirft `ArgumentNullException`, wenn der `configure`-Callback `null` ist.
|
||||
* `BuildStaticClient` wirft `InvalidOperationException`, wenn weder `BaseAddress` noch `ConfigureClient` gesetzt sind, **oder** wenn beide gleichzeitig gesetzt sind.
|
||||
|
||||
Variante mit `HttpClient`-Feinkonfiguration:
|
||||
|
||||
{{code language="vb.net"}}
|
||||
ReCClient.BuildStaticClient(
|
||||
Sub(cfg)
|
||||
cfg.ConfigureClient = Sub(http)
|
||||
http.BaseAddress = New Uri("https://ihre-rec-api-adresse.com/")
|
||||
http.Timeout = TimeSpan.FromSeconds(30)
|
||||
End Sub
|
||||
End Sub)
|
||||
{{/code}}
|
||||
|
||||
{{code language="csharp"}}
|
||||
ReCClient.BuildStaticClient(cfg =>
|
||||
{
|
||||
cfg.ConfigureClient = http =>
|
||||
{
|
||||
http.BaseAddress = new Uri("https://ihre-rec-api-adresse.com/");
|
||||
http.Timeout = TimeSpan.FromSeconds(30);
|
||||
};
|
||||
});
|
||||
{{/code}}
|
||||
|
||||
Nach dem `BuildStaticClient`-Aufruf liefert `ReCClient.Create()` Instanzen aus dem statischen Provider:
|
||||
|
||||
{{code language="vb.net"}}
|
||||
Dim client As ReCClient = ReCClient.Create()
|
||||
Await client.RecActions.InvokeAsync(profilId, "batch-001")
|
||||
{{/code}}
|
||||
|
||||
{{code language="csharp"}}
|
||||
// Once at application startup
|
||||
ReCClient.BuildStaticClient("https://ihre-rec-api-adresse.com/");
|
||||
|
||||
// Later somewhere in code
|
||||
var client = ReCClient.Create();
|
||||
await client.RecActions.InvokeAsync(profileId, "batch-001");
|
||||
{{/code}}
|
||||
|
||||
Variante mit `HttpClient`-Konfiguration:
|
||||
==== 6.1.2 Ältere Komfort-Overloads ====
|
||||
|
||||
Aus Bequemlichkeit existieren zwei weitere Overloads, die intern an die `StaticBuildConfiguration`-Variante delegieren. Sie sind als `Obsolete` markiert mit dem Hinweis, die `Action<StaticBuildConfiguration>`-Variante zu verwenden, bleiben aber funktionsfähig:
|
||||
|
||||
{{code language="vb.net"}}
|
||||
ReCClient.BuildStaticClient(Sub(http)
|
||||
http.BaseAddress = New Uri("https://ihre-rec-api-adresse.com/")
|
||||
http.Timeout = TimeSpan.FromSeconds(30)
|
||||
End Sub)
|
||||
' Variante mit Basis-URL als String
|
||||
ReCClient.BuildStaticClient("https://ihre-rec-api-adresse.com/")
|
||||
|
||||
' Mit Options-Callback und optionalem Logger
|
||||
ReCClient.BuildStaticClient(
|
||||
"https://ihre-rec-api-adresse.com/",
|
||||
Sub(opt) opt.LogSuccessfulRequests = True,
|
||||
myLogger)
|
||||
{{/code}}
|
||||
|
||||
{{code language="csharp"}}
|
||||
ReCClient.BuildStaticClient(http =>
|
||||
{
|
||||
http.BaseAddress = new Uri("https://ihre-rec-api-adresse.com/");
|
||||
http.Timeout = TimeSpan.FromSeconds(30);
|
||||
});
|
||||
{{/code}}
|
||||
// Variant with base URL string
|
||||
ReCClient.BuildStaticClient("https://ihre-rec-api-adresse.com/");
|
||||
|
||||
Optional kann zusätzlich `ReCClientOptions` über einen Callback gesetzt werden – die Signatur entspricht der von `AddRecClient`:
|
||||
|
||||
{{code language="vb.net"}}
|
||||
// With options callback and optional logger
|
||||
ReCClient.BuildStaticClient(
|
||||
"https://ihre-rec-api-adresse.com/",
|
||||
Sub(opt)
|
||||
opt.LogSuccessfulRequests = True
|
||||
End Sub)
|
||||
{{/code}}
|
||||
|
||||
{{code language="csharp"}}
|
||||
ReCClient.BuildStaticClient(
|
||||
"https://ihre-rec-api-adresse.com/",
|
||||
opt =>
|
||||
{
|
||||
opt.LogSuccessfulRequests = true;
|
||||
});
|
||||
opt => opt.LogSuccessfulRequests = true,
|
||||
myLogger);
|
||||
{{/code}}
|
||||
|
||||
=== 6.2 Synchrone Wrapper über TaskSyncExtensions ===
|
||||
|
||||
Reference in New Issue
Block a user