© Lilu330/Shutterstock.com
Mit ASP.NET Core und Angular eine Webanwendung erstellen

Das Beste aus zwei Welten


Moderne verteilte Systeme im Web bestehen heutzutage aus vielen verschiedenen Teilen, Systemen und Technologien. Frontend und Backend sind zwei sehr wichtige Elemente einer aktuellen Webapplikation. Für maximale Flexibilität kann man diese Teile vollkommen trennen und eine im Browser laufende, eigene Applikation als Frontend mit einem REST-Service im Backend kommunizieren lassen.

Video: Mehr als nur Web: Cross-Plattform-Anwendungen mit Angular, Electron und Cordova

Die Auswahl eines Frontend-Frameworks ist nicht leicht, jedoch hat sich Angular in der letzten Zeit nicht nur durch ein gutes „Separation of Concerns“-Konzept und gute Synchronisierungsmechanismen von Model, View, Architektur und hoher Performance hervorgehoben. Auch die Regelmäßigkeit, mit der neue Versionen erscheinen, das Angular CLI und nicht zuletzt der Internetgigant Google mit Long Term Support tragen dazu bei, dass mehr und mehr Businessanwendungen im Web auf die Angular-Plattform setzen.

Im Backend hat Microsoft mit ASP.NET Core spätestens seit Version 2.x den alten Mantel abgeworfen und kommt neu, schlank und vor allem schnell daher. Live-Reload, Middleware, die Geschwindigkeit und Cross-Plattform-Fähigkeit sind nur einige Gründe, warum man ASP.NET Core mehr als nur einen Blick widmen sollte.

In diesem Artikel möchte ich die Bestandteile und auch die Vorteile eines Frontends mit Angular sowie eines REST Backends mit ASP.NET Core darstellen und zeigen, wie man diese Teile einer Webapplikation programmieren kann. Als Beispiel programmieren wir einen Booktracker, der eine Merkliste für Bücher enthält, die man als gelesen markieren kann. Außerdem kann man neue Bücher hinzufügen und bestehende bearbeiten. Den kompletten Quellcode gibt es natürlich wieder auf GitHub [1].

Das ASP.NET Core Backend

ASP.NET Core bietet ein Command Line Interface (CLI) an, mit dem wir uns über Templates das Grundgerüst unseres Web-APIs erstellen lassen können [2]. Eine Kommandozeile im gewünschten Ordner und der Befehl dotnet new webapi in der Konsole erzeugen hierbei ein neues Web-API für uns, das wir mit dotnet watch run laufen lassen können. Beim Erstellen des Web-API wurde gleich der dotnet restore-Command ausgeführt, mit dem alle unsere NuGet-Pakete heruntergeladen und für unsere Anwendung bereitgestellt wurden. Hierbei wird ein neuer Webserver „Kestrel“ in einer Konsole gehostet, der unsere Applikation hochfährt und für Requests bereitstellt. Dabei stehen uns in der neuesten Version ein HTTP- und ein HTTPS-Endpunkt zur Verfügung. dotnet watch run bietet zudem einen Live-Reload-Server, sodass jedes Mal, wenn wir eine Datei in unserem Projekt ändern, das Backend neu gestartet wird und wir es nicht manuell unterbrechen und wieder hochfahren müssen.

Konfiguration und Start des Web-API

ASP.NET-Core-Applikationen sind grundsätzlich Konsolenprogramme, die wir beispielsweise mit dem Befehl dotnet run von der Kommandozeile aus starten können. Somit ist der Startpunkt unseres Web-API eine einfache Konsolenapplikation, die uns einen Webserver bereitstellt, statt beispielsweise ein „Hello World“ auf der Konsole auszugeben (Listing 1).

Listing 1

public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>(); }

Falls etwaige Konfigurationen für unsere Applikation vorgenommen werden sollen, können diese Konfigurationsfiles hier eingelesen werden. Standardmäßig werden die mitgenerierten appsettings.json und appsettings.*.json zur Konfiguration herbeigezogen und appliziert. Aber es werden auch weitere Konfigurationsformate wie *.xml oder sogar *.ini unterstützt. ASP.NET Core erstellt also neben einem Webserver auch ein Konfigurationsobjekt, das wir in der Startup.cs zur Verfügung gestellt bekommen und nutzen können.

Start-up und Dependency Injecton

Basierend auf der fertigen Konfiguration, die uns mitgegeben wird, können wir nun unser Web-API konfigurieren. ASP.NET Core macht dabei Gebrauch vom internen Dependency-Injection-System. In der Datei Startup.cs bekommen wir im Konstruktor der Klasse die Konfiguration übergeben (Listing 2). ASP.NET Core kommt also mit einem eigenen Dependency-Injection-System, auf das wir im weiteren Verlauf dieses Artikels noch eingehen werden.

Listing 2

public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // ... }

Die Startup.cs-Datei stellt zwei weitere Methoden bereit: ConfigureServices und Configure. Erstere füllt den Dependency-Injection-Container, den wir von ASP. NET Core übergeben bekommen:

public void ConfigureServices(IServiceCollection services) { /*...*/ }

