Übung: Erstellen einer einfachen Webanwendung

Abgeschlossen

Bisher haben Sie MongoDB und Node.js auf Ihrer Ubuntu-VM installiert. Jetzt ist es Zeit eine einfache Web-App zu erstellen und das Ganze in Aktion zu sehen. Dabei wird veranschaulicht, wie AngularJS und Express hineinpassen.

Dies lässt sich am besten anhand eines Beispiels erklären. Die Web-App, die Sie erstellen, implementiert eine einfache Datenbank für Bücher. Mit der Web-App können Sie Informationen zu Büchern auflisten, neue Bücher hinzufügen und vorhandene Bücher löschen.

Die hier gezeigte Web-App veranschaulicht viele Konzepte, die für die meisten Web-Apps mit dem MEAN-Stapel gelten. Sie können die Features, die Sie für Ihre eigenen MEAN-Stapel-Apps benötigen, anhand Ihrer Anforderungen und Interessen untersuchen.

Die Bücherwebanwendung wird wie folgt aussehen.

Screenshot of a web page with a form and submission button.

Die Komponenten des MEAN-Stapels erfüllen folgende Aufgaben.

  • MongoDB speichert Informationen zu Büchern.
  • Express.js leitet alle HTTP-Anforderungen an den entsprechenden Handler weiter.
  • AngularJS verbindet die Benutzeroberfläche mit der Geschäftslogik des Programms.
  • Node.js hostet die serverseitige App.

Wichtig

Sie erstellen hier zu Lernzwecken nur eine einfache Web-App. Sie dient lediglich zum Testen des MEAN-Stapels und bietet Ihnen einen Eindruck der Funktionsweise. Die App ist nicht sicher genug und noch nicht bereit für die Produktion.

Was ist mit Express?

Bisher haben Sie MongoDB und Node.js auf Ihrer VM installiert. Was ist mit Express.js, dem E im Akronym MEAN?

Express.js ist ein Webserverframework für Node.js, das das Erstellen von Web-Apps vereinfacht.

Der Hauptzweck von Express besteht im Verarbeiten von Anforderungsrouting. Routing beschreibt, wie die App auf eine Anforderung an einen bestimmten Endpunkt reagiert. Ein Endpunkt besteht aus einem Pfad oder URI und einer Anforderungsmethode wie GET oder POST. Auf eine GET-Anforderung an den Endpunkt /book können Sie beispielsweise reagieren, indem Sie die Liste aller Bücher in der Datenbank bereitstellen. Auf eine POST-Anforderung an den gleichen Endpunkt können Sie reagieren, indem Sie einen Eintrag zur Datenbank hinzufügen, der auf den Feldern basiert, die der Benutzer in ein Webformular eingibt.

In der Web-App, die Sie in Kürze erstellen, verwenden Sie Express, um HTTP-Anforderungen weiterzuleiten und Webinhalte an den Benutzer zurückzugeben. Express kann Ihre Web-Apps außerdem bei der Arbeit mit HTTP-Cookies und beim Verarbeiten von Abfragezeichenfolgen unterstützen.

Express ist ein Node.js-Paket. Sie verwenden das npm-Hilfsprogramm, das im Lieferumfang von Node.js enthalten ist, um Node.js-Pakete zu installieren und zu verwalten. Später erstellen Sie in dieser Lerneinheit eine Datei namens package.json, um Express und andere Abhängigkeiten zu definieren. Dann führen Sie den npm install-Befehl zum Installieren dieser Abhängigkeiten aus.

Was ist mit AngularJS?

Auch AngularJS, das A in MEAN, haben Sie noch nicht installiert.

AngularJS vereinfacht das Schreiben und Testen von Web-Apps, da Sie damit die Darstellung Ihrer Webseite, den HTML-Code, besser vom Verhalten der Webseite unterscheiden können. Wenn Ihnen das MVC-Muster (Model View Controller) oder das Konzept der Datenbindung vertraut ist, wird Ihnen auch AngularJS bekannt vorkommen.

