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.
Nota
Questo esercizio è facoltativo. Se si vuole completare questo esercizio, è necessario creare una sottoscrizione di Azure prima di iniziare. Se non si ha un account Azure o non si vuole crearne uno in questo momento, è possibile leggere le istruzioni in modo da comprendere le informazioni presentate.
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
Non confondere AngularJS 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.htmlL'applicazione Web include le cartelle e i file seguenti:
-
Booksè la directory principale del progetto.-
server.jsdefinisce 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.jsoninclude informazioni sull'applicazione, tra cui il nome, la descrizione e i pacchetti Node.js che l'applicazione deve eseguire.
-
-
Books/appcontiene codice che viene eseguito nel server.-
model.jsdefinisce la connessione di database e lo schema. Può essere considerato come il modello di dati per l'applicazione. -
routes.jsgestisce il routing delle richieste. Definisce ad esempio le richieste GET all'endpoint/bookspecificando l'elenco di tutti i libri presenti nel database.
-
-
Books/publiccontiene i file serviti direttamente al browser del client.-
index.htmlcontiene 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.jscontiene 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
codeper aprire i file tramite l'editor di Cloud Shell.code Books
Creare il modello di dati
Aprire
app/model.jsdall'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
- Il suo 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.jsdall'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 Punto finale Descrizione GET /bookRecupera tutti i libri dal database. POST /bookCrea un oggetto Bookche si basa sui campi specificati dall'utente nel modulo Web e lo scrive nel database.ELIMINA /book/:isbnElimina 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.jsdall'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
myAppe 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_bookeadd_bookper 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.htmldall'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_bookche 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.jsdall'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
publice 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.jsondall'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 versionamento Semantic Versioning. 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 versioni maggiore e minore 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 "myResourceGroupName" \
--show-details \
--query [publicIps] \
--output tsv)
echo $ipaddress
Hai finito di modificare i file. 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
scpper copiare il contenuto della directory~/Booksnella 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 lo si ha, eseguire i comandi di Cloud Shell della sezione precedente per recuperarlo.
Creare una connessione SSH alla macchina virtuale, come in precedenza:
ssh azureuser@$ipaddressPassare alla directory
Booksnella home directory:cd ~/BooksEseguire
npm installper 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
~/Bookseseguire questo comando per avviare l'applicazione Web:sudo nodejs server.jsQuesto 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.