Blazor ist ein Microsoft-Framework für die Entwicklung von Webkomponenten auf Basis von .NET, das Entwicklern die Möglichkeit bietet, sowohl Client- als auch Servercode in C# zu schreiben. Dies ermöglicht eine engere Integration zwischen Frontend und Backend, insbesondere im Vergleich zu traditionellen JavaScript-basierten Webframeworks wie React oder Angular. Ein entscheidender Vorteil von Blazor ist, dass Entwickler bis zu 99% ihres Codes in C# schreiben können, was eine konsistente Codebasis und eine stärkere Integration zwischen Server und Client ermöglicht. In dieser Hinsicht ist Blazor WebAssembly ein weiteres leistungsstarkes Werkzeug, das speziell für die Entwicklung von Single-Page-Applications (SPA) gedacht ist, wobei die .NET-Assemblies und die .NET-Laufzeitumgebung direkt im Browser ausgeführt werden.

Blazor WebAssembly bietet verschiedene Vorteile, wie etwa die Möglichkeit, Apps offline auszuführen, was die Unabhängigkeit von einer ständigen Netzwerkverbindung ermöglicht. Zudem kann eine Blazor WebAssembly-Anwendung auf einer statischen Website oder einem Content-Delivery-Netzwerk (CDN) gehostet werden, was die Skalierbarkeit und die Performance der Anwendung verbessert. Diese Eigenschaften machen Blazor zu einer ausgezeichneten Wahl für moderne Webanwendungen, die eine hohe Interaktivität erfordern, aber ohne die Komplexität und den Overhead traditioneller JavaScript-Frameworks.

Blazor Hosting-Modelle

Blazor bietet verschiedene Hosting-Modelle, die für unterschiedliche Anwendungsszenarien von Bedeutung sind. Dazu gehören Blazor WebAssembly, Blazor Server und Blazor Hybrid.

  • Blazor WebAssembly: Bei diesem Modell wird die gesamte Anwendung im Browser ausgeführt. Der Code wird heruntergeladen und im Browser-Caching gespeichert, was dazu führt, dass der Benutzer die Anwendung auch ohne Internetverbindung nutzen kann. Dies bietet den Vorteil einer höheren Skalierbarkeit, da die Verarbeitungslogik größtenteils auf den Client übertragen wird. Dies eignet sich besonders gut für Progressive Web Apps (PWAs), die auch offline funktionieren müssen.

  • Blazor Server: Bei Blazor Server wird der Code auf dem Server ausgeführt, und die Benutzeroberfläche wird in Echtzeit über SignalR (WebSocket) an den Browser des Benutzers gesendet. Dies hat den Vorteil einer schnellen Ladezeit und vollständigen Unterstützung für .NET-APIs. Allerdings ist der Benutzer auf eine stabile Internetverbindung angewiesen, da alle Interaktionen in Echtzeit zwischen Server und Client ausgetauscht werden müssen.

  • Blazor Hybrid: In diesem Modell wird Blazor in einer nativen Client-App, beispielsweise einer mobilen Anwendung oder einer Windows-Anwendung, ausgeführt. Der Vorteil hier ist der Zugang zu nativen Client-Funktionen, was eine bessere Benutzererfahrung und eine tiefere Integration mit der zugrunde liegenden Plattform ermöglicht.

Jedes dieser Modelle hat seine eigenen Vor- und Nachteile, und die Wahl des richtigen Modells hängt von den Anforderungen der spezifischen Anwendung ab. Blazor WebAssembly eignet sich gut für Anwendungen, die offline funktionieren und skalierbar sein müssen, während Blazor Server eine vollständige .NET-Unterstützung und eine schnelle Ladezeit bietet.

Bereitstellung von Blazor WebAssembly

Blazor WebAssembly-Anwendungen können auf zwei Arten bereitgestellt werden. Eine Möglichkeit besteht darin, eine reine Blazor WebAssembly-Client-Anwendung zu erstellen und ihre Dateien auf einem beliebigen statischen Webserver zu hosten. Dies kann auf Plattformen wie Azure Static Web Apps oder anderen ähnlichen Hosting-Diensten erfolgen. Alternativ kann eine Blazor WebAssembly-Anwendung als Teil einer serverseitigen Webanwendung bereitgestellt werden, bei der die Blazor-Anwendung und die damit verbundenen Dienste auf demselben Server gehostet werden.