AngularJS ist ein Front-End-Framework von JavaScript, was bedeutet, dass es nur den Clients zur Verfügung stehen muss, die auf die App zugreifen. Das heißt, AngularJS wird im Webbrowser Ihrer Benutzer ausgeführt, nicht auf Ihrem Webserver. Und da AngularJS JavaScript ist, können Sie es mit Leichtigkeit zum Abrufen von Daten von Ihrem Webserver verwenden, die auf der Seite angezeigt werden sollen.

Sie installieren AngularJS nicht wirklich. Stattdessen fügen Sie einen Verweis auf die JavaScript-Datei in Ihrer HTML-Seite hinzu, wie Sie es von anderen JavaScript-Bibliotheken kennen. Es gibt mehrere Möglichkeiten zum Einschließen von AngularJS in Ihren Webseiten. Hier laden Sie AngularJS über ein Content Delivery Network (CDN). Ein CDN ist eine Möglichkeit, Bilder, Videos und andere Inhalte geografisch zu verteilen, um die Downloadgeschwindigkeit zu verbessern.

Fügen Sie diesen Code noch nicht hinzu. Hier sehen Sie jedoch ein Beispiel, in dem AngularJS über ein CDN geladen wird. Diesen Code fügen Sie in der Regel zum <head>-Abschnitt Ihrer HTML-Seite hinzu.

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.2/angular.min.js"></script>

Hinweis

Verwechseln Sie AngularJS nicht mit Angular. Obwohl sich viele Konzepte bei beiden ähneln, ist AngularJS der Vorgänger von Angular. AngularJS wird weiterhin häufig zum Erstellen von Web-Apps verwendet. Während AngularJS auf JavaScript basiert, basiert Angular auf TypeScript. Dies ist eine Programmiersprache, mit der JavaScript-Programme ganz einfach geschrieben werden können.

Wie erstelle ich die App?

Dafür befolgen Sie einen einfachen Prozess. Sie schreiben Anwendungscode in Cloud Shell und kopieren anschließend die Dateien mit SCP (Secure Copy Protocol) auf Ihren virtuellen Computer. Anschließend starten Sie die Node.js-App, und die Ergebnisse werden im Browser angezeigt.

In der Praxis schreiben und testen Sie Ihre Web-App normalerweise in einer lokalen Umgebung, z. B. von Ihrem Laptop aus oder über einen virtuellen Computer, den Sie lokal ausführen. Dann speichern Sie Ihren Code in einem Versionskontrollsystem, z. B. Git, und verwenden ein Continuous Integration- und Continuous Delivery-System (CI/CD), beispielsweise Azure DevOps, um Ihre Änderungen zu testen und auf Ihre VM hochzuladen. Am Ende dieses Moduls finden Sie weitere Ressourcen.

Erstellen der Bücher-Web-App

Hier erstellen Sie den gesamten Code, die Skripts und HTML-Dateien, aus denen Ihre Web-App besteht. Aus Gründen der Übersichtlichkeit sind die wichtigen Teile jeder Datei hervorgehoben, wir werden jedoch nicht detailliert darauf eingehen.

Wenn Sie noch immer mit Ihrem virtuellen Computer über SSH verbunden sind, führen Sie exit aus, um die SSH-Sitzung zu beenden und zu Cloud Shell zurückzukehren.

exit

Sie befinden sich jetzt wieder in Ihrer Cloud Shell-Sitzung.

