Update ReC.Client docs with DI, async, and API changes
Comprehensively updated the documentation for the ReC.Client library to improve usability and align with modern .NET development practices. Key changes include: - Added an introduction to the library, its purpose, and supported frameworks (.NET 8 and .NET Framework 4.6.2). - Documented core features like DI support, typed APIs, consistent error handling, and flexible GET methods. - Provided installation and setup instructions with examples in VB.NET and C#. - Explained usage patterns for GET endpoints (typed and dynamic), CRUD operations, and invoking RecActions. - Highlighted error handling via ReCApiException with examples. - Added a section on testing with in-process API testing recommendations. - Marked static APIs and synchronous helpers as [Obsolete], explaining limitations and providing migration tips. - Provided migration guidance for recent API changes, such as `GetAsync<T>` returning deserialized values and unified error handling. - Addressed FAQs about new patterns and deprecated methods. These updates aim to modernize the library, promote best practices, and simplify adoption for developers.
This commit is contained in:
470
docs/ReC.Client.xwiki
Normal file
470
docs/ReC.Client.xwiki
Normal file
@@ -0,0 +1,470 @@
|
||||
== 1. Einleitung ==
|
||||
|
||||
**ReC.Client** ist eine .NET-Client-Bibliothek für den typisierten und bequemen Zugriff auf die **ReC.API**. Anstatt direkt mit `HttpClient` zu arbeiten, bietet die Bibliothek thematisch geordnete API-Klassen (z. B. `RecActionApi`, `ResultApi`, `ProfileApi`, `EndpointAuthApi`, `EndpointParamsApi`, `EndpointsApi`, `CommonApi`) und integriert sich nahtlos in **Microsoft.Extensions.DependencyInjection**.
|
||||
|
||||
Die Bibliothek unterstützt sowohl **.NET 8** als auch **.NET Framework 4.6.2** (Multi-Targeting).
|
||||
|
||||
=== 1.1 Kernmerkmale ===
|
||||
|
||||
* **DI-orientiert**: Registrierung über `services.AddRecClient(...)`.
|
||||
* **Typisierte API-Klassen**: jede Domäne hat eine eigene API-Klasse als Eigenschaft auf `ReCClient`.
|
||||
* **Konsistente Fehlerbehandlung**: Bei HTTP-Fehlerstatus wird einheitlich eine `ReCApiException` geworfen, inklusive Statuscode, Methode, URI, Body usw.
|
||||
* **Flexibles Lesen**: GET-Endpunkte unterstützen sowohl typisierte (`GetAsync<T>(...)`) als auch dynamische (`GetAsync(...)` ohne Typparameter, liefert `dynamic` / `JsonElement`) Abfragen.
|
||||
* **Optionen**: Logging und Verhalten lassen sich über `ReCClientOptions` steuern.
|
||||
|
||||
== 2. Installation und Setup ==
|
||||
|
||||
=== 2.1 Konfiguration mit Dependency Injection (empfohlen) ===
|
||||
|
||||
Registrieren Sie den Client in `Program.cs` / `Startup.cs` über `AddRecClient`. Sie können entweder eine Basis-URL als String oder einen Konfigurations-Delegate für den zugrunde liegenden `HttpClient` übergeben.
|
||||
|
||||
{{code language="vb.net"}}
|
||||
Imports Microsoft.Extensions.Hosting
|
||||
Imports Microsoft.Extensions.DependencyInjection
|
||||
Imports ReC.Client
|
||||
|
||||
Module Program
|
||||
Sub Main(args As String())
|
||||
Dim builder = Host.CreateDefaultBuilder(args)
|
||||
|
||||
builder.ConfigureServices(
|
||||
Sub(services)
|
||||
' Variante A: Basis-URL als String
|
||||
services.AddRecClient("https://ihre-rec-api-adresse.com/")
|
||||
|
||||
' Variante B: HttpClient feinkonfigurieren
|
||||
' services.AddRecClient(Sub(client)
|
||||
' client.BaseAddress = New Uri("https://ihre-rec-api-adresse.com/")
|
||||
' client.Timeout = TimeSpan.FromSeconds(30)
|
||||
' End Sub)
|
||||
End Sub)
|
||||
|
||||
Dim app = builder.Build()
|
||||
app.Run()
|
||||
End Sub
|
||||
End Module
|
||||
{{/code}}
|
||||
|
||||
{{code language="csharp"}}
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using ReC.Client;
|
||||
|
||||
var builder = Host.CreateDefaultBuilder(args);
|
||||
builder.ConfigureServices(services =>
|
||||
{
|
||||
// Variant A: base URL as string
|
||||
services.AddRecClient("https://ihre-rec-api-adresse.com/");
|
||||
|
||||
// Variant B: configure HttpClient explicitly
|
||||
// services.AddRecClient(client =>
|
||||
// {
|
||||
// client.BaseAddress = new Uri("https://ihre-rec-api-adresse.com/");
|
||||
// client.Timeout = TimeSpan.FromSeconds(30);
|
||||
// });
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
app.Run();
|
||||
{{/code}}
|
||||
|
||||
=== 2.2 Konstruktor-Injektion ===
|
||||
|
||||
Sobald registriert, kann `ReCClient` per Konstruktor in jeden Dienst injiziert werden.
|
||||
|
||||
{{code language="vb.net"}}
|
||||
Imports ReC.Client
|
||||
|
||||
Public Class MeinDienst
|
||||
Private ReadOnly _recClient As ReCClient
|
||||
|
||||
Public Sub New(recClient As ReCClient)
|
||||
_recClient = recClient
|
||||
End Sub
|
||||
End Class
|
||||
{{/code}}
|
||||
|
||||
{{code language="csharp"}}
|
||||
using ReC.Client;
|
||||
|
||||
public class MeinDienst
|
||||
{
|
||||
private readonly ReCClient _recClient;
|
||||
|
||||
public MeinDienst(ReCClient recClient)
|
||||
{
|
||||
_recClient = recClient;
|
||||
}
|
||||
}
|
||||
{{/code}}
|
||||
|
||||
=== 2.3 Optionen über ReCClientOptions ===
|
||||
|
||||
Über `ReCClientOptions` lässt sich das Verhalten des Clients steuern, z. B. ob erfolgreiche Anfragen geloggt werden sollen.
|
||||
|
||||
{{code language="vb.net"}}
|
||||
services.AddRecClient("https://ihre-rec-api-adresse.com/")
|
||||
services.Configure(Of ReCClientOptions)(
|
||||
Sub(opt)
|
||||
opt.LogSuccessfulRequests = True
|
||||
End Sub)
|
||||
{{/code}}
|
||||
|
||||
{{code language="csharp"}}
|
||||
services.AddRecClient("https://ihre-rec-api-adresse.com/");
|
||||
services.Configure<ReCClientOptions>(opt =>
|
||||
{
|
||||
opt.LogSuccessfulRequests = true;
|
||||
});
|
||||
{{/code}}
|
||||
|
||||
== 3. Überblick über die API-Klassen ==
|
||||
|
||||
`ReCClient` bündelt mehrere thematische API-Klassen als Eigenschaften:
|
||||
|
||||
* `RecActions` (`RecActionApi`) – Verwaltung und Auslösen von RecActions (CRUD + Invoke)
|
||||
* `Results` (`ResultApi`) – Lesen, Anlegen, Aktualisieren und Löschen von Result-Datensätzen
|
||||
* `Profiles` (`ProfileApi`) – Verwaltung der Profile
|
||||
* `EndpointAuth` (`EndpointAuthApi`) – Verwaltung der Endpoint-Authentifizierungsdaten
|
||||
* `EndpointParams` (`EndpointParamsApi`) – Verwaltung der Endpoint-Parameter
|
||||
* `Endpoints` (`EndpointsApi`) – Verwaltung der Endpoints
|
||||
* `Common` (`CommonApi`) – Gemeinsame Operationen, die nicht entitätsspezifisch sind
|
||||
|
||||
Alle entitätsspezifischen Klassen erben von `BaseCrudApi` und bieten ein konsistentes CRUD-Schema.
|
||||
|
||||
== 4. Verwendung ==
|
||||
|
||||
=== 4.1 GET-Endpunkte: typisiert oder dynamisch ===
|
||||
|
||||
Die GET-Methoden in `RecActionApi`, `ProfileApi` und `ResultApi` existieren jeweils als **zwei Overloads**:
|
||||
|
||||
* **Generisch**: `GetAsync<T>(...)` – führt die Anfrage aus, liest den Response-Body **einmal** und deserialisiert ihn in den Typ `T`.
|
||||
* **Nicht-generisch**: `GetAsync(...)` – identische Parameterliste, gibt aber ein `dynamic` (in der Praxis `System.Text.Json.JsonElement`) zurück. Intern wird `GetAsync<object>(...)` aufgerufen.
|
||||
|
||||
Beide Overloads teilen sich Implementierung und Fehlerbehandlung: bei HTTP-Fehlerstatus wird **`ReCApiException`** geworfen.
|
||||
|
||||
{{info}}
|
||||
Da der nicht-generische Overload eine andere Signatur als der generische besitzt (kein Typparameter), gibt es **keinen Konflikt**. Welcher Overload aufgerufen wird, hängt davon ab, ob Sie einen Typparameter angeben oder nicht.
|
||||
{{/info}}
|
||||
|
||||
==== 4.1.1 Typisiertes Lesen ====
|
||||
|
||||
{{code language="vb.net"}}
|
||||
Imports ReC.Application.Common.Dto
|
||||
|
||||
' Alle Actions für ein Profil als typisiertes Array
|
||||
Dim actions As RecActionViewDto() =
|
||||
Await recClient.RecActions.GetAsync(Of RecActionViewDto())(profileId:=42)
|
||||
|
||||
For Each a In actions
|
||||
Console.WriteLine($"Action {a.Id} -> Endpoint {a.EndpointUri}")
|
||||
Next
|
||||
{{/code}}
|
||||
|
||||
{{code language="csharp"}}
|
||||
using ReC.Application.Common.Dto;
|
||||
|
||||
// All actions for a profile as a typed array
|
||||
var actions = await recClient.RecActions.GetAsync<RecActionViewDto[]>(profileId: 42);
|
||||
|
||||
foreach (var a in actions!)
|
||||
{
|
||||
Console.WriteLine($"Action {a.Id} -> Endpoint {a.EndpointUri}");
|
||||
}
|
||||
{{/code}}
|
||||
|
||||
==== 4.1.2 Dynamisches Lesen ====
|
||||
|
||||
Wenn das Schema flexibel ist oder Sie das Ergebnis nur weiterleiten möchten, können Sie den nicht-generischen Overload verwenden:
|
||||
|
||||
{{code language="vb.net"}}
|
||||
Imports System.Text.Json
|
||||
|
||||
Dim payload As Object = Await recClient.RecActions.GetAsync(profileId:=42)
|
||||
Dim element As JsonElement = CType(payload, JsonElement)
|
||||
|
||||
If element.ValueKind = JsonValueKind.Array Then
|
||||
For Each item In element.EnumerateArray()
|
||||
Console.WriteLine(item.GetProperty("id").GetInt64())
|
||||
Next
|
||||
End If
|
||||
{{/code}}
|
||||
|
||||
{{code language="csharp"}}
|
||||
using System.Text.Json;
|
||||
|
||||
dynamic? payload = await recClient.RecActions.GetAsync(profileId: 42);
|
||||
var element = (JsonElement)payload!;
|
||||
|
||||
if (element.ValueKind == JsonValueKind.Array)
|
||||
{
|
||||
foreach (var item in element.EnumerateArray())
|
||||
{
|
||||
Console.WriteLine(item.GetProperty("id").GetInt64());
|
||||
}
|
||||
}
|
||||
{{/code}}
|
||||
|
||||
=== 4.2 Eine RecAction auslösen (Invoke) ===
|
||||
|
||||
`RecActionApi.InvokeAsync` startet die Stapelverarbeitung der Actions eines Profils. Es gibt zwei Overloads: einen mit `InvokeReferences`-Objekt und einen Komfort-Overload mit nur einer Batch-ID.
|
||||
|
||||
{{code language="vb.net"}}
|
||||
Imports ReC.Client.Api
|
||||
|
||||
Public Async Function FuehreProfilAktionenAus(recClient As ReCClient, profilId As Long) As Task
|
||||
Try
|
||||
Await recClient.RecActions.InvokeAsync(
|
||||
profilId,
|
||||
New InvokeReferences With {.BatchId = "batch-" & Guid.NewGuid().ToString("N")})
|
||||
Catch ex As ReC.Client.ReCApiException
|
||||
' Auswertung von ex.StatusCode, ex.Method, ex.RequestUri, ex.ResponseBody
|
||||
Throw
|
||||
End Try
|
||||
End Function
|
||||
{{/code}}
|
||||
|
||||
{{code language="csharp"}}
|
||||
using ReC.Client;
|
||||
using ReC.Client.Api;
|
||||
|
||||
public async Task ExecuteProfileActionsAsync(ReCClient recClient, long profileId)
|
||||
{
|
||||
try
|
||||
{
|
||||
await recClient.RecActions.InvokeAsync(
|
||||
profileId,
|
||||
new InvokeReferences { BatchId = $"batch-{Guid.NewGuid():N}" });
|
||||
}
|
||||
catch (ReCApiException ex)
|
||||
{
|
||||
// Inspect ex.StatusCode, ex.Method, ex.RequestUri, ex.ResponseBody
|
||||
throw;
|
||||
}
|
||||
}
|
||||
{{/code}}
|
||||
|
||||
Komfort-Overload nur mit Batch-ID:
|
||||
|
||||
{{code language="vb.net"}}
|
||||
Await recClient.RecActions.InvokeAsync(profilId, "batch-001")
|
||||
{{/code}}
|
||||
|
||||
{{code language="csharp"}}
|
||||
await recClient.RecActions.InvokeAsync(profileId, "batch-001");
|
||||
{{/code}}
|
||||
|
||||
=== 4.3 Anlegen, Aktualisieren, Löschen (CRUD) ===
|
||||
|
||||
Alle CRUD-Operationen sind asynchron und werfen bei Fehlern eine `ReCApiException`.
|
||||
|
||||
* `CreateAsync(payload)` – HTTP POST mit JSON-Body.
|
||||
* `UpdateAsync(id, payload)` – HTTP PUT auf `/{ResourcePath}/{id}` mit JSON-Body.
|
||||
* `DeleteAsync(payload)` – HTTP DELETE; das Payload wird in den **Query-String** serialisiert (die API bindet Delete-Parameter aus der URL).
|
||||
|
||||
{{code language="vb.net"}}
|
||||
Imports ReC.Application.RecActions.Commands
|
||||
Imports ReC.Application.Common.Procedures.UpdateProcedure.Dto
|
||||
|
||||
' POST
|
||||
Await recClient.RecActions.CreateAsync(New InsertActionCommand With {
|
||||
.ProfileId = 1,
|
||||
.EndpointId = 1,
|
||||
.Active = True,
|
||||
.Sequence = 1
|
||||
})
|
||||
|
||||
' PUT
|
||||
Await recClient.RecActions.UpdateAsync(123, New UpdateActionDto With {
|
||||
.Active = False,
|
||||
.Sequence = 2
|
||||
})
|
||||
|
||||
' DELETE (Payload wird zu Query-String)
|
||||
Await recClient.RecActions.DeleteAsync(New DeleteActionCommand With {
|
||||
.Start = 100,
|
||||
.End = 110,
|
||||
.Force = False
|
||||
})
|
||||
{{/code}}
|
||||
|
||||
{{code language="csharp"}}
|
||||
using ReC.Application.RecActions.Commands;
|
||||
using ReC.Application.Common.Procedures.UpdateProcedure.Dto;
|
||||
|
||||
// POST
|
||||
await recClient.RecActions.CreateAsync(new InsertActionCommand
|
||||
{
|
||||
ProfileId = 1,
|
||||
EndpointId = 1,
|
||||
Active = true,
|
||||
Sequence = 1
|
||||
});
|
||||
|
||||
// PUT
|
||||
await recClient.RecActions.UpdateAsync(123, new UpdateActionDto
|
||||
{
|
||||
Active = false,
|
||||
Sequence = 2
|
||||
});
|
||||
|
||||
// DELETE (payload becomes query string)
|
||||
await recClient.RecActions.DeleteAsync(new DeleteActionCommand
|
||||
{
|
||||
Start = 100,
|
||||
End = 110,
|
||||
Force = false
|
||||
});
|
||||
{{/code}}
|
||||
|
||||
=== 4.4 Fehlerbehandlung mit ReCApiException ===
|
||||
|
||||
Sobald die API einen Statuscode außerhalb von 2xx zurückgibt, wirft die Bibliothek eine `ReCApiException`. Diese enthält folgende Informationen:
|
||||
|
||||
* `StatusCode` – `HttpStatusCode` der Antwort (z. B. 404, 400, 500)
|
||||
* `ReasonPhrase` – Optionaler HTTP-Reason-Phrase
|
||||
* `ResponseBody` – Roher Response-Body als String (sofern lesbar)
|
||||
* `Method` – HTTP-Methode der ursprünglichen Anfrage (z. B. `GET`, `POST`)
|
||||
* `RequestUri` – Aufgerufene URI mit Pfad und Query
|
||||
|
||||
{{code language="vb.net"}}
|
||||
Try
|
||||
Dim profile = Await recClient.Profiles.GetAsync(Of ProfileViewDto)(id:=42)
|
||||
Catch ex As ReCApiException
|
||||
If ex.StatusCode = Net.HttpStatusCode.NotFound Then
|
||||
' Profil existiert nicht
|
||||
Else
|
||||
' Allgemeiner Fehler
|
||||
Console.WriteLine($"{ex.Method} {ex.RequestUri} -> {ex.StatusCode}: {ex.ResponseBody}")
|
||||
Throw
|
||||
End If
|
||||
End Try
|
||||
{{/code}}
|
||||
|
||||
{{code language="csharp"}}
|
||||
try
|
||||
{
|
||||
var profile = await recClient.Profiles.GetAsync<ProfileViewDto>(id: 42);
|
||||
}
|
||||
catch (ReCApiException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
|
||||
{
|
||||
// Profile does not exist
|
||||
}
|
||||
catch (ReCApiException ex)
|
||||
{
|
||||
Console.WriteLine($"{ex.Method} {ex.RequestUri} -> {ex.StatusCode}: {ex.ResponseBody}");
|
||||
throw;
|
||||
}
|
||||
{{/code}}
|
||||
|
||||
== 5. Testen ==
|
||||
|
||||
Das Testprojekt verwendet `Microsoft.AspNetCore.Mvc.Testing`, um die `ReC.API` mit `WebApplicationFactory<Program>` **in-process** zu starten. Der `ReCClient` wird über DI konfiguriert und auf den in-process HTTP-Handler verdrahtet. So müssen Tests die API nicht extern starten.
|
||||
|
||||
Empfehlungen:
|
||||
|
||||
* Schreiben Sie Tests als `async Task` und verwenden Sie `await` – **vermeiden** Sie `GetAwaiter().GetResult()` oder `TaskSyncExtensions.Sync(...)`.
|
||||
* Verwenden Sie `Assert.ThrowsAsync<ReCApiException>(...)`, um Fehlerpfade zu prüfen, und werten Sie `StatusCode`, `Method` und `RequestUri` aus.
|
||||
* Für GET-Tests reicht eine einzelne Methode pro Verhalten (typisiert vs. dynamisch) statt redundanter Setups.
|
||||
|
||||
== 6. Veraltete / statische APIs ==
|
||||
|
||||
Einige historische Hilfsmittel sind weiterhin vorhanden und funktionsfähig, jedoch mit `[Obsolete]` markiert. Sie sind kein Breaking Change – Sie können sie übergangsweise weiter benutzen, sollten aber langfristig migrieren.
|
||||
|
||||
Betroffen:
|
||||
|
||||
* `ReCClient.BuildStaticClient(string)` und `ReCClient.BuildStaticClient(Action<HttpClient>)`
|
||||
* `ReCClient.Create()`
|
||||
* `TaskSyncExtensions.Sync(...)` und `TaskSyncExtensions.Sync<TResult>(...)`
|
||||
|
||||
Warum als `Obsolete` markiert?
|
||||
|
||||
* **Statischer Provider**: Ein global gehaltener `IServiceProvider` erschwert die Verwaltung von `HttpClient`-Lebenszyklen, kann zu schwer reproduzierbaren Problemen in lang laufenden Prozessen führen und behindert Tests.
|
||||
* **Synchrone Blockade**: `Task.GetAwaiter().GetResult()` (was `TaskSyncExtensions.Sync` intern tut) kann in Umgebungen mit `SynchronizationContext` (z. B. WinForms, WPF, einige Test-Runner) zu Deadlocks führen und macht Fehler schwerer diagnostizierbar.
|
||||
|
||||
Funktionieren sie noch?
|
||||
|
||||
* Ja. Die Methoden bleiben kompilierbar und ausführbar. Sie erhalten lediglich eine Compiler-Warnung bei der Verwendung.
|
||||
|
||||
Wann darf man sie übergangsweise nutzen?
|
||||
|
||||
* In Legacy-Code, der nicht sofort auf DI + `async/await` umgestellt werden kann.
|
||||
* In sehr kurzen, isolierten Skripten oder Konsolenanwendungen ohne `SynchronizationContext`, wo das Risiko überschaubar ist.
|
||||
|
||||
Beispiel – statischer Client (nicht empfohlen, aber möglich):
|
||||
|
||||
{{code language="vb.net"}}
|
||||
' Einmalig beim Anwendungsstart
|
||||
ReCClient.BuildStaticClient("https://ihre-rec-api-adresse.com/")
|
||||
|
||||
' Später irgendwo im Code
|
||||
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}}
|
||||
|
||||
Beispiel – synchrone Blockade (nicht empfohlen, aber möglich):
|
||||
|
||||
{{code language="vb.net"}}
|
||||
Imports ReC.Client
|
||||
|
||||
' Achtung: kann zu Deadlocks führen
|
||||
recClient.RecActions.InvokeAsync(profilId, "batch-001").Sync()
|
||||
{{/code}}
|
||||
|
||||
{{code language="csharp"}}
|
||||
using ReC.Client;
|
||||
|
||||
// Warning: may deadlock depending on context
|
||||
recClient.RecActions.InvokeAsync(profileId, "batch-001").Sync();
|
||||
{{/code}}
|
||||
|
||||
Migrations-Tipps:
|
||||
|
||||
* **`BuildStaticClient` / `Create`** ? ersetzen durch `services.AddRecClient(...)` und Konstruktor-Injektion.
|
||||
* **`TaskSyncExtensions.Sync`** ? den umliegenden Codepfad asynchron machen (`async Task`) und `await` verwenden.
|
||||
|
||||
== 7. Migrations-Hinweise (jüngste Änderungen) ==
|
||||
|
||||
Folgende Änderungen sind zu beachten, falls Sie von einer früheren Version migrieren:
|
||||
|
||||
* **GET-Methoden geben jetzt deserialisierte Werte zurück, nicht mehr `HttpResponseMessage`.**
|
||||
** `GetAsync<T>(...)` liest den Body **einmal** und gibt `T?` zurück.
|
||||
** `GetAsync(...)` (ohne Typparameter) gibt `dynamic`/`JsonElement` zurück.
|
||||
** Falls Sie zuvor `HttpResponseMessage` selbst behandelt haben (Status, Body lesen, deserialisieren), entfällt dieser Schritt.
|
||||
* **Einheitliche Fehlerbehandlung über `ReCApiException`.**
|
||||
** Bei HTTP-Fehlern wird konsistent diese Exception geworfen – auch für GET.
|
||||
** Sie müssen nicht mehr selbst auf `IsSuccessStatusCode` prüfen.
|
||||
* **`GetDynamicAsync` wurde umbenannt zu `GetAsync` (Overload).**
|
||||
** Es gibt nun pro API-Klasse zwei `GetAsync`-Overloads: typisiert und dynamisch. Aufrufe von `GetDynamicAsync(...)` müssen zu `GetAsync(...)` geändert werden.
|
||||
* **`TaskSyncExtensions` und statische `ReCClient`-Helfer sind `[Obsolete]`.**
|
||||
|
||||
== 8. FAQ ==
|
||||
|
||||
**Warum gibt `GetAsync<T>` einen deserialisierten Wert statt `HttpResponseMessage` zurück?**
|
||||
|
||||
Damit wird der Response-Body genau einmal gelesen, Fehler werden einheitlich über `ReCApiException` behandelt, und Aufrufer müssen weder selbst auf den Statuscode prüfen noch die Antwort manuell deserialisieren.
|
||||
|
||||
**Wozu der nicht-generische `GetAsync(...)`-Overload?**
|
||||
|
||||
Er ist ein Komfort-Aufruf für Fälle, in denen Sie das Schema nicht (oder noch nicht) typisieren möchten. Intern ruft er `GetAsync<object>` auf und liefert ein `JsonElement` zurück, das Sie ad hoc inspizieren können.
|
||||
|
||||
**Gibt es einen Konflikt zwischen dem generischen und dem nicht-generischen `GetAsync`?**
|
||||
|
||||
Nein. Beide Methoden haben unterschiedliche Signaturen (ein Methodengeneric-Parameter ist Teil der Signatur). Der Compiler wählt anhand der Aufrufsyntax (`GetAsync<T>(...)` vs. `GetAsync(...)`).
|
||||
|
||||
**Soll ich die statischen `ReCClient.Create`-Helfer noch verwenden?**
|
||||
|
||||
Nur in Legacy-Szenarien. Für neuen Code: DI mit `services.AddRecClient(...)`.
|
||||
|
||||
**Sind synchrone Aufrufe via `Sync()` sicher?**
|
||||
|
||||
Nicht generell. In Umgebungen mit `SynchronizationContext` riskieren sie Deadlocks. Verwenden Sie `async/await`.
|
||||
Reference in New Issue
Block a user