Ein weiterer wichtiger Aspekt bei der Bereitstellung von Blazor WebAssembly-Anwendungen ist die Browserkompatibilität. Während Blazor WebAssembly theoretisch Zugriff auf alle .NET-APIs hat, gibt es in der Praxis Einschränkungen, da Blazor innerhalb einer Browser-Sandbox läuft. Wenn eine nicht unterstützte API verwendet wird, führt dies zu einer PlatformNotSupportedException. Um diese Einschränkungen zu erkennen, bietet Microsoft eine Plattformkompatibilitätsanalyse, die Entwicklern hilft, unzulässige API-Aufrufe frühzeitig zu identifizieren.

Wichtige Überlegungen und Best Practices

Blazor WebAssembly ermöglicht die Entwicklung von Anwendungen, die modern, schnell und skalierbar sind. Dennoch müssen Entwickler einige wichtige Überlegungen berücksichtigen:

  1. Ressourcenverbrauch und Ladezeiten: Blazor WebAssembly lädt die gesamte .NET-Laufzeit und die Assemblies in den Browser. Dies kann zu längeren Ladezeiten führen, besonders bei größeren Anwendungen. Entwickler sollten daher sorgfältig optimieren und dafür sorgen, dass nur die benötigten Komponenten geladen werden.

  2. Kompatibilität mit alten Browsern: Obwohl Blazor WebAssembly in allen modernen Browsern funktioniert, gibt es Einschränkungen bei älteren Versionen oder weniger gängigen Browsern. Entwickler sollten sicherstellen, dass ihre Anwendungen auf den gängigsten Browsern getestet werden.

  3. Fehlerbehandlung und Testing: Da Blazor WebAssembly Code im Browser ausführt, müssen Entwickler sicherstellen, dass der Code gut getestet und robust ist, um mögliche Fehler, die nur im Client auftreten, schnell zu identifizieren und zu beheben.

  4. Caching und Offline-Funktionalität: Einer der größten Vorteile von Blazor WebAssembly ist die Möglichkeit, Offline-Funktionalität zu nutzen. Dies erfordert jedoch eine sorgfältige Planung des Caching und des lokalen Speichers. Blazor WebAssembly kann mit Service Workern und anderen Web-Technologien kombiniert werden, um die Anwendung offline funktionsfähig zu machen, was in modernen Webanwendungen zunehmend an Bedeutung gewinnt.

Blazor WebAssembly ist ein leistungsstarkes Werkzeug, das Entwicklern eine neue Perspektive auf die Entwicklung von Webanwendungen bietet, die sowohl robust als auch modern sind. Dennoch erfordert der Umgang mit Blazor ein gewisses Maß an Verständnis der zugrunde liegenden Technologien, wie etwa WebAssembly und die Besonderheiten der Client-Server-Architektur. Wer Blazor effektiv einsetzen möchte, sollte sich nicht nur auf die reinen Grundlagen konzentrieren, sondern auch fortgeschrittene Techniken wie Offline-Support, Performance-Optimierung und Plattformkompatibilität berücksichtigen.

Wie integriert man .NET MAUI Apps mit Blazor und nativen Plattformen unter Berücksichtigung von API-Services und MVVM-Architektur?

Die Integration von .NET MAUI-Anwendungen mit Blazor und nativen Plattformen erfordert ein tiefes Verständnis sowohl der Web-API-Entwicklung als auch der Anwendungsarchitektur. Zunächst wird die Konfiguration eines Webservices mittels ASP.NET Core minimal APIs verdeutlicht, wobei insbesondere die Einbindung von Swagger zur Dokumentation der API-Endpunkte hervorzuheben ist. Swagger stellt im Entwicklungsumfeld eine interaktive Oberfläche bereit, die es ermöglicht, API-Routen zu testen und die Rückgabe von Entitäten wie Kategorien in Echtzeit zu überprüfen. Diese Dokumentationsmöglichkeit ist essenziell, um sowohl die Korrektheit der API als auch deren Nutzerfreundlichkeit zu gewährleisten.

Die Definition der Endpunkte folgt einem klaren Muster: GET-Methoden zur Abfrage von Kategorien und einzelnen Kategorie-Details, POST zum Anlegen neuer Einträge, PUT zur Aktualisierung vorhandener und DELETE zum Entfernen von Datensätzen. Dabei werden jeweils spezifische Statuscodes angegeben, die das Ergebnis der Operationen präzise signalisieren, was eine REST-konforme Gestaltung des Webservices unterstreicht. Die Verwendung von Entity Framework Core in Kombination mit dem NorthwindContext erlaubt eine saubere Datenzugriffslogik, die direkt in den Lambda-Ausdrücken der Routenhandlers implementiert ist.