Erstellen der Dateien

  1. Führen Sie die folgenden Befehle in Cloud Shell aus, um die Ordner und Dateien für Ihre Webanwendung zu erstellen:

    cd ~
    mkdir Books
    touch Books/server.js
    touch Books/package.json
    mkdir Books/app
    touch Books/app/model.js
    touch Books/app/routes.js
    mkdir Books/public
    touch Books/public/script.js
    touch Books/public/index.html
    

    Folgendes ist enthalten:

    • Books: Das Stammverzeichnis des Projekts
      • server.js definiert den Einstiegspunkt für die Web-App. Dieser lädt die erforderlichen Node.js-Pakete, gibt den zu belauschenden Port an und lauscht an eingehendem HTTP-Datenverkehr.
      • package.json stellt Informationen zu Ihrer App bereit, einschließlich des Namens, der Beschreibung und der Node.js-Pakete, die zum Ausführen Ihrer App erforderlich sind.
    • Books/app: Enthält Code, der auf dem Server ausgeführt wird.
      • model.js definiert die Datenbankverbindung und das Datenbankschema. Sie können dies als das Datenmodell für Ihre App betrachten.
      • routes.js verarbeitet das Routing von Anforderungen. Beispielsweise werden GET-Anforderungen an den /book-Endpunkt definiert, indem die Liste aller Bücher in der Datenbank bereitgestellt werden.
    • Books/public: Enthält Dateien, die direkt an den Browser des Clients gesendet werden.
      • index.html enthält die Indexseite. Diese enthält ein Webformular, über das Benutzer Informationen zu Büchern übermitteln können. Außerdem zeigt sie alle Bücher in der Datenbank an und ermöglicht Ihnen, Einträge aus der Datenbank zu löschen.
      • script.js enthält JavaScript-Code, der im Browser des Benutzers ausgeführt wird. Dieser kann Anforderungen an den Server senden, um Bücher aufzulisten, zur Datenbank hinzuzufügen und aus der Datenbank zu löschen.
  2. Führen Sie den Befehl code aus, um Ihre Dateien im Cloud Shell-Editor zu öffnen.

    code Books
    

Erstellen des Datenmodells

  1. Öffnen Sie app/model.js über den Editor, und fügen Sie folgenden Code hinzu:

    var mongoose = require('mongoose');
    var dbHost = 'mongodb://localhost:27017/Books';
    mongoose.connect(dbHost, { useNewUrlParser: true } );
    mongoose.connection;
    mongoose.set('debug', true);
    var bookSchema = mongoose.Schema( {
        name: String,
        isbn: {type: String, index: true},
        author: String,
        pages: Number
    });
    var Book = mongoose.model('Book', bookSchema);
    module.exports = Book;
    

    Wichtig

    Achten Sie beim Einfügen oder Ändern von Code in einer Datei im Editor darauf, dass Sie anschließend speichern, indem Sie das Menü unter „...“ oder die Tastenkombination (STRG+S unter Windows und Linux, Cmd+S unter macOS) verwenden.

    Dieser Code nutzt Mongoose zum Vereinfachen des Datenverkehrs in und aus MongoDB. Mongoose ist ein schemabasiertes System für die Modellierung von Daten. Der Code definiert ein Datenbankdokument namens „Book“ mit dem angegebenen Schema. Das Schema definiert vier Felder, die ein einzelnes Buch beschreiben:

    • Der Name oder Titel des Buchs
    • Die internationale Standardbuchnummer bzw. ISBN des Buchs, die das Buch eindeutig identifiziert
    • Der Autor des Buchs
    • Die Anzahl der enthaltenen Seiten

    Als Nächstes erstellen Sie HTTP-Handler, die GET-, POST- und DELETE-Anforderungen zu Datenbankvorgängen zuordnen.

Erstellen der Express.js-Routen, die HTTP-Anforderungen verarbeiten

  1. Öffnen Sie app/routes.js über den Editor, und fügen Sie folgenden Code hinzu:

    var path = require('path');
    var Book = require('./model');
    var routes = function(app) {
        app.get('/book', function(req, res) {
            Book.find({}, function(err, result) {
                if ( err ) throw err;
                res.json(result);
            });
        });
        app.post('/book', function(req, res) {
            var book = new Book( {
                name:req.body.name,
                isbn:req.body.isbn,
                author:req.body.author,
                pages:req.body.pages
            });
            book.save(function(err, result) {
                if ( err ) throw err;
                res.json( {
                    message:"Successfully added book",
                    book:result
                });
            });
        });
        app.delete("/book/:isbn", function(req, res) {
            Book.findOneAndRemove(req.query, function(err, result) {
                if ( err ) throw err;
                res.json( {
                    message: "Successfully deleted the book",
                    book: result
                });
            });
        });
        app.get('*', function(req, res) {
            res.sendFile(path.join(__dirname + '/public', 'index.html'));
        });
    };
    module.exports = routes;
    

    Mit diesem Code werden vier Routen für die App erstellt. Es folgt eine kleine Übersicht über diese.

    HTTP-Verb Endpunkt BESCHREIBUNG
    GET /book Ruft alle Bücher aus der Datenbank ab.
    POST /book Erstellt ein Book-Objekt anhand der Felder, die der Benutzer im Webformular ausfüllt. Dieses Objekt wird dann in die Datenbank geschrieben.
    DELETE /book/:isbn Löscht das Buch, das anhand der ISBN in der Datenbank identifiziert wird.
    GET * Gibt die Indexseite zurück, wenn keine andere Route mit der Anforderung übereinstimmt.

    Express.js kann HTTP-Antworten direkt an den Routenverarbeitungscode leiten oder statischen Inhalt aus Dateien bereitstellen. Im Code werden beide Situationen dargestellt. Die ersten drei Routen geben bei API-Anforderungen JSON-Daten zu Büchern zurück. Die vierte Route (der Standardfall) gibt den Inhalt der Indexdatei index.html zurück.

