März 2019

Band 34, Nummer 3

[Webentwicklung]

Full Stack C# mit Blazor

Von Jonathan C. Miller | März 2019 | Code abrufen

Blazor, das experimentelle Framework von Microsoft, das C# in den Browser bringt, ist das fehlende Teil im C#-Puzzle. Ein C#-Programmierer kann heute Desktop- und serverseitige Web-Apps sowie Cloud-, Smartphone-, Tablet-, Smartwatch-, TV- und IoT-Anwendungen schreiben. Blazor ist das letzte Teil des Puzzles, weil C#-Entwickler nun Code und Geschäftslogik direkt im Browser des Benutzers teilen können. Dies ist eine leistungsstarke Fähigkeit und bedeutet eine gigantische Produktivitätssteigerung für C#-Entwickler.

In diesem Artikel stelle ich einen häufigen Anwendungsfall für Codefreigabe vor. Ich zeige, wie Validierungslogik gemeinsam von einem Blazor-Client und einer WebAPI-Serveranwendung genutzt werden kann. Heute wird erwartet, dass Sie die Eingabe nicht nur auf dem Server, sondern auch im Clientbrowser überprüfen. Benutzer moderner Webanwendungen erwarten Feedback nahezu in Echtzeit. Die Tage, an denen wir ein langes Formular ausfüllen und auf „Absenden“ klicken mussten, nur damit ein Fehler in rot angezeigt wurde, liegen zumeist hinter uns.

Eine Blazor Webanwendung, die im Browser ausgeführt wird, kann Code mit einem C#-Back-End-Server gemeinsam verwenden. Sie können Ihre Logik in einer freigegebenen Bibliothek speichern und sie auf dem Front- und Back-End nutzen. Dies bringt viele Vorteile mit sich. Sie können alle Regeln an einem Ort speichern und sicher sein, dass sie nur dort aktualisiert werden müssen. Sie wissen, dass sie wirklich gleich funktionieren, weil sie aus demselben Code bestehen. Sie sparen viel Zeit beim Testen und bei der Problembehandlung im Vergleich zu Szenarien, in denen die Client- und Serverlogik nicht immer gleich ist.

Am bemerkenswertesten ist vielleicht, dass Sie eine Bibliothek für die Validierung sowohl auf dem Client als auch auf dem Server verwenden können. Traditionell zwingt ein JavaScript-Front-End Entwickler dazu, zwei Versionen von Validierungsregeln zu schreiben: eine in JavaScript für das Front-End und eine weitere in der Sprache des Back-Ends. Versuche, dieses Ungleichgewicht zu lösen, beinhalten komplizierte Regelframeworks und zusätzliche Abstraktionsschichten. Mit Blazor wird die gleiche .NET Core-Bibliothek auf dem Client und dem Server ausgeführt.

Blazor ist immer noch ein experimentelles Framework, aber es entwickelt sich schnell weiter. Bevor Sie dieses Beispiel erstellen, stellen Sie sicher, dass Sie die richtige Version von Visual Studio, des .NET Core SDK und der Blazor-Sprachdienste installiert haben. Machen Sie sich mit den ersten Schritten auf blazor.net vertraut.

Erstellen einer neuen Blazor-Anwendung

Zunächst erstellen wir eine neue Blazor-Anwendung. Klicken Sie im Dialogfeld „Neues Projekt“ auf „ASP.NET Core-Webanwendung“, klicken Sie auf „OK“, und wählen Sie dann das Blazor-Symbol in dem Dialogfeld aus, das in Abbildung1 gezeigt wird. Klicken Sie auf „OK“. Dadurch wird die Blazor-Standardbeispielanwendung erstellt. Wenn Sie bereits mit Blazor experimentiert haben, wird Ihnen diese Standardanwendung vertraut sein.

Auswählen einer Blazor-Anwendung
Abbildung 1: Auswählen einer Blazor-Anwendung