Auf diesem Container können wir unsere abhängigen Services eintragen und später in unseren Klassen via Dependency Injection injiziert bekommen und somit nutzen. Auch MVC selbst wird hier mitsamt seinen Services in den Container gelegt.

Die Methode Configure erstellt eine Pipeline für alle unsere Requests, bevor sie von unseren Controllern bearbeitet werden. Hierbei ist die Reihenfolge der hinzugefügten Middleware wichtig. Das heißt, jeder eingehende Request durchläuft die Middleware, die wir in dieser Methode angeben können. Ebenso die ausgehende Response, diesmal wird die Middleware jedoch in umgekehrter Reihenfolge abgearbeitet. Also können Features wie Authentication etc. hier in die Pipeline für unsere Requests „eingehängt“ werden. Auch MVC selbst wird als Middleware hinzugefügt. Die Services haben wir schon in der ConfigureServices-Methode angegeben, die zugehörige Middleware nutzen wir mit app.UseMvc(). Somit kann unser Web-API Requests in Controllern empfangen, Routing nutzen etc.

Verwenden des Dependency-Injection-Containers

In unserer Beispielapplikation brauchen wir ein Repository, mit dem wir unsere Entities in der Datenbank abspeichern können:

public interface IBookRepository { ... } public class BookRepository : IBookRepository

Dieses Repository können wir nun in unserem Dependency-Injection-Container vor dem Interface registrieren:

services.AddScoped<IBookRepository, BookRepository>();

AddScoped sorgt hierbei dafür, dass die Instanz so lange gehalten wird, wie der Request bearbeitet wird, und dass mit jedem Request eine neue Instanz erstellt wird. Die weitere Methode AddSingleton() legt eine Instanz des Service beim Hochfahren der Applikation an, AddTransient () würde eine Instanz pro Konstruktorinjection erstellen.

Definieren eines REST-Endpunkts mit Controllern

Der eigentliche REST-Endpunkt wird in ASP.NET Core in Controllern abgebildet. Die HTTP-Verben wie GET, POST, PUT, PATCH, DELETE etc. können in Controllern implementiert werden (Listing 3).

Listing 3

[Route("api/[controller]")] [ApiController] public class ValuesController : ControllerBase { // }

Das Route-Attribut über der Controllerklasse legt die Adresse des Endpunkts fest. Hierbei ist der Präfix herkömmlicherweise api/, gefolgt von einem generischen [controller]. Das wird ersetzt durch den Namen der Klasse ohne das Suffix „Controller“. In diesem Fall also api/values.

Mit dem ApiController-Attribut legen wir fest, dass es sich um einen HTTP-Endpunkt handelt, der HTTP-Antworten verschickt. Mit dem Ableiten von ControllerBase verzichten wir auf alle View-Funktionalitäten, die wir im Fall einer kompletten MVC-Applikation bräuchten, da wir als Antwort keine komplett gerenderten Seiten, sondern JSON verschicken wollen.

Innerhalb dieses Controllers können wir nun den REST-Endpunkt definieren, mit dem sich unsere Bücher abspeichern und aktualisieren lassen. In unserem Beispiel erstellen wir uns einen BooksController, für den wir den Endpunkt api/books definieren. Da das Repository schon im Container registriert wurde, können wir es nun im Controller einfach injecten (Listing 4).

Listing 4

[Route("api/[controller]")] [ApiController] public class BooksController : ControllerBase { private readonly IBookRepository _bookRepository; public BooksController(IBookRepository repository) { _bookRepository = repository; } }

Methode

Link

GET

api/books/

GET

api/books/{id}

POST

api/books/

PUT

api/books/{id}

DELETE

api/books/{id}

Tabelle 1: REST-Endpunkt

„AddMvc()“

Mit dem Aufruf AddMvc() in der Startup-Klasse haben wir bereits einen JSON-Serialisierer hinzugefügt, der uns die Daten automatisch von Objekten zu JSON und von JSON zu Objekten parst. ASP.NET Core liefert diese Funktionalität also out of the box.

Um nun alle Bücher abrufen zu können, müssen wir auf das HTTP-GET-Verb reagieren (Tabelle 1) und als Antwort alle Bücher als JSON serialisiert zurückgeben (Kasten: „AddMvc()“). ASP.NET Core bietet hierbei als Rückgabetyp einer Methode unter anderem das IActionResult-Interface oder eine gar generische Klasse ActionResult<T> an, um HTTP-Antworten zu definieren. Gleichzeitig bietet das Ableiten der Controllerklasse von ControllerBase einen weiteren Vorteil: Man kann kleine Helfermethoden wie zum Beispiel Ok(...) oder BadRequest(...) benutzen, die dem Entwickler helfen, die richtige HTTP-Antwort mit dem korrekten HTTP-Statuscode zurück zum Client zu schicken. Das ist bei entkoppelten Systemen und einer solchen Service-Architektur von elementarer Wichtigkeit.

Eine Methode, die auf ein HTTP-Verb reagieren soll, kann mit...

Neugierig geworden?

Angebote für Teams

Für Firmen haben wir individuelle Teamlizenzen. Wir erstellen Ihnen gerne ein passendes Angebot.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang