Oefening: een algemene webtoepassing maken

Voltooid

U hebt nu MongoDB en Node.js op uw Ubuntu-VM geïnstalleerd. Nu is het tijd om een eenvoudige webtoepassing te maken om te zien hoe alles werkt. Gaandeweg ziet u hoe AngularJS- en Express kunnen worden toegepast.

Oefening is een uitstekende manier om vertrouwd te raken met de materie. Met de webtoepassing die u bouwt, implementeert u een eenvoudige boekdatabase. Met de webtoepassing kunt u informatie weergeven over boeken, nieuwe boeken toevoegen en bestaande boeken verwijderen.

De webtoepassing die u hier ziet, toont veel concepten die betrekking hebben op de meeste MEAN-stackwebtoepassingen. U kunt de functies die u nodig hebt om uw eigen MEAN-stacktoepassingen te bouwen, verkennen op basis van uw behoeften en interesses.

Hier ziet u hoe de Boeken-webtoepassing eruitziet.

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

Hier ziet u hoe elk onderdeel van de MEAN-stack in de toepassing past.

  • MongoDB bevat informatie over boeken.
  • Express.js stuurt elke HTTP-aanvraag naar de juiste handler.
  • AngularJS verbindt de gebruikersinterface met de bedrijfslogica van het programma.
  • Node.js fungeert als host voor de toepassing aan serverzijde.

Belangrijk

Voor educatieve doeleinden bouwt u hier een eenvoudige webtoepassing. Het doel hiervan is het testen van uw MEAN-stack en om u een idee te geven van hoe dit werkt. De toepassing is niet voldoende veilig of gereed voor productiegebruik.

Hoe zit het met Express?

Tot nu toe hebt u MongoDB en Node.js geïnstalleerd op de VM. Hoe zit het met Express.js, de E in het MEAN-acroniem?

Express.js is een webserverframework dat is gebouwd voor Node.js waarmee het proces voor het bouwen van webtoepassingen wordt vereenvoudigd.

Het belangrijkste doel van Express is het afhandelen van routering van aanvragen. Routering verwijst naar de manier waarop de toepassing op een aanvraag naar een bepaald eindpunt reageert. Een eindpunt bestaat uit een pad of URI en een aanvraagmethode zoals GET of POST. U kunt bijvoorbeeld reageren op een GET-aanvraag naar het eindpunt /book door de lijst met alle boeken in de database op te geven. U kunt reageren op een POST-aanvraag naar hetzelfde eindpunt door een vermelding toe te voegen aan de database op basis van de velden die de gebruiker heeft ingevoerd in een webformulier.

In de webtoepassing die u zo gaat bouwen, routeert u met Express de HTTP-aanvragen en retourneert u de webinhoud aan uw gebruiker. Met Express kunt u er ook voor zorgen dat webtoepassingen werken met HTTP-cookies en querytekenreeksen verwerken.

Express is een Node.js-pakket. U gebruikt het npm-hulpprogramma, dat wordt geleverd met Node.js, om Node.js-pakketten te installeren en te beheren. Verderop in deze les maakt u een bestand met de naam package.json Express en andere afhankelijkheden en voert u vervolgens de npm install opdracht uit om deze afhankelijkheden te installeren.

Hoe zit het met AngularJS?

Net zoals Express hebt u AngularJS, de A van het MEAN-acroniem, nog niet geïnstalleerd.

Met AngularJS kunt u webtoepassingen gemakkelijker schrijven en testen, omdat u hiermee het uiterlijk van uw webpagina (uw HTML-code) beter kunt scheiden van hoe uw webpagina zich gedraagt. Als u bekend bent met het MVC-patroon (model-view-controller) of het concept gegevensbinding, weet u al wat AngularJS is.

AngularJS wordt een front-end JavaScript-framework genoemd. Dit houdt in dat het alleen beschikbaar is op de client die toegang heeft tot de toepassing. Met andere woorden, AngularJS wordt uitgevoerd in de webbrowser van uw gebruiker, niet op uw webserver. En omdat AngularJS JavaScript is, kunt u hiermee eenvoudig gegevens ophalen van uw webserver om deze weer te geven op de pagina.

U installeert AngularJS niet echt. In plaats daarvan voegt u een verwijzing naar het JavaScript-bestand in uw HTML-pagina toe, net zoals bij andere JavaScript-bibliotheken. Er zijn verschillende manieren om AngularJS op te nemen in uw webpagina's. Hier laadt u AngularJS vanuit een netwerk voor contentlevering of CDN. Een CDN is een manier om afbeeldingen, video en andere inhoud geografisch te distribueren om de downloadsnelheden te verbeteren.