Die gemeinsame Logik zur Validierung von Geschäftsregeln wird für ein neues Registrierungsformular gezeigt. Abbildung 2 zeigt ein einfaches Formular mit Feldern für Vorname, Nachname, E-Mail-Adresse und Telefonnummer. In diesem Beispiel wird sichergestellt, dass alle Felder ausgefüllt werden (da es sich um Pflichtfelder handelt), dass die Namensfelder eine maximale Länge aufweisen und dass die Felder für E-Mail-Adresse und Telefonnummer im richtigen Format vorliegen. Es wird eine Fehlermeldung unter jedem Feld angezeigt, und diese Meldungen werden während der Eingabe durch den Benutzer aktualisiert. Schließlich wird die Schaltfläche „Register“ (Registrieren) nur aktiviert, wenn keine Fehler vorliegen.

Registrierungsformular
Abbildung 2: Registrierungsformular

Freigegebene Bibliothek

Der gesamte Code, der zwischen dem Server und dem Blazor-Client geteilt werden muss, wird in einem separaten freigegebenen Bibliotheksprojekt gespeichert. Die freigegebene Bibliothek enthält die Modellklasse und eine sehr einfache Validierungsengine. Die Modellklasse enthält die Datenfelder für das Registrierungsformular. Das sieht folgendermaßen aus:

public class RegistrationData : ModelBase
{
  [RequiredRule]
  [MaxLengthRule(50)]
  public String FirstName { get; set; }
 
  [RequiredRule]
  [MaxLengthRule(50)]
  public String LastName { get; set; }
 
  [EmailRule]
  public String Email { get; set; }
 
  [PhoneRule]
  public String Phone { get; set; }
 
}

Die RegistrationData-Klasse erbt von einer ModelBase-Klasse, die die gesamte Logik enthält, die verwendet werden kann, um die Regeln zu validieren und Fehlermeldungen zurückzugeben, die an die Blazor-Seite gebunden sind. Jedes Feld ist mit Attributen versehen, die Validierungsregeln zugeordnet sind. Ich habe mich entschieden, ein sehr einfaches Modell zu erstellen, das sich sehr ähnlich anfühlt wie das EF-Datenanmerkungsmodell (Entity Framework). Die gesamte Logik für dieses Modell ist in der freigegebenen Bibliothek enthalten.

Die ModelBase-Klasse enthält Methoden, die von der Blazor-Clientanwendung oder der Serveranwendung verwendet werden können, um festzustellen, ob Validierungsfehler vorliegen. Außerdem wird ein Ereignis ausgelöst, wenn das Modell geändert wird, damit der Client die Benutzeroberfläche aktualisieren kann. Jede Modellklasse kann von dieser Klasse erben und erhält automatisch die gesamte Logik der Validierungsengine.

Ich beginne damit, zuerst eine neue ModelBase-Klasse innerhalb des SharedLibrary-Projekts zu erstellen:

public class ModelBase
{
}

Fehler und Regeln

Nun füge ich der ModelBase-Klasse ein privates Wörterbuch hinzu, das eine Liste von Validierungsfehlern enthält. Das _errors-Wörterbuch wird durch den Feldnamen und dann durch den Regelnamen codiert. Der Wert ist die eigentliche Fehlermeldung, die angezeigt werden soll. Diese Konfiguration macht die Feststellung von Validierungsfehlern für ein bestimmtes Feld und das schnelle Abrufen der Fehlermeldungen einfach. Dies ist der Code:

private Dictionary<String, Dictionary<String, String>> _errors =
  new Dictionary<string, Dictionary<string, string>>();

Nun füge ich die AddError-Methode für die Eingabe von Fehlern in das interne Fehlerwörterbuch hinzu. AddError verfügt über Parameter für fieldName, ruleName und errorText. Die Methode durchsucht das interne Fehlerwörterbuch und entfernt Einträge, wenn sie bereits vorhanden sind, und fügt dann den neuen Fehlereintrag hinzu, wie im folgenden Code gezeigt:

private void AddError(String fieldName, String ruleName, String errorText)
{
  if (!_errors.ContainsKey(fieldName)) { _errors.Add(fieldName,
    new Dictionary<string, string>()); }
  if (_errors[fieldName].ContainsKey(ruleName))
     { _errors[fieldName].Remove(ruleName); }
  _errors[fieldName].Add(ruleName, errorText);
  OnModelChanged();
}