Erstellen der clientseitigen JavaScript-App

  1. Öffnen Sie public/script.js über den Editor, und fügen Sie folgenden Code hinzu:

    var app = angular.module('myApp', []);
    app.controller('myCtrl', function($scope, $http) {
        var getData = function() {
            return $http( {
                method: 'GET',
                url: '/book'
            }).then(function successCallback(response) {
                $scope.books = response.data;
            }, function errorCallback(response) {
                console.log('Error: ' + response);
            });
        };
        getData();
        $scope.del_book = function(book) {
            $http( {
                method: 'DELETE',
                url: '/book/:isbn',
                params: {'isbn': book.isbn}
            }).then(function successCallback(response) {
                console.log(response);
                return getData();
            }, function errorCallback(response) {
                console.log('Error: ' + response);
            });
        };
        $scope.add_book = function() {
            var body = '{ "name": "' + $scope.Name +
            '", "isbn": "' + $scope.Isbn +
            '", "author": "' + $scope.Author +
            '", "pages": "' + $scope.Pages + '" }';
            $http({
                method: 'POST',
                url: '/book',
                data: body
            }).then(function successCallback(response) {
                console.log(response);
                return getData();
            }, function errorCallback(response) {
                console.log('Error: ' + response);
            });
        };
    });
    

    Beachten Sie, wie dieser Code ein Modul namens „myApp“ und einen Controller namens „myCtrl“ definiert. Auf die Funktionsweise dieses Moduls und Controllers wird hier nicht eingegangen, aber Sie werden diese Namen im nächsten Schritt zum Binden der Benutzeroberfläche (HTML-Code) mit der Geschäftslogik der App verwenden.

    Sie haben zuvor vier Routen erstellt, die verschiedene GET-, POST- und DELETE-Vorgänge auf dem Server verarbeiten. Dieser Code ähnelt eben diesen Vorgängen von der Clientseite (Webbrowser des Benutzers) aus.

    Die Funktion getData sendet beispielsweise eine GET-Anforderung an den /book-Endpunkt. Denken Sie daran, dass der Server diese Anforderung verarbeitet, indem er Informationen zu allen Büchern aus der Datenbank abruft und diese als JSON-Daten zurückgibt. Beachten Sie, wie die resultierenden JSON-Daten der $scope.books-Variable zugewiesen werden. Im nächsten Schritt sehen Sie, wie sich dies darauf auswirkt, was den Benutzer*innen auf der Webseite angezeigt wird.

    Dieser Code ruft die getData-Funktion auf, wenn die Seite geladen wird. Sie können sich die Funktionen del_book und add_book ansehen, um deren Funktionsweise ein wenig kennenzulernen. Sie benötigen keinen clientseitigen Code, der dem Standardhandler des Servers, da der Standardhandler die Indexseite zurückgibt, keine JSON-Daten.

