Esercizio - Creare un'applicazione Web di base
Per il momento nella macchina virtuale Ubuntu sono stati installati MongoDB e Node.js. A questo punto, è ora di creare un'applicazione Web di base per vedere tutto in azione. Lungo il percorso, si comprende anche il ruolo di AngularJS ed Express.
Un modo eccellente per imparare è seguire un esempio. L'applicazione Web che viene creata implementa un database di libri di base. L'applicazione Web consente di visualizzare informazioni sui libri, aggiungere nuovi libri ed eliminare libri esistenti.
L'applicazione Web usata di seguito illustra anche molti concetti che si applicano alla maggior parte delle applicazioni Web dello stack MEAN. In base alle proprie esigenze e interessi, è possibile esplorare le funzionalità necessarie per le proprie applicazioni dello stack MEAN.
Ecco che aspetto ha l'applicazione Web Books.
Ecco il compito di ogni componente dello stack MEAN.
- In MongoDB si archiviano le informazioni sui libri.
- Express.jsi esegue il routing di ogni richiesta HTTP al gestore appropriato.
- Con AngularJS si connette l'interfaccia utente con la logica di business del programma.
- In Node.js si ospita l'applicazione lato server.
Importante
Ai fini dell'apprendimento, in questo esercizio si creerà un'applicazione Web di base. Il suo scopo è testare lo stack MEAN per ricavarne un'idea del funzionamento. L'applicazione non è sufficientemente sicura né pronta per essere usata in un ambiente di produzione.
Informazioni su Express
Finora, nella macchina virtuale sono stati installati MongoDB e Node.js. Che cos'è Express.js, la E nell'acronimo MEAN?
Express.js è un framework di server Web creato per Node.js che semplifica il processo di creazione delle applicazioni Web.
Lo scopo principale di Express è di gestire il routing delle richieste. Per routing si intende il modo in cui l'applicazione risponde a una richiesta proveniente da un endpoint specifico. Un endpoint è costituito da un percorso, o URI, e da un metodo di richiesta, ad esempio GET o POST. Ad esempio, si può rispondere a una richiesta GET all'endpoint /book
specificando l'elenco di tutti i libri presenti nel database. Si può rispondere a una richiesta POST all'endpoint /book
aggiungendo una voce al database sulla base dei campi che l'utente ha immesso in un modulo Web.
Nell'applicazione Web che viene creata a breve si usa Express per il routing delle richieste HTTP e per la restituzione del contenuto Web all'utente. Express facilita anche l'uso delle applicazioni Web con i cookie HTTP, oltre che l'elaborazione delle stringhe di query.
Express è un pacchetto Node.js. Per installare e gestire i pacchetti Node.js si usa l'utilità npm. Più avanti in questa unità, si crea un file denominato package.json
per definire Express e altre dipendenze, quindi si esegue il comando npm install
per installare tali dipendenze.
Informazioni su AngularJS
Al pari di Express, neanche AngularJS, la lettera A dell'acronimo MEAN, è stato ancora installato.
AngularJS facilita la scrittura e il test delle applicazioni Web in quanto consente di separare meglio l'aspetto della pagina Web, vale a dire il codice HTML, dal suo comportamento. Se si ha familiarità con il modello model-view-controller (MVC) o con il concetto di data binding, AngularJS non riserva sorprese.
AngularJS è un framework JavaScript front-end, pertanto è sufficiente che sia disponibile nel client che accede all'applicazione. In altre parole, AngularJS viene eseguito nel browser Web dell'utente, non nel server Web. Poiché AngularJS è un linguaggio JavaScript, è possibile usarlo per recuperare facilmente dal server Web i dati da visualizzare nella pagina.
AngularJS non viene installato. Un riferimento al file JavaScript viene invece aggiunto alla pagina HTML, proprio come accade con altre librerie JavaScript. È possibile includere AngularJS nelle pagine Web in diversi modi. In questo caso si carica AngularJS da una rete per la distribuzione di contenuti (CDN). Una rete CDN è un modo per distribuire geograficamente immagini, video e altri contenuti migliorando la velocità di download.
Per il momento, non aggiungere questo codice. Nell'esempio riportato di seguito AngularJS viene caricato da una rete CDN. Il codice viene in genere aggiunto alla sezione <head>
della pagina HTML.
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.2/angular.min.js"></script>
Nota
AngularJS non va confuso con Angular. Anche se in entrambi molti concetti sono simili, AngularJS è il predecessore di Angular. AngularJS viene ancora comunemente usato per la compilazione di applicazioni Web. Mentre AngularJS si basa su JavaScript, Angular si basa su TypeScript, un linguaggio di programmazione che semplifica la scrittura di programmi JavaScript.
Come si compila l'applicazione?
In questo esempio si usa un processo di base. Si scrive il codice dell'applicazione di Cloud Shell e quindi si usa il protocollo per la copia di sicurezza (SCP) per copiare i file nella macchina virtuale. L'applicazione Node.js viene quindi avviata e i risultati vengono visualizzati nel browser.
In pratica, l'applicazione Web viene scritta e testata in un ambiente più locale, ad esempio un portatile o una macchina virtuale eseguita localmente. È possibile archiviare il codice in un sistema di controllo della versione, ad esempio Git. Quindi, usare un sistema di integrazione continua e recapito continuo (CI/CD), ad esempio Azure DevOps, per testare le modifiche e caricarle nella macchina virtuale. Altre risorse utili sono indicate alla fine di questo modulo.
Creare l'applicazione Web Books
In questa unità si crea tutto il codice, gli script e i file HTML che costituiscono l'applicazione Web. Per brevità, le parti importanti di ogni file sono evidenziate, ma non vengono incluse informazioni dettagliate complete.
Se si è ancora connessi alla macchina virtuale con SSH, eseguire exit
per chiudere la sessione SSH e tornare a Cloud Shell.
exit
Ora si è di nuovo nella sessione Cloud Shell.
Creare i file
In Cloud Shell eseguire questi comandi per creare le cartelle e i file per l'applicazione Web:
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
L'applicazione Web include le cartelle e i file seguenti:
Books
è la directory radice del progetto.server.js
definisce il punto di ingresso all'applicazione Web. Carica i pacchetti Node.js necessari, specifica la porta di ascolto e inizia l'ascolto del traffico HTTP in ingresso.package.json
include informazioni sull'applicazione, tra cui il nome, la descrizione e i pacchetti Node.js che l'applicazione deve eseguire.
Books/app
contiene codice che viene eseguito nel server.model.js
definisce la connessione di database e lo schema. Può essere considerato come il modello di dati per l'applicazione.routes.js
gestisce il routing delle richieste. Definisce ad esempio le richieste GET all'endpoint/book
specificando l'elenco di tutti i libri presenti nel database.
Books/public
contiene i file serviti direttamente al browser del client.index.html
contiene la pagina di indice. Contiene un modulo Web che consente all'utente di inviare informazioni relative ai libri. Visualizza anche tutti i libri presenti nel database e consente di eliminare le voci dal database.script.js
contiene il codice JavaScript eseguito nel browser dell'utente. Può inviare richieste al server per ottenere l'elenco dei libri, aggiungere libri al database ed eliminare libri dal database.
Eseguire il comando
code
per aprire i file tramite l'editor di Cloud Shell.code Books
Creare il modello di dati
Aprire
app/model.js
dall'editor e aggiungere il codice seguente: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;
Importante
Ogni volta che si incolla o si modifica il codice in un file nell'editor, assicurarsi di salvare le modifiche tramite il menu "..." o il tasto di scelta rapida (CTRL+S in Windows e Linux o Comando+S in macOS).
Il codice usa Mongoose per semplificare il processo di trasferimento dei dati da e verso MongoDB. Mongoose è un sistema basato su schema per la modellazione dei dati. Il codice definisce un documento di database denominato "Book" con lo schema specificato. Lo schema definisce quattro campi che descrivono un singolo libro:
- Nome o titolo del libro
- Codice ISBN (International Standard Book Number) che identifica il libro in modo univoco
- Autore
- Numero di pagine contenute
Creare quindi i gestori HTTP che eseguono il mapping delle richieste GET, POST e DELETE alle operazioni del database.
Creare le route di Express.js che gestiscono le richieste HTTP
Aprire
app/routes.js
dall'editor e aggiungere il codice seguente: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;
Il codice crea quattro route per l'applicazione. Di seguito viene offerta una breve panoramica di ognuna.
Verbo HTTP Endpoint Descrizione GET /book
Recupera tutti i libri dal database. POST /book
Crea un oggetto Book
che si basa sui campi specificati dall'utente nel modulo Web e lo scrive nel database.DELETE /book/:isbn
Elimina il libro identificato dal codice ISBN dal database. GET *
Restituisce la pagina di indice quando non si trova una corrispondenza con altre route. Express.js può gestire le risposte HTTP direttamente nel codice di gestione delle route oppure può gestire il contenuto statico proveniente dai file. In questo codice sono illustrati entrambi. Le prime tre route restituiscono dati JSON per le richieste API relative ai libri. La quarta route (il caso predefinito) restituisce il contenuto del file di indice,
index.html
.
Creare l'applicazione JavaScript lato client
Aprire
public/script.js
dall'editor e aggiungere il codice seguente: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); }); }; });
Si noti come il codice definisca un modulo denominato
myApp
e un controller denominatomyCtrl
. Il funzionamento di moduli e controller non verrà illustrato in dettaglio in questa sede, ma questi nomi verranno usati nel passaggio successivo per associare l'interfaccia utente (codice HTML) alla logica di business dell'applicazione.In precedenza sono state create quattro route che gestiscono varie operazioni GET, POST e DELETE nel server. Questo codice è simile a quelle stesse operazioni, ma sul lato client, ovvero il Web browser dell'utente.
La funzione
getData
, ad esempio, invia una richiesta GET all'endpoint/book
. È importante ricordare che il server gestisce la richiesta recuperando dal database le informazioni relative a tutti i libri e restituendo queste informazioni sotto forma di dati JSON nella risposta. Si noti come i dati JSON nella risposta vengano assegnati alla variabile$scope.books
. Nel prossimo passaggio si apprende come il codice influisca su quanto viene visualizzato dall'utente nella pagina Web.Al caricamento della pagina, il codice chiama la funzione
getData
. È possibile esaminare le funzionidel_book
eadd_book
per avere un'idea del relativo funzionamento. Per la corrispondenza con il gestore predefinito del server non è necessario alcun codice lato client perché il gestore predefinito restituisce la pagina di indice, non dati JSON.
Creare l'interfaccia utente
Aprire
public/index.html
dall'editor e aggiungere il codice seguente:<!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>
Il codice crea un modulo HTML di base con quattro campi per inviare i dati relativi ai libri e una tabella in cui sono visualizzati tutti i libri archiviati nel database.
Anche se questo codice HTML è standard, gli attributi HTML
ng-
potrebbero essere poco noti. Questi attributi HTML collegano il codice AngularJS all'interfaccia utente. Ad esempio, quando si seleziona Aggiungi, AngularJS chiama la funzioneadd_book
che invia i dati del modulo al server.È possibile esaminare il codice per avere un'idea di come ognuno degli attributi
ng-
si colleghi alla logica di business dell'applicazione.
Creare il server Express.js per ospitare l'applicazione
Aprire
server.js
dall'editor e aggiungere il codice seguente: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')); });
Questo codice crea l'applicazione Web vera e propria. Rende disponibili i file statici dalla directory
public
e usa le route definite in precedenza per gestire le richieste.
Definire informazioni e dipendenze del pacchetto
Tenere presente che package.json
offre informazioni sull'applicazione, tra cui il nome, la descrizione e i pacchetti Node.js che l'applicazione deve eseguire.
Aprire
package.json
dall'editor e aggiungere il codice seguente:{ "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" } }
Verranno visualizzati informazioni o metadati riguardanti l'applicazione, tra cui il nome, la descrizione e la licenza.
Il campo repository
specifica il punto in cui è conservato il codice. Per informazioni di riferimento, è poi possibile esaminare il codice su GitHub all'URL indicato qui.
Il campo main
definisce il punto di ingresso dell'applicazione. Lo forniamo qui per completezza. Tuttavia, il punto di ingresso è importante solo se si prevede di pubblicare l'applicazione come pacchetto Node.js in modo che altri utenti possano scaricarlo e usarlo.
Il campo dependencies
è invece importante. Definisce i pacchetti Node.js necessari per l'applicazione. A breve si viene connessi alla macchina virtuale una seconda volta e si esegue il comando npm install
per installare questi pacchetti.
I pacchetti Node usano generalmente lo schema di controllo delle versioni Schema di controllo delle versioni. Il numero di versione contiene tre componenti: versione principale, versione secondaria e patch. La notazione ~
tilde indica a npm di installare la versione patch più recente nelle versione principale e in quella secondaria specificate. Le versioni visualizzate si riferiscono alle versioni più recenti usate per testare il modulo. In pratica, è possibile incrementare poi la versione mentre si aggiorna ed esegue il test dell'applicazione, se si vogliono usare le funzionalità più recenti offerte da ogni pacchetto dipendente.
Copiare i file nella macchina virtuale
Prima di procedere, assicurarsi di avere a portata di mano l'indirizzo IP della macchina virtuale. In caso contrario, eseguire questi comandi da Cloud Shell per recuperarlo:
ipaddress=$(az vm show \
--name MeanStack \
--resource-group "<rgn>[sandbox resource group name]</rgn>" \
--show-details \
--query [publicIps] \
--output tsv)
echo $ipaddress
La modifica dei file è stata completata. Assicurarsi di aver salvato le modifiche apportate a ogni file e chiudere l'editor.
Per chiudere l'editor, selezionare i puntini di sospensione nell'angolo in alto a destra e quindi selezionare Chiudi editor.
Eseguire il comando seguente
scp
per copiare il contenuto della directory~/Books
nella sessione di Cloud Shell nello stesso nome di directory nella macchina virtuale:scp -r ~/Books azureuser@$ipaddress:~/Books
Installare altri pacchetti Node
Si supponga che durante il processo di sviluppo siano stati identificati altri pacchetti Node che si vogliono usare. Ad esempio si tenga presente che app/model.js
inizia con questa riga.
var mongoose = require('mongoose');
È importante ricordare che l'applicazione usa Mongoose per consentire il trasferimento dei dati da e verso il database MongoDB.
Per l'applicazione sono necessari anche Express.js e i pacchetti body-parser. Body-parser è un plug-in che consente a Express di usare i dati contenuti nel modulo Web inviato dal client.
Connettersi alla macchina virtuale e installare i pacchetti specificati in package.json
.
Prima di connettersi alla macchina virtuale, assicurarsi di avere l'indirizzo IP della macchina virtuale. Se non si ha l'indirizzo, eseguire i comandi di Cloud Shell della sezione precedente per recuperarlo.
Creare una connessione SSH alla macchina virtuale, come in precedenza:
ssh azureuser@$ipaddress
Passare alla directory
Books
nella home directory:cd ~/Books
Eseguire
npm install
per installare i pacchetti dipendenti:sudo apt install npm -y && npm install
Mantenere la connessione SSH aperta per la sezione successiva.
Testare l'applicazione
A questo punto è possibile testare l'applicazione Web Node.js.
Dalla directory
~/Books
eseguire questo comando per avviare l'applicazione Web:sudo nodejs server.js
Questo comando avvia l'applicazione restando in ascolto sulla porta 80 per le richieste HTTP in ingresso.
In un'altra scheda del browser, passare all'indirizzo IP pubblico della macchina virtuale.
Verrà visualizzata la pagina di indice che include un modulo Web.
Provare ad aggiungere alcuni libri al database. Dopo aver aggiunto un libro, la pagina aggiorna l'elenco completo dei libri.
Per eliminare un libro dal database, è anche possibile selezionare Elimina.