Schließlich füge ich die RemoveError-Methode hinzu, die die Parameter fieldName und ruleName akzeptiert und das interne Fehlerwörterbuch nach einem passenden Fehler durchsucht und diesen entfernt. Dies ist der Code:

private void RemoveError(String fieldName, String ruleName)
{
  if (!_errors.ContainsKey(fieldName)) { _errors.Add(fieldName,
    new Dictionary<string, string>()); }
  if (_errors[fieldName].ContainsKey(ruleName))
     { _errors[fieldName].Remove(ruleName);
    OnModelChanged();
  }
}

Der nächste Schritt besteht darin, die CheckRules-Funktionen hinzuzufügen, die die Aufgabe übernehmen, die dem Modell zugeordneten Validierungsregeln zu finden und auszuführen. Es gibt zwei verschiedene CheckRules-Funktionen: Eine Funktion, der ein Parameter fehlt und die alle Regeln für alle Felder überprüft, und eine zweite Funktion, die einen fieldName-Parameter besitzt und nur ein bestimmtes Feld überprüft. Diese zweite Funktion wird verwendet, wenn ein Feld aktualisiert wird und die Regeln für dieses Feld sofort überprüft werden.

Die Funktion CheckRules verwendet Reflektion, um die Liste der Attribute zu finden, die einem Feld zugeordnet sind. Anschließend testet sie jedes Attribut, um festzustellen, ob es sich um einen IModelRule-Typ handelt. Wenn eine IModelRule gefunden wird, wird die Validate-Methode aufgerufen und das Ergebnis zurückgegeben, wie in Abbildung 3 gezeigt.

Abbildung 3: Die CheckRules-Funktion

public void CheckRules(String fieldName)
{
  var propertyInfo = this.GetType().GetProperty(fieldName);
  var attrInfos = propertyInfo.GetCustomAttributes(true);
  foreach (var attrInfo in attrInfos)
  {
    if (attrInfo is IModelRule modelrule)
    {
      var value = propertyInfo.GetValue(this);
      var result = modelrule.Validate(fieldName, value);
      if (result.IsValid)
      {
        RemoveError(fieldName, attrInfo.GetType().Name);
      }
      else
      {
        AddError(fieldName, attrInfo.GetType().Name, result.Message);
      }
    }
  }
}
 
public bool CheckRules()
{
  foreach (var propInfo in this.GetType().GetProperties(
    System.Reflection.BindingFlags.Public |
      System.Reflection.BindingFlags.Instance))
    CheckRules(propInfo.Name);
 
  return HasErrors();
}

Im nächsten Schritt füge ich die Errors-Funktion hinzu. Diese Funktion nimmt einen Feldnamen als Parameter an und gibt eine Zeichenfolge zurück, die die Liste der Fehler für dieses Feld enthält. Sie verwendet das interne _errors-Wörterbuch, um festzustellen, ob für dieses Feld Fehler vorliegen, wie hier gezeigt:

public String Errors(String fieldName)
{
  if (!_errors.ContainsKey(fieldName)) { _errors.Add(fieldName,
    new Dictionary<string, string>()); }
  System.Text.StringBuilder sb = new System.Text.StringBuilder();
  foreach (var value in _errors[fieldName].Values)
    sb.AppendLine(value);
 
  return sb.ToString();
}

Nun muss ich die HasErrors-Funktion hinzufügen, die TRUE zurückgibt, wenn es Fehler für ein Feld des Modells gibt. Diese Methode wird vom Client verwendet, um zu bestimmen, ob die Schaltfläche „Register“ (Registrieren) aktiviert werden soll. Sie wird auch vom WebAPI-Server verwendet, um festzustellen, ob die eingehenden Modelldaten fehlerhaft sind. Hier ist der Funktionscode:

public bool HasErrors()
{
  foreach (var key in _errors.Keys)
    if (_errors[key].Keys.Count > 0) { return true; }
  return false;
}

Werte und Ereignisse