Die Anpassung der .NET MAUI-App für den Zugriff auf diesen Webservice verlangt eine explizite Konfiguration der Netzwerksicherheitseinstellungen, insbesondere auf iOS und Android. So wird in der iOS-App die Info.plist modifiziert, um unsichere HTTP-Verbindungen zuzulassen, was bei der Entwicklung und lokalen Tests häufig unvermeidbar ist. Für Android wird eine Network Security Configuration erstellt, die über die Manifest-Datei eingebunden wird, um klartextbasierte Verbindungen an spezifische IP-Adressen zu erlauben. Diese Schritte sind essenziell, um eine reibungslose Kommunikation zwischen Client und Server in Testumgebungen zu ermöglichen.

Darüber hinaus ist die Implementierung des MVVM (Model-View-ViewModel)-Patterns von zentraler Bedeutung. Diese Architektur trennt die Darstellung (View) von der Logik (ViewModel) und den Datenmodellen (Model), was die Testbarkeit und Wartbarkeit der Anwendung erheblich verbessert. Das Model repräsentiert die Datenstrukturen, wie beispielsweise die Klasse Category, während das ViewModel die Geschäftslogik und die Präsentationslogik zusammenfasst. Die View bindet an das ViewModel, wodurch eine dynamische und reaktive Benutzeroberfläche entsteht, die auf Änderungen der Daten automatisch reagiert.

Die Realisierung von MVVM in .NET MAUI ist mit erheblichem Aufwand verbunden, da Property-Changed-Benachrichtigungen implementiert werden müssen. Hierbei erleichtert das CommunityToolkit.Mvvm durch Source Generators die Entwicklung erheblich. Indem man ViewModel-Klassen von ObservableObject erben lässt und die [ObservableProperty]-Attribute nutzt, werden die notwendigen Benachrichtigungen automatisch generiert. Falls eine Klasse bereits von einer anderen Basisklasse erbt, kann sie stattdessen mit dem [INotifyPropertyChanged]-Attribut dekoriert werden. Diese Techniken reduzieren Boilerplate-Code und fördern sauberen, wartbaren Code.

Ein weiterer wichtiger Aspekt ist die differenzierte Darstellung der Benutzeroberflächen je nach Plattform oder Szenario. So kann auf einem Desktop eine horizontale Carousel-Ansicht mit Bildern realisiert werden, während auf mobilen Geräten eine einfache vertikale Liste angezeigt wird. Die Flexibilität von MVVM ermöglicht es, solche Anpassungen ohne Änderung der Geschäftslogik vorzunehmen.

Neben der technischen Implementierung ist das Verständnis der REST-Prinzipien, insbesondere der Bedeutung von Statuscodes und HTTP-Methoden, essenziell für eine konsistente API-Gestaltung. Ebenso wichtig ist das Bewusstsein für Sicherheitsaspekte bei der Kommunikation zwischen Client und Server, insbesondere in produktiven Umgebungen, wo HTTPS-Verbindungen unabdingbar sind. Das Zulassen von unsicheren Verbindungen sollte ausschließlich in kontrollierten Entwicklungsumgebungen erfolgen.

Darüber hinaus ist das Wissen über den Lebenszyklus und die Konfigurationsmöglichkeiten von .NET MAUI-Anwendungen unabdingbar, um Plattformunterschiede gezielt zu adressieren. Die konsequente Trennung von Geschäftslogik und UI durch MVVM bildet die Grundlage für skalierbare und erweiterbare Anwendungen, die sowohl auf mobilen Geräten als auch auf Desktops konsistent funktionieren.

Wie kann ein Webdienst mit Minimal APIs abgesichert und über JWT authentifiziert werden?

Minimal APIs in ASP.NET Core ermöglichen es, Webdienste schlank und direkt zu definieren, ohne den Overhead klassischer Controllerstrukturen. Doch mit der Vereinfachung der Oberfläche wächst auch die Verantwortung, Sicherheitsmechanismen explizit und korrekt zu implementieren. In der hier vorgestellten Architektur wird die Authentifizierung über JSON Web Tokens (JWT) realisiert, ein Verfahren, das sich durch seine Kompaktheit und Plattformunabhängigkeit auszeichnet.

