Övning – Skapa en grundläggande webbapp

Slutförd

Du har installerat MongoDB och Node.js på den virtuella Ubuntu-datorn. Nu är det dags att skapa ett enkelt webbprogram för att se hur allt fungerar i praktiken. Under processens gång får du även se hur AngularJS och Express används.

Ett bra sätt att lära sig är genom att följa exempel. Webbprogrammet som du ska skapa implementerar en enkel bokdatabas. Med webbprogrammet kan du lista information om böcker, lägga till nya böcker och ta bort befintliga böcker.

Webbprogrammet som du ser här demonstrerar många begrepp som gäller för de flesta webbprogram som baseras på MEAN-stacken. Baserat på dina behov och intressen kan du utforska de funktioner som du behöver för att skapa dina egna MEAN-stackprogram.

Så här kommer webbprogrammet Books att se ut.

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

Så här fungerar de olika komponenterna i MEAN-stacken.

  • MongoDB lagrar information om böcker.
  • Express.js dirigerar varje HTTP-begäran till lämplig hanterare.
  • AngularJS ansluter användargränssnittet till programmets affärslogik.
  • Node.js är värd för programmet på serversidan.

Viktigt!

I utbildningssyfte ska du här skapa ett enkelt webbprogram. Målet är att du ska testa MEAN-stacken för att få en uppfattning om hur den fungerar. Programmet är inte tillräckligt säkert eller redo för produktionsanvändning.

Och Express?

Hittills har du installerat MongoDB och Node.js på den virtuella datorn. Hur är det med Express.js, E i MEAN-förkortningen?

Express.js är ett webbserverramverk som har skapats för Node.js som förenklar processen för att skapa webbprogram.

Det huvudsakliga syftet med Express är att hantera routning av begäranden. Routning syftar på hur programmet svarar på en begäran till en viss slutpunkt. En slutpunkt består av en sökväg eller URI och en begärandemetod som GET eller POST. Du kanske exempelvis svarar på en GET-begäran till slutpunkten /book genom att tillhandahålla en lista över alla böcker i databasen. Du kanske svarar på en POST-begäran till samma slutpunkt genom att lägga till en post i databasen baserat på fält som användaren angett i ett webbformulär.

I webbprogrammet som du snart ska skapa ska du använda Express för att dirigera HTTP-begäranden och för att returnera webbinnehåll till användaren. Express kan också hjälpa dina webbprogram att arbeta med HTTP-cookies och bearbeta frågesträngar.

Express är ett Node.js-paket. Du använder verktyget npm, som medföljer Node.js, för att installera och hantera Node.js-paket. Senare i den här lektionen skapar du en fil med namnet package.json för att definiera Express och andra beroenden och kör npm install sedan kommandot för att installera dessa beroenden.

Och AngularJS?

Precis som Express har du ännu inte installerat AngularJS, bokstaven A i MEAN-förkortningen.

AngularJS gör webbprogram enklare att skriva och testa eftersom det gör att du bättre kan skilja utseendet på din webbsida – HTML-koden – från hur webbsidan fungerar. Om du är bekant med MVC-mönstret (Model-View-Controller) eller begreppet databindning kommer du att känna igen dig i AngularJS.

AngularJS är ett så kallat JavaScript-ramverk för klientsidan, vilket innebär att det bara behöver vara tillgängligt på klienten som kommer åt programmet. Det betyder att AngularJS körs i användarens webbläsare, inte på din webbserver. Och eftersom AngularJS är JavaScript kan du använda det för att enkelt hämta data från webbservern som ska visas på sidan.

Du installerar egentligen inte AngularJS. I stället lägger du till en referens till JavaScript-filen på HTML-sidan, precis som med andra JavaScript-bibliotek. Du kan lägga till AngularJS till dina webbsidor på flera sätt. Här läser du in AngularJS från ett nätverk för innehållsleverans eller CDN. Ett CDN är ett sätt att distribuera bilder, videoklipp och annat innehåll geografiskt för att förbättra hämtningshastigheten.