Hier volgt een voorbeeld van het laden van AngularJS vanaf een CDN. Normaal gesproken voegt u deze code toe aan de <head> sectie van uw HTML-pagina.

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

Notitie

Let op: AngularJS is niet hetzelfde als Angular. Veel concepten zijn weliswaar vergelijkbaar, maar in feite is AngularJS de voorloper van Angular. AngularJS wordt nog altijd veel gebruikt voor het bouwen van webtoepassingen. Terwijl AngularJS is gebaseerd op JavaScript, is Angular gebaseerd op TypeScript, een programmeertaal waarmee u eenvoudiger JavaScript-programma's kunt schrijven.

Hoe compileer ik de toepassing?

In dit voorbeeld gebruikt u een eenvoudig proces. U schrijft de toepassingscode vanuit Cloud Shell en gebruikt vervolgens SCP, ofwel het beveiligde kopieprotocol, om de bestanden naar uw virtuele machine te kopiëren. Vervolgens start u de Node.js-toepassing en ziet u de resultaten in uw browser.

In de praktijk schrijft en test u uw webtoepassing doorgaans in een meer lokale omgeving, zoals vanaf uw laptop of vanaf een virtuele machine die u lokaal uitvoert. U kunt uw code vervolgens opslaan in een versiebeheersysteem zoals Git en een systeem voor continue integratie en continue levering ( of CI/CD), zoals Azure DevOps, gebruiken om uw wijzigingen te testen en te uploaden naar uw VM. Aan het einde van deze module vindt u meer resources.

De webtoepassing voor boeken maken

Hier maakt u alle code-, script- en HTML-bestanden waaruit uw webtoepassing bestaat. Kortheidshalve leggen we hier alleen de nadruk op belangrijke onderdelen van elk bestand. We gaan niet in op alle details.

Als u nog steeds verbinding hebt met uw virtuele machine via SSH, voert u exit uit om de SSH-sessie te verlaten en terug te keren naar de Cloud Shell.

exit

U bent nu terug bij uw Cloud Shell-sessie.

De bestanden maken

  1. Voer vanuit Cloud Shell deze opdrachten uit om de mappen en bestanden voor uw webtoepassing te maken:

    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
    

    Dit omvat het volgende:

    • Books is de hoofdmap van het project.
      • server.js definieert het toegangspunt voor de webtoepassing. Hiermee worden de vereiste Node.js-pakketten geladen, wordt opgegeven op welke poorten moet worden geluisterd en wordt geluisterd naar binnenkomend HTTP-verkeer.
      • package.json bevat informatie over uw toepassing, zoals de naam, de beschrijving en de Node.js-pakketten die nodig zijn om de toepassing te kunnen uitvoeren.
    • Books/app bevat de code die wordt uitgevoerd op de server.
      • model.js definieert de databaseverbinding en het schema. U kunt het zien als het gegevensmodel voor uw toepassing.
      • routes.js verwerkt routeringsaanvragen. Hiermee worden bijvoorbeeld GET-aanvragen naar het eindpunt /book gedefinieerd door de lijst met alle boeken in de database op te geven.
    • Books/public bevat bestanden die rechtstreeks aan de clientbrowser worden aangeboden.
      • index.html bevat de indexpagina. Het bevat een webformulier waarmee de gebruiker informatie over boeken kan invoeren. Ook worden hier alle boeken in de database weergegeven. Hier kunt u tevens vermeldingen in de database verwijderen.
      • script.js bevat JavaScript-code die wordt uitgevoerd in de browser van de gebruiker. Het kan aanvragen verzenden naar de server om de lijst met boeken weer te geven, boeken aan de database toe te voegen en boeken uit de database te verwijderen.
  2. Voer de opdracht code uit om uw bestanden via de Cloud Shell-editor te openen.

    code Books
    

Het gegevensmodel maken

  1. Open en voeg het volgende toe vanuit de editor app/model.js :

    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;
    

    Belangrijk

    Wanneer u code in een bestand in de editor plakt of wijzigt, moet u niet vergeten het bestand vervolgens op te slaan met behulp van het menu '...' of de sneltoets (Ctrl+S in Windows en Linux, Command+S in macOS).

    Deze code maakt gebruik van Mongoose om de gegevensoverdracht in en uit MongoDB te vereenvoudigen. Mongoose is een schemasysteem voor het modelleren van gegevens. Met de code wordt een databasedocument met de naam 'Boek' gedefinieerd met het opgegeven schema. In het schema zijn vier velden gedefinieerd waarmee één boek wordt beschreven:

    • De naam van het boek
    • Het internationale standaardboeknummer (of ISBN) dat het boek uniek identificeert
    • De auteur
    • Het aantal pagina's

    Vervolgens gaat u HTTP-handlers maken die GET-, POST- en DELETE-aanvragen toewijzen aan databasebewerkingen.