Erstellen der Benutzeroberfläche

  1. Öffnen Sie public/index.html über den Editor, und fügen Sie folgenden Code hinzu:

    <!doctype html>
    <html ng-app="myApp" ng-controller="myCtrl">
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.2/angular.min.js"></script>
        <script src="script.js"></script>
    </head>
    <body>
        <div>
        <table>
            <tr>
            <td>Name:</td>
            <td><input type="text" ng-model="Name"></td>
            </tr>
            <tr>
            <td>Isbn:</td>
            <td><input type="text" ng-model="Isbn"></td>
            </tr>
            <tr>
            <td>Author:</td>
            <td><input type="text" ng-model="Author"></td>
            </tr>
            <tr>
            <td>Pages:</td>
            <td><input type="number" ng-model="Pages"></td>
            </tr>
        </table>
        <button ng-click="add_book()">Add</button>
        </div>
        <hr>
        <div>
        <table>
            <tr>
            <th>Name</th>
            <th>Isbn</th>
            <th>Author</th>
            <th>Pages</th>
            </tr>
            <tr ng-repeat="book in books">
            <td><input type="button" value="Delete" data-ng-click="del_book(book)"></td>
            <td>{{book.name}}</td>
            <td>{{book.isbn}}</td>
            <td>{{book.author}}</td>
            <td>{{book.pages}}</td>
            </tr>
        </table>
        </div>
    </body>
    </html>
    

    Dieser Code erstellt ein einfaches HTML-Formular mit vier Feldern zum Übermitteln von Buchdaten und einer Tabelle, in der alle in der Datenbank gespeicherten Bücher angezeigt werden.

    Obwohl es sich hierbei um HTML-Standardcode handelt, können die ng--HTML-Attribute Ihnen unbekannt sein. Diese HTML-Attribute verknüpfen den AngularJS-Code mit der Benutzeroberfläche. Wenn Sie beispielsweise Hinzufügen auswählen, ruft AngularJS die add_book-Funktion auf, die Formulardaten an den Server sendet.

    Sie können den Code hier überprüfen, um zu sehen, in welchem Zusammenhang die einzelnen ng--Attribute zur Geschäftslogik der App stehen.

Erstellen des Express.js-Servers zum Hosten der App

  1. Öffnen Sie server.js über den Editor, und fügen Sie folgenden Code hinzu:

    var express = require('express');
    var bodyParser = require('body-parser');
    var app = express();
    app.use(express.static(__dirname + '/public'));
    app.use(bodyParser.json());
    require('./app/routes')(app);
    app.set('port', 80);
    app.listen(app.get('port'), function() {
        console.log('Server up: http://localhost:' + app.get('port'));
    });
    

    Mit diesem Code wird die Web-App erstellt. Er stellt statische Dateien aus dem public-Verzeichnis bereit und verwendet die zuvor definierten Routen zum Verarbeiten von Anforderungen.

Definieren von Paketinformationen und Abhängigkeiten

Denken Sie daran, dass package.json Informationen zu Ihrer App bereitstellt, einschließlich des Namens, der Beschreibung und der Node.js-Pakete, die zum Ausführen Ihrer App erforderlich sind.

  1. Öffnen Sie package.json über den Editor, und fügen Sie folgenden Code hinzu:

    {
      "name": "books",
      "description": "Sample web app that manages book information.",
      "license": "MIT",
      "repository": {
        "type": "git",
        "url": "https://github.com/MicrosoftDocs/mslearn-build-a-web-app-with-mean-on-a-linux-vm"
      },
      "main": "server.js",
      "dependencies": {
        "express": "~4.16",
        "mongoose": "~5.3",
        "body-parser": "~1.18"
      }
    }
    

Ihnen werden Informationen oder Metadaten zu Ihrer App angezeigt, einschließlich Name, Beschreibung und Lizenz.

Das Feld repository gibt an, wo der Code gespeichert wird. Sie können sich den Code zu Referenzzwecken über die URL im obigen Code auf GitHub ansehen.

Das Feld main definiert den Einstiegspunkt der App. Da Sie die App nicht als Node.js-Paket veröffentlichen möchten, damit andere sie herunterladen und verwenden können, ist dies nicht wichtig. Der Einstiegspunkt wird hier lediglich zugunsten der Vollständigkeit angegeben.

Das Feld dependencies ist wichtig. Es definiert die Node.js-Pakete, die für Ihre App erforderlich sind. In Kürze stellen Sie eine weitere Verbindung mit Ihrer VM her und führen den Befehl npm install aus, um diese Pakete zu installieren.