Vänta med att lägga till den här koden, men här är ett exempel som läser in AngularJS från ett CDN. Du lägger vanligtvis till den här koden i <head> avsnittet på HTML-sidan.

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

Kommentar

Blanda inte ihop AngularJS med Angular. Många av begreppen i AngularJS och Angular påminner om varandra, men AngularJS är föregångaren till Angular. AngularJS används fortfarande ofta för att skapa webbprogram. Medan AngularJS baseras på JavaScript, bygger Angular på TypeScript, ett programmeringsspråk som gör det enklare att skriva JavaScript-program.

Hur skapar jag programmet?

Här ska du använda en grundläggande process. Du ska skriva programkod från Cloud Shell och sedan använda SCP, eller Secure Copy Protocol, för att kopiera filerna till den virtuella datorn. Sedan startar du node.js-programmet och ser resultatet i webbläsaren.

I praktiken skriver och testar du vanligtvis webbprogrammet i en mer lokal miljö, till exempel från din bärbara dator eller från en virtuell dator som du kör lokalt. Du kan sedan lagra koden i ett versionskontrollsystem som Git och använda ett system för kontinuerlig integrering och kontinuerlig leverans – eller CI/CD – som Azure DevOps för att testa dina ändringar och ladda upp dem till den virtuella datorn. Vi hänvisar till fler resurser i slutet av den här modulen.

Skapa webbprogrammet Books

Här skapar du alla kod-, skript- och HTML-filer som utgör webbappen. För att hålla det kort framhäver vi endast särskilt viktiga delar i varje fil, utan att gå in i detalj.

Om du fortfarande är ansluten till den virtuella datorn via SSH kör du exit för att lämna SSH-sessionen och återgå till Cloud Shell.

exit

Nu är du tillbaka i Cloud Shell-sessionen.

Skapa filerna

  1. Från Cloud Shell kör du dessa kommandon för att skapa mappar och filer för ditt webbprogram:

    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
    

    Här följer en översikt:

    • Books är projektets rotkatalog.
      • server.js definierar startpunkten till webbprogrammet. Den läser in de nödvändiga Node.js-paketen, anger lyssningsporten och börjar lyssna efter inkommande HTTP-trafik.
      • package.json innehåller information om ditt program, inklusive namn, beskrivning och vilka Node.js-paket programmet ska köra.
    • Books/app innehåller kod som körs på servern.
      • model.js definierar databasanslutningen och schemat. Du kan se det som datamodellen för ditt program.
      • routes.js hanterar routning av begäranden. Den definierar exempelvis GET-begäranden till /book-slutpunkten genom att tillhandahålla en lista över alla böcker i databasen.
    • Books/public innehåller filer som hämtas direkt till klientens webbläsare.
      • index.html innehåller indexsidan. Den innehåller ett webbformulär som gör att användaren kan skicka information om böcker. Den visar också alla böcker i databasen och gör att du kan ta bort poster från databasen.
      • script.js innehåller JavaScript-kod som körs i användarens webbläsare. Den kan skicka begäranden till servern för att visa en lista över böcker, lägga till böcker i databasen och ta bort böcker från databasen.
  2. Kör kommandot code för att öppna dina filer via Cloud Shell-redigeraren.

    code Books
    

Skapa datamodellen

  1. Öppna i redigeraren app/model.js och lägg till följande:

    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;
    

    Viktigt!

    När du klistrar in eller ändrar koden i en fil i redigeringsprogrammet, är det viktigt att du sparar efteråt via menyn ”...” eller med snabbtangenten (Ctrl+S i Windows och Linux eller Cmd+S i macOS).

    I den här koden används Mongoose för att förenkla dataöverföringen till och från MongoDB. Mongoose är ett schemabaserat system för modellering av data. Koden definierar ett databasdokument med namnet ”Book” med det angivna schemat. Schemat definierar fyra fält som beskriver en enskild bok:

    • Bokens namn eller titel
    • Dess internationella standardboknummer – eller ISBN – som unikt identifierar boken
    • Bokens författare
    • Antalet sidor som den innehåller

    Nu ska du skapa HTTP-hanterare som mappar GET-, POST- och DELETE-begäranden till databasåtgärder.