Es ist an der Zeit, die GetValue-Methode hinzuzufügen, die einen fieldname-Parameter annimmt und Reflektion verwendet, um das Feld im Modell zu finden und seinen Wert zurückzugeben. Dieser wird vom Blazor-Client verwendet, um den aktuellen Wert abzurufen und im Eingabefeld anzuzeigen, wie hier gezeigt:

public String GetValue(String fieldName)
{
  var propertyInfo = this.GetType().GetProperty(fieldName);
  var value = propertyInfo.GetValue(this);
 
  if (value != null) { return value.ToString(); }
  return String.Empty;           
}

Fügen Sie nun die SetValue-Methode hinzu. Sie verwendet Reflektion, um das Feld im Modell zu finden und seinen Wert zu aktualisieren. Sie löst dann die Methode CheckRules aus, die alle Regeln für das Feld überprüft. Sie wird im Blazor-Client verwendet, um den Wert zu aktualisieren, wenn der Benutzer eine Eingabe im Eingabetextfeld vornimmt. Dies ist der Code:

public void SetValue(String fieldName, object value)
{
  var propertyInfo = this.GetType().GetProperty(fieldName);
  propertyInfo.SetValue(this, value);
  CheckRules(fieldName);
}

Schließlich füge ich das Ereignis für ModelChanged hinzu, das ausgelöst wird, wenn ein Wert für das Modell geändert wurde oder eine Validierungsregel hinzugefügt oder aus dem internen Fehlerwörterbuch entfernt wurde. Der Blazor-Client lauscht auf dieses Ereignis und aktualisiert die Benutzeroberfläche, wenn es ausgelöst wird. Dies bewirkt, dass die angezeigten Fehler aktualisiert werden, wie im folgenden Code gezeigt:

public event EventHandler<EventArgs> ModelChanged;
 
protected void OnModelChanged()
{
  ModelChanged?.Invoke(this, new EventArgs());
}

Diese Validierungsengine ist zugegebenermaßen ein sehr einfacher Entwurf mit vielen Verbesserungsmöglichkeiten. In einer Geschäftsanwendung in der Produktion wäre es sinnvoll, Schweregrade für Fehler (wie „Information“, „Warnung“ und „Fehler“) zu verwenden. In bestimmten Szenarien wäre es hilfreich, wenn die Regeln dynamisch aus einer Konfigurationsdatei geladen werden könnten, ohne den Code ändern zu müssen. Ich empfehle nicht, dass Sie Ihre eigene Validierungsengine erstellen. Es gibt eine Vielzahl anderer Möglichkeiten. Diese Engine ist so konzipiert, dass sie gut genug ist, um ein Beispiel aus der Praxis zu demonstrieren, aber auch so einfach, dass sie in diesen Artikel passt und leicht zu verstehen ist.

Erstellen der Regeln

An diesem Punkt ist eine RegistrationData-Klasse vorhanden, die die Formularfelder enthält. Die Felder der Klasse sind mit Attributen wie RequiredRule und EmailRule versehen. Die RegistrationData-Klasse erbt von einer ModelBase-Klasse, die die gesamte Logik zur Validierung der Regeln und zum Benachrichtigen des Clients über Änderungen enthält. Der letzte Teil der Validierungsengine ist die Regellogik selbst. Diese werde ich im nächsten Schritt untersuchen.

Ich beginne damit, eine neue Klasse namens IModelRule in der SharedLibrary zu erstellen. Diese Regel besteht aus einer einzigen Validierungsmethode, die ein ValidationResult zurückgibt. Jede Regel muss wie hier gezeigt die IModelRule-Schnittstelle implementieren:

public interface IModelRule
{
  ValidationResult Validate(String fieldName, object fieldValue);
}

Nun erstelle ich eine neue Klasse namens ValidationResult in der SharedLibrary, die aus zwei Feldern besteht. Das IsValid-Feld informiert Sie darüber, ob die Regel gültig ist oder nicht, während das Message-Feld die Fehlermeldung enthält, die angezeigt werden soll, wenn die Regel ungültig ist. Dies ist der Code dafür:

