Files
SlipItIn/SlipItIn/Services/ApiService.cs
Tim Krampitz bbc6ad6080 Architektur-Redesign: SignalR-Multiplayer & MVVM
Umfassende Umstrukturierung der SlipItIn-App für eine moderne, zustandsbasierte und SignalR-gestützte Multiplayer-Architektur. Einführung neuer Services für Auth, API, SignalR, lokalen Speicher und Game-State-Management via Dependency Injection. Komplette Neugestaltung der Views (Login, Registrierung, Lobby, Spielbrett) mit MVVM und CommunityToolkit.Mvvm. Navigation jetzt über MAUI Shell mit expliziten Routen. Game-Logik auf Events/Messaging umgestellt, UI und Logik entkoppelt, Echtzeit-Updates integriert. SignalR für alle Spielaktionen inkl. Wiederverbindung und Offline-Queueing. Lokaler Speicher für Auth, Userdaten, GameState und Aktionen. MainPage entfernt, neue Views implementiert. Projektdatei um NuGet-Pakete und Shared-Projekt erweitert. Backend-Änderungen für direkte Spieler-Zuordnung und korrekte EF Core-Relationen. App ist nun robust, testbar und unterstützt Multiplayer- sowie Offline-Szenarien.
2026-07-26 17:31:26 +02:00

85 lines
2.9 KiB
C#

using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using SlipItIn.Services.Interfaces;
using SlipItIn.Shared.DTOs;
namespace SlipItIn.Services;
public class ApiService : IApiService
{
private readonly HttpClient _httpClient;
private readonly IAppConfigurationService _configuration;
private readonly IAuthSessionService _authSession;
public ApiService(IAppConfigurationService configuration, IAuthSessionService authSession)
{
_configuration = configuration;
_authSession = authSession;
_httpClient = new HttpClient
{
BaseAddress = new Uri(_configuration.ApiBaseUrl)
};
}
public Task<AuthResponseDto> RegisterAsync(RegisterRequestDto request, CancellationToken cancellationToken = default)
=> PostAuthAsync("api/auth/register", request, cancellationToken);
public Task<AuthResponseDto> LoginAsync(LoginRequestDto request, CancellationToken cancellationToken = default)
=> PostAuthAsync("api/auth/login", request, cancellationToken);
public Task LogoutAsync() => _authSession.ClearSessionAsync();
private async Task<AuthResponseDto> PostAuthAsync<TRequest>(string endpoint, TRequest request, CancellationToken cancellationToken)
{
AttachAuthHeader();
var response = await _httpClient.PostAsJsonAsync(endpoint, request, cancellationToken);
if (response.IsSuccessStatusCode)
{
var payload = await response.Content.ReadFromJsonAsync<AuthResponseDto>(cancellationToken: cancellationToken);
if (payload is null)
throw new InvalidOperationException("Leere Auth-Antwort vom Server.");
return payload;
}
var message = await TryReadErrorMessageAsync(response, cancellationToken);
throw new InvalidOperationException(message);
}
private void AttachAuthHeader()
{
var token = _authSession.AccessToken;
_httpClient.DefaultRequestHeaders.Authorization = string.IsNullOrWhiteSpace(token)
? null
: new AuthenticationHeaderValue("Bearer", token);
}
private static async Task<string> TryReadErrorMessageAsync(HttpResponseMessage response, CancellationToken cancellationToken)
{
var raw = await response.Content.ReadAsStringAsync(cancellationToken);
if (string.IsNullOrWhiteSpace(raw))
return $"Serverfehler ({(int)response.StatusCode}).";
try
{
using var doc = JsonDocument.Parse(raw);
if (doc.RootElement.ValueKind == JsonValueKind.Object &&
doc.RootElement.TryGetProperty("message", out var msgElement))
{
var msg = msgElement.GetString();
if (!string.IsNullOrWhiteSpace(msg))
return msg;
}
}
catch
{
// ignore
}
return raw;
}
}