Skapa Express.js-vägar som hanterar HTTP-begäranden

  1. Öppna och lägg till följande kod från redigeraren app/routes.js :

    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;
    

    Den här koden skapar fyra vägar för programmet. Här är en kort översikt över var och en.

    HTTP-verb Slutpunkt beskrivning
    GET /book Hämtar alla böcker från databasen.
    POST /book Skapar ett Book-objekt baserat på de fält som användaren har angett i webbformuläret och skriver objektet till databasen.
    DELETE /book/:isbn Tar bort en bok, baserat på dess ISBN, från databasen.
    GET * Returnerar indexsidan när ingen annan väg matchas.

    Express.js kan hantera HTTP-svar direkt i routningshanteringskoden, eller så kan det hantera statiskt innehåll från filer. Den här koden visar båda metoderna. De första tre vägarna returnerar JSON-data för API-förfrågningar om böcker. Den fjärde vägen (standardfallet) returnerar innehållet i indexfilen index.html.

Skapa JavaScript-programmet för klientsidan

  1. Öppna public/script.js från redigeraren och lägg till den här koden:

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

    Observera att den här koden definierar en modul med namnet ”myApp” och en kontrollant med namnet ”myCtrl”. Vi kommer inte att gå in i detalj på hur modulen och kontrollanten fungerar här, men du ska använda dessa namn i nästa steg för att binda användargränssnittet (HTML-kod) till programmets affärslogik.

    Tidigare skapade du fyra vägar som hanterar olika GET-, POST- och DELETE-åtgärder på servern. Den här koden liknar dessa åtgärder, men från klientsidan (användarens webbläsare).

    Funktionen getData skickar exempelvis en GET-begäran till slutpunkten /book. Kom ihåg att servern hanterar den här begäran genom att hämta information om alla böcker från databasen och returnerar informationen som JSON-data. Observera hur resulterande JSON-data tilldelas till variabeln $scope.books. Du får lära dig hur detta påverkar vad användaren ser på webbsidan i nästa steg.

    Den här koden anropar funktionen getData när sidan läses in. Du kan utforska funktionerna del_book och add_book för att få en uppfattning om hur de fungerar. Du behöver inte kod på klientsidan för att matcha serverns standardhanterare, eftersom standardhanteraren returnerar indexsidan och inte JSON-data.

Skapa användargränssnittet

  1. Öppna public/index.html från redigeraren och lägg till den här koden:

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

    Den här koden skapar ett grundläggande HTML-formulär med fyra fält för att skicka bokdata och en tabell som visar alla böcker som lagras i databasen.

    Även om det här är vanlig HTML-standardkod kanske du inte känner igen ng--HTML-attributen. Dessa HTML-attribut kopplar AngularJS-koden till användargränssnittet. När du till exempel väljer Lägg till anropar AngularJS funktionen add_book, som skickar formulärets data till servern.

    Du kan undersöka koden här för att få en uppfattning om hur vart och ett av attributen ng- relaterar till programmets affärslogik.

Skapa Express.js-servern som värd för programmet

  1. Öppna server.js från redigeraren och lägg till den här koden:

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

    Den här koden skapar själva webbprogrammet. Den hanterar statiska filer från public katalogen och använder de vägar som du definierade tidigare för att hantera begäranden.

Definiera paketinformation och beroenden

Som du kanske minns innehåller package.json information om ditt program, inklusive namn, beskrivning och vilka Node.js-paket programmet ska köra.

  1. Öppna package.json från redigeraren och lägg till den här koden:

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