public class ValidationResult
{
  public bool IsValid { get; set; }
  public String Message { get; set; }
}

Die Beispielanwendung verwendet vier verschiedene Regeln, die alle öffentliche Klassen sind, die von der Attribute-Klasse erben und die IModelRule-Schnittstelle implementieren.

Jetzt ist es Zeit, die Regeln zu erstellen. Beachten Sie, dass alle Validierungsregeln einfach Klassen sind, die von der Attribute-Klasse erben und die Validate-Methode der IModelRule-Schnittstelle implementieren. Die max-length-Regel in Abbildung 4 gibt einen Fehler zurück, wenn der eingegebene Text länger als die angegebene maximale Länge ist. Die anderen Regeln (für „Required“, „Phone“ und „Email“) funktionieren ähnlich, aber mit unterschiedlicher Logik für die Art der Daten, die sie überprüfen.

Abbildung 4: Die MaxLengthRule-Klasse

public class MaxLengthRule : Attribute, IModelRule
{
  private int _maxLength = 0;
  public MaxLengthRule(int maxLength) { _maxLength = maxLength; }
 
  public ValidationResult Validate(string fieldName, object fieldValue)
  {
    var message = $"Cannot be longer than {_maxLength} characters";
    if (fieldValue == null) { return new ValidationResult() { IsValid = true }; }
 
    var stringvalue = fieldValue.ToString();
    if (stringvalue.Length > _maxLength )
    {
      return new ValidationResult() { IsValid = false, Message = message };
    }
    else
    {
      return new ValidationResult() { IsValid = true };
    }
  }
}

Erstellen des Blazor-Registrierungsformulars

Nachdem die Validierungsengine nun in der freigegebenen Bibliothek vollständig ist, kann sie auf ein neues Registrierungsformular in der Blazor-Anwendung angewendet werden. Ich beginne damit, zunächst einen Verweis auf das SharedLibrary-Projekt aus der Blazor-Anwendung hinzuzufügen. Dies geschieht im Fenster „Projektmappe“ des Dialogfelds „Verweis-Manager“, wie in Abbildung 5 gezeigt.

Hinzufügen eines Verweises auf die freigegebene Bibliothek
Abbildung 5: Hinzufügen eines Verweises auf die freigegebene Bibliothek

Als nächstes füge ich dem NavMenu der Anwendung einen neuen Navigationslink hinzu. Ich öffne die Datei „Shared\NavMenu.cshtml“ und füge der Liste einen neuen Link zum Registrierungsformular hinzu, wie in Abbildung 6 gezeigt.

Abbildung 6: Hinzufügen eines Links zum Registrierungsformular

<div class=@(collapseNavMenu ? "collapse" : null) onclick=@ToggleNavMenu>
  <ul class="nav flex-column">
    <li class="nav-item px-3">
      <NavLink class="nav-link" href="" Match=NavLinkMatch.All>
        <span class="oi oi-home" aria-hidden="true"></span> Home
      </NavLink>
    </li>
    <li class="nav-item px-3">
      <NavLink class="nav-link" href="counter">
        <span class="oi oi-plus" aria-hidden="true"></span> Counter
      </NavLink>
    </li>
    <li class="nav-item px-3">
      <NavLink class="nav-link" href="fetchdata">
        <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
      </NavLink>
    </li>
    <li class="nav-item px-3">
      <NavLink class="nav-link" href="registrationform">
        <span class="oi oi-list-rich" aria-hidden="true"></span> Registration Form
      </NavLink>
    </li>
  </ul>
</div>

Schließlich füge ich die neue Datei „RegistrationForm.cshtml“ dem Ordner „Pages“ hinzu. Dies geschieht mit dem in Abbildung 7 gezeigten Code.

Der CSHTML-Code in Abbildung 7 enthält vier <TextInput>-Felder innerhalb des <form>-Tags. Das <TextInput>-Tag ist eine benutzerdefinierte Blazor-Komponente, die die Datenbindung und die Fehleranzeigelogik für das Feld übernimmt. Die Komponente benötigt nur drei Parameter, um zu funktionieren:

  • Model-Feld: Identifiziert die Klasse, an die sie datengebunden ist.
  • FieldName: Identifiziert den Datenmember, an den die Datenbindung erfolgen soll.
  • DisplayName-Feld: Ermöglicht der Komponente das Anzeigen benutzerfreundlicher Nachrichten.