De Express.js-routes maken die HTTP-aanvragen verwerken

  1. Open app/routes.js en voeg de volgende code toe vanuit de editor:

    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;
    

    Met deze code worden vier routes gemaakt voor de toepassing. Hier volgt een kort overzicht van elke route.

    HTTP-woord Eindpunt Beschrijving
    GET /book Hiermee haalt u alle boeken uit de database op.
    POSTEN /book Hiermee maakt u een Book-object op basis van de velden die de gebruiker heeft opgegeven in het webformulier. Vervolgens wordt het object naar de database geschreven.
    DELETE /book/:isbn Hiermee verwijdert u het boek op basis van het ISBN uit de database.
    GET * Hiermee retourneert u de indexpagina indien geen andere route overeenkomt.

    Express.js kan HTTP-antwoorden rechtstreeks verwerken in de routeverwerkingscode, of het kan statische inhoud uit bestanden verwerken. In deze code ziet u beide. De eerste drie routes retourneren JSON-gegevens voor boek-API-aanvragen. De vierde route (standaard) retourneert de inhoud van het indexbestand index.html.

De JavaScript-clienttoepassing maken

  1. Open public/script.js in de editor en voeg de volgende code toe:

    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);
            });
        };
    });
    

    U ziet hoe met deze code een module met de naam myApp en een controller met de naam myCtrl wordt gedefinieerd. We gaan hier verder niet in op de werking van modules en controllers. U gebruikt deze namen in de volgende stap om de gebruikersinterface (HTML-code) te koppelen aan de bedrijfslogica van de toepassing.

    Eerder hebt u vier routes gemaakt waarmee verschillende GET-, POST- en DELETE-bewerkingen op de server worden verwerkt. Deze code lijkt op deze bewerkingen, maar dan vanaf de clientzijde (de webbrowser van de gebruiker).

    Met de functie getData wordt bijvoorbeeld een GET-aanvraag verzonden naar het /book-eindpunt. De server verwerkt deze aanvraag door informatie op te halen over alle boeken in de database en deze informatie als JSON-gegevens te retourneren. De resulterende JSON-gegevens worden toegewezen aan de variabele $scope.books. In de volgende stap leert u hoe dit van invloed is op wat de gebruiker op de webpagina ziet.

    Deze code roept de functie getData aan wanneer de pagina wordt geladen. U kunt de functies del_book en add_book controleren om een idee te krijgen van hun werking. U hebt geen code aan de clientzijde nodig om overeen te komen met de standaardhandler van de server, omdat de standaardhandler de indexpagina retourneert en geen JSON-gegevens.

De gebruikersinterface maken

  1. Open public/index.html in de editor en voeg de volgende code toe:

    <!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>
    

    Met deze code maakt u een eenvoudig HTML-formulier, met vier velden voor het verzenden van boekgegevens en een tabel waarin alle boeken worden weergegeven die zijn opgeslagen in de database.

    Hoewel dit standaard HTML-code is, komen de ng--HTML-kenmerken u mogelijk niet bekend voor. Met deze HTML-kenmerken wordt de AngularJS-code geactiveerd voor de gebruikersinterface. Wanneer u bijvoorbeeld Toevoegen selecteert, wordt via AngularJS de functie add_book aangeroepen, waarmee de formuliergegevens naar de server worden verzonden.

    U kunt de code hier bekijken om een beeld te krijgen van hoe elk van de kenmerken zich verhoudt tot de bedrijfslogica van de ng- toepassing.

De Express.js-server maken om de toepassing te hosten

  1. Open server.js in de editor en voeg de volgende code toe:

    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'));
    });
    

    Met deze code wordt de webtoepassing zelf gemaakt. Het dient statische bestanden uit de public map en gebruikt de routes die u eerder hebt gedefinieerd om aanvragen te verwerken.

Pakketgegevens en afhankelijkheden definiëren

package.json bevat informatie over uw toepassing, zoals de naam, de beschrijving en de Node.js-pakketten die nodig zijn om de toepassing te kunnen uitvoeren.

  1. Open package.json in de editor en voeg de volgende code toe:

    {
      "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"
      }
    }
    

Er worden gegevens, de zogeheten metagegevens, weergegeven over uw toepassing, zoals de naam, de beschrijving en de licentie.