Du ser information, eller metadata, om ditt program, inklusive dess namn, beskrivning och licens.

Fältet repository anger var koden finns. Om det behövs kan du granska koden senare på GitHub på URL:en som visas här.

Fältet main definierar programmets startpunkt. Det tillhandahålls här för fullständighet, men det är inte viktigt eftersom du inte planerar att publicera ditt program som ett Node.js-paket för andra att ladda ned och använda.

Fältet dependencies är viktigt. Det definierar Node.js-paketen som ditt program behöver. Snart ska du ansluta till den virtuella datorn en andra gång och köra kommandot npm install för att installera dessa paket.

Node-paket använder vanligtvis det semantiska versionshanteringsschemat. Versionsnumret innehåller tre komponenter: högre version, lägre version och uppdatering. ~-notationen (tilde) här beordrar npm att installera den senaste uppdateringsversionen under de angivna högre och lägre versionerna. De versioner som du ser här är de senaste som den här modulen testades med. I praktiken kan du öka versionen med tiden allteftersom du uppdaterar och testar ditt program med de senaste funktionerna som varje beroende paket tillhandahåller.

Kopiera filerna till den virtuella datorn

Innan du fortsätter kontrollerar du att du har den virtuella datorns IP-adress till hands. Om du inte har det kör du dessa kommandon från Cloud Shell för att hämta det:

ipaddress=$(az vm show \
  --name MeanStack \
  --resource-group "<rgn>[sandbox resource group name]</rgn>" \
  --show-details \
  --query [publicIps] \
  --output tsv)
echo $ipaddress
  1. Nu är du klar med redigeringen av filerna. Kontrollera att du har sparat ändringarna i varje fil och stäng sedan redigeraren.

    Om du vill stänga redigeraren väljer du ellipserna i det övre högra hörnet och väljer sedan Stäng redigeraren.

  2. Kör följande scp kommando för att kopiera innehållet ~/Books i katalogen i Cloud Shell-sessionen till samma katalognamn på den virtuella datorn:

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

Installera ytterligare Node-paket

Anta att du under utvecklingsprocessen upptäckte fler Node-paket som du vill använda. Som du kanske minns börjar exempelvis app/model.js med den här raden.

var mongoose = require('mongoose');

Som vi nämnde tidigare använder programmet Mongoose för att överföra data till och från MongoDB-databasen.

Programmet kräver även Express.js och body-parser-paketen. Body-parser är ett plugin-program som gör att Express kan arbeta med data från webbformuläret som skickas av klienten.

Nu ska vi ansluta till den virtuella datorn och installera de paket som du angav i package.json.

  1. Innan du ansluter till den virtuella datorn kontrollerar du att du har den virtuella datorns IP-adress till hands. Om du inte har det kör du Cloud Shell-kommandona i föregående avsnitt för att hämta det.

  2. Precis som du gjorde tidigare skapar du en SSH-anslutning till den virtuella datorn:

    ssh azureuser@$ipaddress
    
  3. Flytta till Books katalogen under hemkatalogen:

    cd ~/Books
    
  4. Kör npm install för att installera de beroende paketen:

    sudo apt install npm -y && npm install
    

Håll SSH-anslutningen öppen för nästa avsnitt.

Testa programmet

Nu är det dags att testa Node.js-webbprogrammet!

  1. Kör det här kommandot från ~/Books katalogen för att starta webbprogrammet:

    sudo nodejs server.js
    

    Det här kommandot startar programmet genom att lyssna efter inkommande HTTP-begäranden på port 80.

  2. Gå till den virtuella datorns offentliga IP-adress från en separat webbläsarflik.

    Du ser indexsidan, som innehåller ett webbformulär.

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

    Prova att lägga till några böcker till databasen. Varje gång du lägger till en bok uppdateras den fullständiga listan med böcker på sidan.

    Screenshot of the book web page with sample data populated.

    Du kan även välja Ta bort om du vill ta bort en bok från databasen.