Webprogrammierung mit Blazor WebAssembly, Teil 1: Web-API-Aufrufe und Rendering

Aufruf von Web APIs

Inhaltsverzeichnis

Ein erstes Beispiel, wie man den generierten Proxy-Code des Backends aufruft, sieht man schon in der Implementierung von About.Razor in Abbildung 3. Die generierte Klasse MiracleListAPI.MiracleListProxy wird per Dependency Injection mit @inject in die Razor Component injiziert. Im Lebenszyklusereignis OnInitializedAsync() erfolgt dann der Aufruf der entsprechend generierten Methode mit await, zum Beispiel await proxy.AboutAsync(). Voraussetzung für die Dependency Injection ist wie üblich in .NET Core/.NET 5, dass die Klasse im Dependency-Injection-Container registriert wurde. Das erfolgt in Blazor-WebAssembly-Anwendungen in der Program.cs-Datei. Das Listing 1 gibt den Kern der Startklasse wieder.

Listing 1: Ausschnitt aus Program.cs

public class Program
 {
 var builder = WebAssemblyHostBuilder.CreateDefault(args);
 builder.RootComponents.Add<App>("app");
 IServiceCollection services = builder.Services;  
 services.AddSingleton(new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
 services.AddScoped<MiracleListAPI.MiracleListProxy>();
 await builder.Build().RunAsync();
}

Wer keine generierte Proxyklasse verwenden will oder kann, würde direkt die Methoden der Klasse HttpClient nutzen, zum Beispiel:

@inject HttpClient Http
var daten = await Http.GetJsonAsync<List<Typ>>("https://Server/URL");

Die Web-API-Operation /About, die die Methode AboutAsync() in Abbildung 3 aufruft, ist eine der wenigen Methoden im MiracleList-Backend, die keine Authentifizierung erfordert, denn diese Operation liefert nur allgemeine Daten über das Backend-System. Für einen konkreten Zugriff auf die Datenbank ist eine Authentifizierung erforderlich, wobei dieses auch in Schulungen verwendete Backend bewusst ein extrem gnädiges Benutzerverwaltungssystem besitzt: Wenn es zu einer Kombination aus Benutzernamen und Kennwort noch kein Konto gibt, wird automatisch ein solches mit Testdaten erstellt. Zur Authentifizierung brauchen Entwickler allerdings eine Client-ID, die zuvor zu beantragen ist (s. o.).

An dieser frühen Phase der Entwicklung soll noch keine Anmeldemaske erstellt werden. Vielmehr hinterlegen Entwickler die Anmeldedaten statisch in einer Klasse, sodass die Anmeldung bei jedem Seitenaufruf automatisch erfolgen kann (s. Listing 2). In der dritten Folge dieses Tutorials wird die Klasse AuthenticationManager dann zum AuthenticationStateProvider für Blazor ausgebaut.

Listing 2: Statisch hinterlegte Authentifizierung

using MiracleListAPI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Web
{
 /// <summary>
 /// Authentifizierung zum Debugging
 /// </summary>
 public class AuthenticationManager
 {
  MiracleListAPI.MiracleListProxy proxy { get; set; }

  public AuthenticationManager(MiracleListAPI.MiracleListProxy proxy)
  {
   this.proxy = proxy;
  }

  public const string DebugUser = "iXTutorial";
  public const string DebugPassword = "Sehr+Geheim"; // :-)
  public const string ClientID = "0000-0000-0000";
  public LoginInfo CurrentLoginInfo = null;
  public string Token { get { return CurrentLoginInfo?.Token; } }

  public async Task<bool> Login()
  {
   var l = new LoginInfo() { Username = AuthenticationManager.DebugUser, Password = AuthenticationManager.DebugPassword, ClientID = AuthenticationManager.ClientID };
   try
   {
    CurrentLoginInfo = await proxy.LoginAsync(l);
    if (String.IsNullOrEmpty(CurrentLoginInfo.Token))
    {
     Console.WriteLine("Anmeldung NICHT erfolgreich: " + this.CurrentLoginInfo.Username);
     return false;
    }
    else
    {
     Console.WriteLine("Anmeldung erfolgreich: " + this.CurrentLoginInfo.Username);
     return true;
    }
   }
   catch (Exception ex)
   {
    Console.WriteLine("Anmeldefehler: " + ex.Message);
    return false;
   }
  }
 }
}

Die /Login-Operation auf dem Server erwartet als Datenstruktur den Typ LoginInfo mit Benutzername, Kennwort und Client-ID. Anwender sollten unbedingt in Listing 2 die dort abgedruckte, nicht funktionierende Client-ID durch eine beantragte persönliche Client-ID ersetzen. Wenn die Client-ID korrekt war und Benutzername beziehungsweise Kennwort zueinander passten (oder neu waren), erhalten Aufrufer wieder ein LoginInfo-Objekt, in dem sich nun ein Token befindet, das bei jedem Aufruf einer Web-API-Operation zu übergeben ist. Die Übergabe erfolgt in Wirklichkeit im HTTP-Header; das verbirgt der von NSwag Studio generierte Wrapper. Nicht umsonst, hat er doch für rund ein Dutzend Web-API-Operationen bereits über 1400 Zeilen C#-Programmcode erzeugt, die den Fingerkuppenabrieb deutlich reduzieren und das Budget der Auftraggeber schonen.

Die Klasse AuthenticationManager nutzt die aus Konsolenanwendungen bekannte statische Methode Console.WriteLine() für Protokollausgaben. In Blazor WebAssembly landen diese Ausgaben in der Entwicklerkonsole des Webbrowsers. In Blazor Server würden sie aber auf dem Webserver landen. Außerdem ist zu beachten, dass Console.WriteLine() in C# nicht wie console.log in JavaScript für Objektparameter die Werte der einzelnen Objekteigenschaften ausgibt, sondern in den meisten Fällen nur den Klassennamen ausgibt; Entwickler müssen selbst für einen "Dump" des Objekts sorgen.

In Listing 2 ist noch zu beachten, dass hier die Dependency Injection weder mit @inject noch mit [Inject] erfolgen kann, da es sich um keine Razor Component, sondern nur um eine "einfache" Klasse handelt. Hier bekommt man die Instanzen per Konstruktorparameter. Damit das funktioniert, ist diese Klasse auch dem Dependency-Injection-Container bekannt zu machen und deren Instanzen dann über den Dependency-Injection-Container zu beschaffen:

services.AddScoped<AuthenticationManager>();