Node-Pakete verwenden in der Regel das Versionsverwaltungsschema Semantic Versioning (Semantische Versionierung). Die Versionsnummer enthält drei Komponenten: Hauptversion, Nebenversion und Patch. Die Tildennotation ~ weist npm an, die neueste Patchversion unter den angegebenen Haupt- und Nebenversionen zu installieren. Die hier gezeigten Versionen sind die neuesten, mit denen dieses Modul getestet wurde. In der Praxis können Sie die Version mit der Zeit erhöhen, während Sie Ihre App aktualisieren und testen, um die neuesten Features zu nutzen, die die einzelnen abhängigen Pakete bereitstellen.

Kopieren der Dateien in Ihre VM

Bevor Sie fortfahren, stellen Sie sicher, dass Sie über die IP-Adresse Ihrer VM verfügen. Falls nicht, führen Sie die folgenden Befehle in Cloud Shell aus, um sie abzurufen:

ipaddress=$(az vm show \
  --name MeanStack \
  --resource-group "<rgn>[sandbox resource group name]</rgn>" \
  --show-details \
  --query [publicIps] \
  --output tsv)
echo $ipaddress
  1. Sie sind mit dem Bearbeiten von Dateien fertig. Stellen Sie sicher, dass Sie die Änderungen an allen Dateien gespeichert haben, und schließen Sie dann den Editor.

    Klicken Sie zum Schließen des Editors auf die Auslassungspunkte in der oberen rechten Ecke, und wählen Sie dann Editor schließen aus.

  2. Führen Sie den folgenden scp-Befehl aus, um die Inhalte des ~/Books-Verzeichnisses aus der Cloud Shell-Sitzung in das Verzeichnis mit dem gleichen Namen auf Ihrer VM zu kopieren:

    scp -r ~/Books azureuser@$ipaddress:~/Books
    

Installieren zusätzlicher Node-Pakete

Angenommen, Sie finden während des Entwicklungsprozesses weitere Node-Pakete, die Sie verwenden möchten. Denken Sie daran, dass app/model.js mit der folgenden Zeile beginnt.

var mongoose = require('mongoose');

Die App nutzt Mongoose zum Übertragen von Daten in und aus Ihrer MongoDB-Datenbank.

Für die App sind außerdem Express.js und die body-parser-Pakete erforderlich. Body-parser ist ein Plug-In, mit dem Express mit Daten aus dem Webformular arbeiten kann, das vom Client gesendet wird.

Stellen Sie eine Verbindung mit Ihrer VM her, und installieren Sie die Pakete, die Sie in package.json angegeben haben.

  1. Stellen Sie sicher, dass Sie über die IP-Adresse Ihrer VM verfügen, bevor Sie eine Verbindung mit Ihrer VM herstellen. Falls nicht verfügbar, führen Sie zum Abrufen die Cloud Shell-Befehle im vorherigen Abschnitt aus.

  2. Stellen Sie eine SSH-Verbindung mit Ihrer VM her, wie Sie es bereits zuvor getan haben:

    ssh azureuser@$ipaddress
    
  3. Navigieren Sie zum Books-Verzeichnis im Stammverzeichnis:

    cd ~/Books
    
  4. Führen Sie npm install zum Installieren der abhängigen Pakete aus:

    sudo apt install npm -y && npm install
    

Lassen Sie die SSH-Verbindung für die nächste Einheit geöffnet:

Testen der App

Sie sind jetzt bereit, Ihre Node.js-Web-App zu testen.

  1. Führen Sie den folgenden Befehl im ~/Books-Verzeichnis aus, um die Web-App zu starten:

    sudo nodejs server.js
    

    Mit diesem Befehl wird die App gestartet, indem an Port 80 auf eingehende HTTP-Anforderungen lauscht.

  2. Öffnen Sie die öffentliche IP-Adresse Ihrer VM in einer neuen Registerkarte.

    Die Indexseite, die ein Webformular enthält, wird angezeigt.

    Screenshot of the book web page with a form and submission button.

    Versuchen Sie, ein paar Bücher zur Datenbank hinzuzufügen. Jedes Mal, wenn Sie ein Buch hinzufügen, aktualisiert die Seite die vollständige Liste der Bücher.

    Screenshot of the book web page with sample data populated.

    Sie können auch auf die Schaltfläche Delete (Löschen) klicken, um ein Buch aus der Datenbank zu entfernen.