Het veld repository geeft aan waar de code wordt onderhouden. U kunt de code later bekijken op GitHub via de URL die hier wordt weergegeven.

Met het veld main wordt het toegangspunt van de toepassing gedefinieerd. Het is hier beschikbaar voor volledigheid, maar het is niet belangrijk omdat u niet van plan bent om uw toepassing als node.js-pakket te publiceren, zodat anderen deze kunnen downloaden en gebruiken.

Het veld dependencies is belangrijk. Met dit veld worden namelijk de Node.js-pakketten gedefinieerd die uw toepassing nodig heeft. U gaat zo voor de tweede keer verbinding maken met uw VM en de opdracht npm install uitvoeren om deze pakketten te installeren.

Node-pakketten maken doorgaans gebruik van het Semantic Versioning-versiebeheerschema. Het versienummer bevat drie onderdelen: de primaire versie, de secundaire versie en de patch. De tilde ~-notatie hier vertelt npm dat de meest recente patchversie moet worden geïnstalleerd onder de opgegeven primaire en secundaire versies. De versies die u hier ziet, zijn de nieuwste versies waarmee deze module is getest. In de praktijk kunt u de versie na verloop van tijd verhogen wanneer u uw toepassing bijwerkt en test voor het gebruik van de nieuwste functies van elk afzonderlijk pakket.

De bestanden naar uw virtuele machine kopiëren

Voordat u verdergaat, moet u ervoor zorgen dat u het IP-adres van uw VIRTUELE machine bij de hand hebt. Als u deze niet hebt, voert u deze opdrachten uit vanuit de Cloud Shell om deze op te halen:

ipaddress=$(az vm show \
  --name MeanStack \
  --resource-group "<rgn>[sandbox resource group name]</rgn>" \
  --show-details \
  --query [publicIps] \
  --output tsv)
echo $ipaddress
  1. U bent klaar met het bewerken van bestanden. Zorg ervoor dat u wijzigingen in elk bestand hebt opgeslagen en sluit vervolgens de editor.

    Als u de editor wilt sluiten, selecteert u het beletselteken in de rechterbovenhoek en selecteert u Editor sluiten.

  2. Voer de volgende scp opdracht uit om de inhoud van de ~/Books map in uw Cloud Shell-sessie te kopiëren naar dezelfde mapnaam op uw VIRTUELE machine:

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

Extra Node-pakketten installeren

Stel dat u tijdens het ontwikkelingsproces hebt gezien dat er extra Node-pakketten zijn die u wilt gebruiken. Bijvoorbeeld: app/model.js begint met deze regel.

var mongoose = require('mongoose');

De toepassing gebruikt Mongoose om gegevens van en naar de MongoDB-database over te zetten.

De toepassing vereist ook Express.js en de body-parser-pakketten. Body-parser is een invoegtoepassing waarmee Express kan werken met gegevens van het webformulier dat door de client wordt verzonden.

Laten we nu verbinding maken met uw virtuele machine en de pakketten installeren die u hebt opgegeven in package.json.

  1. Voordat u verbinding maakt met uw VM, moet u ervoor zorgen dat u het IP-adres van uw VM bij de hand hebt. Als u deze niet hebt, voert u de Cloud Shell-opdrachten uit in de vorige sectie om deze op te halen.

  2. Net zoals u eerder hebt gedaan, maakt u een SSH-verbinding met uw VIRTUELE machine:

    ssh azureuser@$ipaddress
    
  3. Ga naar de Books map onder de basismap:

    cd ~/Books
    
  4. Voer deze opdracht uit npm install om de afhankelijke pakketten te installeren:

    sudo apt install npm -y && npm install
    

Houd uw SSH-verbinding geopend voor de volgende sectie.

De toepassing testen

U bent nu klaar om uw Node.js-webtoepassing te testen.

  1. Voer vanuit de ~/Books map deze opdracht uit om de webtoepassing te starten:

    sudo nodejs server.js
    

    Met deze opdracht start u de toepassing door op poort 80 te luisteren naar binnenkomende HTTP-aanvragen.

  2. Navigeer vanaf een ander browsertabblad naar het openbare IP-adres van uw virtuele machine.

    De indexpagina met daarop een webformulier verschijnt.

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

    Voeg een paar boeken toe aan de database. Steeds wanneer u een boek toevoegt, wordt de volledige lijst met boeken op de pagina bijgewerkt.

    Screenshot of the book web page with sample data populated.

    Als u een boek uit de database wilt verwijderen, kunt u ook Verwijderen selecteren.