Abbildung 7: Hinzufügen der Datei „RegistrationForm.cshtml“

@page "/registrationform"
@inject HttpClient Http
@using SharedLibrary
 
<h1>Registration Form</h1>
 
@if (!registrationComplete)
{
  <form>
    <div class="form-group">
      <TextInput Model="model" FieldName="FirstName" DisplayName="First Name" />
    </div>
    <div class="form-group">
      <TextInput Model="model" FieldName="LastName" DisplayName="Last Name" />
    </div>
    <div class="form-group">
      <TextInput Model="model" FieldName="Email" DisplayName="Email" />
    </div>
    <div class="form-group">
      <TextInput Model="model" FieldName="Phone" DisplayName="Phone" />
    </div>
 
    <button type="button" class="btn btn-primary" onclick="@Register"
      disabled="@model.HasErrors()">Register</button>
  </form>
}
else
{
  <h2>Registration Complete!</h2>
}
 
@functions {
  bool registrationComplete = false;
  RegistrationData model { get; set; }
 
  protected override void OnInit()
  {
    base.OnInit();
    model = new RegistrationData() { FirstName =
      "test", LastName = "test", Email = "test@test.com", Phone = "1234567890" };
    model.ModelChanged += ModelChanged;
    model.CheckRules();
  }
 
  private void ModelChanged(object sender, EventArgs e)
  {
    base.StateHasChanged();
  }
 
  async Task Register()
  {
    await Http.PostJsonAsync<RegistrationData>(
      "https://localhost:44332/api/Registration", model);
    registrationComplete = true;
  }
}

Innerhalb des @functions-Blocks der Seite ist nur minimaler Code vorhanden. Die OnInit-Methode initialisiert die Modellklasse mit einigen Testdaten. Sie wird an das ModelChanged-Ereignis gebunden und ruft die CheckRules-Methode auf, um die Regeln zu überprüfen. Der ModelChanged-Handler ruft die Methode base.StateHasChanged auf, um eine Aktualisierung der Benutzeroberfläche zu erzwingen. Die Register-Methode wird aufgerufen, wenn auf die Schaltfläche „Register“ (Registrieren) geklickt wird. Sie sendet die Registrierungsdaten an einen Back-End-WebAPI-Dienst.

Die TextInput-Komponente enthält die Eingabebezeichnung, das Eingabefeld, die Validierungsfehlermeldung und die Logik zum Aktualisieren des Modells, während der Benutzer Eingaben vornimmt. Blazor-Komponenten sind sehr einfach zu schreiben und bieten eine leistungsfähige Möglichkeit, eine Schnittstelle in wiederverwendbare Teile aufzuschlüsseln. Die Parametermember sind mit dem Parameter-Attribut versehen, sodass Blazor weiß, dass es sich um Komponentenparameter handelt.

Das oninput-Ereignis des Eingabefelds wird mit dem OnFieldChanged-Handler verbunden. Es wird jedes Mal ausgelöst, wenn sich die Eingabe ändert. Der OnFieldChanged-Handler ruft dann die SetValue-Methode auf, die bewirkt, dass die Regeln für dieses Feld ausgeführt werden und die Fehlermeldung in Echtzeit aktualisiert wird, während der Benutzer Eingaben vornimmt. Abbildung 8 zeigt den Code.

Abbildung 8: Aktualisieren der Fehlermeldung

@using SharedLibrary
 
<label>@DisplayName</label>
<input type="text" class="form-control" placeholder="@DisplayName"
  oninput="@(e => OnFieldChanged(e.Value))"
  value="@Model.GetValue(FieldName)" />
<small class="form-text" style="color:darkred;">@Model.Errors(FieldName)
  </small>
 
@functions {
 
  [Parameter]
  ModelBase Model { get; set; }
 
  [Parameter]
  String FieldName { get; set; }
 
  [Parameter]
  String DisplayName { get; set; }
 
  public void OnFieldChanged(object value)
  {
    Model.SetValue(FieldName, value);
  }
}

Überprüfung auf dem Server

Die Validierungsengine ist nun verbunden und arbeitet auf dem Client. Der nächste Schritt ist die Verwendung der freigegebenen Bibliothek und der Validierungsengine auf dem Server. Zu diesem Zweck füge ich der Projektmappe zunächst ein weiteres ASP.NET Core-Webanwendungsprojekt hinzu. Dieses Mal wähle ich „API“ anstelle von „Blazor“ im Dialogfeld „Neue ASP.NET Core-Webanwendung“ aus, das in Abbildung 1 gezeigt wird.

Nachdem das neue API-Projekt erstellt wurde, füge ich einen Verweis auf das freigegebene Projekt hinzu, genau wie in der Blazor-Clientanwendung (siehe Abbildung 5). Als nächstes füge ich dem API-Projekt einen neuen Controller hinzu. Der neue Controller akzeptiert den RegistrationData-Aufruf vom Blazor-Client, wie in Abbildung 9 gezeigt. Der Registrierungscontroller wird auf dem Server ausgeführt und ist typisch für einen Back-End-API-Server. Der Unterschied besteht darin, dass er nun die gleichen Validierungsregeln ausführt, die auch auf dem Client ausgeführt werden.

Abbildung 9: Der Registrierungscontroller

[Route("api/Registration")]
[ApiController]
public class RegistrationController : ControllerBase
{
  [HttpPost]
  public IActionResult Post([FromBody] RegistrationData value)
  {
    if (value.HasErrors())
    {
      return BadRequest();
    }
    // TODO: Save data to database
    return Created("api/registration", value);
  }
}

Der Registrierungscontroller verfügt über eine einzige POST-Methode, die die RegistrationData als Wert akzeptiert. Sie ruft die HasErrors-Methode auf, die alle Regeln überprüft und einen booleschen Wert zurückgibt. Wenn Fehler vorliegen, gibt der Controller eine BadRequest-Antwort zurück, andernfalls eine Erfolgsantwort. Ich habe absichtlich den Code weggelassen, der die Registrierungsdaten in einer Datenbank speichern würde, damit ich mich auf das Validierungsszenario konzentrieren kann. Die freigegebene Validierungslogik wird jetzt auf dem Client und dem Server ausgeführt.

Das große Ganze

Dieses einfache Beispiel für die gemeinsame Nutzung der Validierungslogik im Browser und im Back-End hat kaum an der Oberfläche dessen gekratzt, was in einer Full Stack C#-Umgebung möglich ist. Die Magie von Blazor besteht darin, dass es für Heerscharen von C#-Entwicklern nun möglich ist, leistungsstarke, moderne und reaktionsfähige Single-Page-Webanwendungen mit einer minimalen Anlaufphase zu erstellen. Blazor ermöglicht Unternehmen, vorhandenen Code wiederzuverwenden und neu zu verpacken, damit er direkt im Browser ausgeführt werden kann. Die Möglichkeit der gemeinsamen Verwendung von C#-Code im Browser, auf dem Desktop, auf dem Server, in der Cloud und auf mobilen Plattformen wird die Produktivität von Entwicklern erheblich steigern. Blazor wird es Entwicklern auch ermöglichen, den Kunden schneller mehr Funktionen und mehr Geschäftswert zu bieten.

 


Jonathan Miller ist ein Senior Architect. Er entwickelt seit über 10 Jahren Produkte für den Microsoft-Stapel und programmiert für .NET seit dessen Einführung. Miller entwickelt Produkte für den gesamten Stapel und ist Experte für Front-End-Technologien (Windows Forms, Windows Presentation Foundation, Silverlight, ASP.NET, Angular/Bootstrap), Middleware (Windows-Dienste, Web-API) und Back-Ends (SQL Server, Azure).

Unser Dank gilt dem folgenden technischen Experten für die Durchsicht dieses Artikels: Dino Esposito