Die Konfiguration beginnt mit der Definition einer einfachen Route:

csharp
app.MapGet("/", () => "Hello World!").ExcludeFromDescription();

Diese Route bleibt öffentlich zugänglich. Demgegenüber steht eine geschützte Route:

csharp
app.MapGet("/secret", (ClaimsPrincipal user) => $"Welcome, {user.Identity?.Name ?? "secure user"}. The secret ingredient is love.") .RequireAuthorization();

Diese Route erlaubt den Zugriff nur autorisierten Benutzern. Die Identität wird über das ClaimsPrincipal-Objekt ermittelt, das durch das vorherige JWT-basiertes Authentifizierungsverfahren bereitgestellt wird.

Im nächsten Schritt wird über die Kommandozeile ein lokaler JWT generiert:

sql
dotnet user-jwts create

Die Ausgabe liefert eine eindeutige ID, den Namen des Benutzers und den vollständigen Token. Dieser Token ist notwendig, um später geschützte Endpunkte wie /secret aufzurufen.

Um den generierten Token im Detail zu analysieren, kann folgender Befehl verwendet werden:

lua
dotnet user-jwts print <ID> --show-all

Die Ausgabe zeigt den verwendeten Authentifizierungsschema-Typ (Bearer), die autorisierten Zieladressen (Audiences), den Aussteller (Issuer), sowie die Kodierung des Tokens, unterteilt in Header, Payload und Signatur. Ein typischer Header sieht so aus:

json
{"alg":"HS256","typ":"JWT"}

Der Payload enthält beispielsweise:

json
{ "unique_name":"markjprice", "sub":"markjprice", "aud":["http://localhost:5090", "https://localhost:5091"], "iss":"dotnet-user-jwts", ... }

Wichtig ist die zeitliche Gültigkeit (nbf, exp, iat) und die Zielgruppenadressierung über aud. Nur Anfragen, die von einem der hier angegebenen Ursprünge kommen, werden als gültig betrachtet.

Die Konfigurationsdatei appsettings.Development.json wird um einen Abschnitt Authentication erweitert. Hier werden gültige Audiences und der Issuer für das Bearer-Schema definiert:

json
"Authentication": {
"Schemes": { "Bearer": { "ValidAudiences": [ "http://localhost:30225", "https://localhost:44344", "http://localhost:5090", "https://localhost:5091" ], "ValidIssuer": "dotnet-user-jwts" } } }

Nach dem Start des Webdienstes (im HTTPS-Profil) liefert der Zugriff auf /secret ohne gültigen Token erwartungsgemäß eine HTTP-401-Antwort.

Für automatisierte Tests und Integrationstests bietet sich die Verwendung einer .http-Datei an, zum Beispiel webapi-secure-request.http:

bash
GET https://localhost:5091/secret Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Die Antwort lautet bei erfolgreicher Authentifizierung:

csharp
Welcome, markjprice. The secret ingredient is love.

Wird kein oder ein ungültiger Token gesendet, erfolgt ein direkter Zugriffsschutz durch Rückgabe des HTTP-Statuscodes 401.

Wichtig ist zu verstehen, dass ein JWT nicht nur Zugang gewährt, sondern durch seinen Aufbau und die enthaltenen Claims auch zur Identifikation, Rollenprüfung und Autorisierung dient. Es ersetzt klassische Sessions vollständig, bringt aber auch neue Herausforderungen in Bezug auf Sicherheit, Token-Gültigkeit und Verwaltung.

Neben der technischen Implementierung ist die Gestaltung der Token-Inhalte entscheidend. Claims sollten möglichst sparsam, aber gezielt gewählt werden. Rollen oder benutzerdefinierte Claims können verwendet werden, um feingranulare Zugriffskontrollen zu etablieren.

Besonders relevant ist auch die Definition der audiences. Ein JWT ist nur für die darin enthaltenen Adressen gültig. Das verhindert die missbräuchliche Verwendung desselben Tokens auf anderen Systemen. Ebenso ist der issuer zentraler Bestandteil der Gültigkeitsprüfung – nur Token, die vom als vertrauenswürdig konfigurierten Aussteller stammen, werden akzeptiert.

Ein häufig übersehener Aspekt ist das Logging von HTTP-Anfragen und -Antworten. In d