Ejercicio: Creación de una aplicación web básica
Hasta ahora, tiene MongoDB y Node.js instalado en la máquina virtual (VM) Ubuntu. Ha llegado el momento de crear una aplicación web básica para ver todo en acción. A lo largo del camino, verá cómo encaja AngularJS y Express.
Una excelente manera de aprender es con un ejemplo. La aplicación web que compila implementa una base de datos de libros básica. La aplicación web le permite mostrar información sobre libros, agregar nuevos libros y eliminar los existentes.
La aplicación web que ve aquí muestra muchos conceptos que se aplican a la mayoría de las aplicaciones web de pila MEAN. Según sus necesidades e intereses, puede explorar las características que necesita para compilar sus propias aplicaciones de la pila MEAN.
Este es el aspecto de la aplicación web Books.
Así es como encaja cada componente de la pila MEAN.
- MongoDB almacena información sobre los libros.
- Express.js enruta cada solicitud HTTP al controlador adecuado.
- AngularJS conecta la interfaz de usuario con la lógica de negocios del programa.
- Node.js hospeda la aplicación del lado servidor.
Importante
Con fines de aprendizaje, aquí creará una aplicación web básica. Su objetivo es probar la pila MEAN y ayudarle a hacerse una idea de cómo funciona. La aplicación no es lo suficientemente segura ni está preparada para su uso en producción.
¿Qué ocurre con Express?
Hasta ahora, ha instalado MongoDB y Node.js en la máquina virtual. ¿Qué ocurre con Express.js, la E del acrónimo MEAN?
Express.js es un marco de servidor web creado para Node.js que simplifica el proceso para compilar aplicaciones web.
El objetivo principal de Express es controlar el enrutamiento de solicitudes. Enrutamiento hace referencia a cómo responde la aplicación a una solicitud a un punto de conexión concreto. Los puntos de conexión están formados por una ruta de acceso, o URI, y un método de solicitud, como GET o POST. Por ejemplo, podría responder a una solicitud GET al punto de conexión /book
al proporcionar la lista de todos los libros de la base de datos. Puede responder a una solicitud POST al punto de conexión de /book
agregando una entrada a la base de datos en función de los campos que el usuario escribió en un formulario web.
En la aplicación web que se compila en breve, se usa Express para enrutar solicitudes HTTP y devolver contenido web al usuario. Express también puede ayudar a las aplicaciones web a trabajar con cookies HTTP y a procesar cadenas de consulta.
Express es un paquete de Node.js. Usará la utilidad npm que viene con Node.js para instalar y administrar paquetes de Node.js. Más adelante en esta unidad, creará un archivo denominado package.json
para definir Express y otras dependencias y, a continuación, ejecutará el comando npm install
para instalar estas dependencias.
¿Qué ocurre con AngularJS?
Como Express, AngularJS, el A en el acrónimo MEAN todavía no está instalado.
AngularJS hace que las aplicaciones web sean fáciles de escribir y probar porque le permite separar mejor la apariencia de la página web, el código HTML, de su comportamiento. Si está familiarizado con el patrón del controlador de vista–de modelos–(MVC) o el concepto de enlace de datos, AngularJS debe familiarizarse con usted.
AngularJS es un front-end marco de JavaScript, lo que significa que solo debe estar disponible en el cliente que tiene acceso a la aplicación. En otras palabras, AngularJS se ejecuta en el explorador web del usuario, no en su servidor web. Y dado que AngularJS es JavaScript, puede usarlo para capturar fácilmente los datos del servidor web para mostrarlos en la página.
En realidad, AngularJS no se instala. En su lugar, agrega una referencia al archivo JavaScript en la página HTML, tal y como se hace con otras bibliotecas de JavaScript. Hay varias maneras de incluir AngularJS en las páginas web. Aquí, carga AngularJS desde una red de entrega de contenido (CDN). Una red CDN es una forma de distribuir imágenes, vídeos y otro contenido de forma geográfica para mejorar las velocidades de descarga.
Aún no agregue este código, pero este es un ejemplo que carga AngularJS desde una red CDN. Normalmente, agrega este código a la sección <head>
de la página HTML.
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.2/angular.min.js"></script>
Nota:
No confunda AngularJS con Angular. Aunque muchos de los conceptos son similares en ambos, AngularJS es el predecesor de Angular. AngularJS se sigue usando habitualmente para compilar aplicaciones web. Mientras que AngularJS se basa en JavaScript, Angular se basa en TypeScript, un lenguaje de programación que facilita la escritura de programas de JavaScript.
¿Cómo se compila la aplicación?
Aquí se usa un proceso básico. Puede escribir código de aplicación desde Cloud Shell y, a continuación, usar el protocolo de copia segura (SCP) para copiar los archivos en la máquina virtual. A continuación, inicie la aplicación Node.js y vea los resultados en el explorador.
En la práctica, normalmente escribiría y probaría la aplicación web en un entorno más local, como su portátil o una máquina virtual que ejecute de forma local. Puede almacenar el código en un sistema de control de versiones, como Git. A continuación, use un sistema de integración continua y entrega continua (CI/CD), como Azure DevOps, para probar los cambios y cargarlos en la máquina virtual. Le apuntamos a más recursos al final de este módulo.
Creación de la aplicación web de libros
Aquí, creará todo el código, el script y los archivos HTML que componen la aplicación web. Por motivos de brevedad, se resaltan las partes importantes de cada archivo, pero no se incluyen detalles completos.
Si aún está conectado a la máquina virtual por medio de SSH, ejecute exit
para salir de la sesión de SSH y volver a Cloud Shell.
exit
Ha vuelto a la sesión de Cloud Shell.
Creación de los archivos
En Cloud Shell, ejecute estos comandos para crear las carpetas y los archivos de la aplicación 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
La aplicación web incluye las siguientes carpetas y archivos:
Books
es el directorio raíz del proyecto.server.js
define el punto de entrada a la aplicación web. Carga los paquetes de Node.js necesarios, especifica el puerto en el que escuchar y empieza a escuchar el tráfico HTTP entrante.package.json
proporciona información sobre la aplicación, como su nombre, descripción y qué paquetes de Node.js debe ejecutar la aplicación.
Books/app
contiene código que se ejecuta en el servidor.model.js
define la conexión de base de datos y el esquema. Piense en ella como el modelo de datos de la aplicación.routes.js
controla el enrutamiento de solicitudes. Por ejemplo, define las solicitudes GET al punto de conexión/book
al proporcionar la lista de todos los libros de la base de datos.
Books/public
contiene archivos que se entregan directamente en el explorador del cliente.index.html
contiene la página de índice. Contiene un formulario web que permite al usuario enviar información sobre los libros. También muestra todos los libros de la base de datos y le permite eliminar las entradas de la base de datos.script.js
contiene el código JavaScript que se ejecuta en el explorador del usuario. Puede enviar solicitudes al servidor para mostrar libros, agregar libros a la base de datos y eliminar libros de la base de datos.
Ejecute el comando
code
para abrir los archivos mediante el editor de Cloud Shell.code Books
Creación del modelo de datos
En el editor, abra
app/model.js
y agregue el siguiente código: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
Cada vez que pegue o cambie código en un archivo en el editor, no olvide guardarlo posteriormente mediante el menú "..." o la tecla de aceleración (CTRL+S en Windows y Linux, o Comando+S en macOS).
Este código usa Mongoose para simplificar el proceso de transferencia de datos dentro y fuera de MongoDB. Mongoose es un sistema basado en esquemas para modelar datos. El código define un documento de base de datos denominado "Book" (Libro) con el esquema proporcionado. El esquema define cuatro campos que describen un único libro:
- El nombre o título del libro.
- Su número de libro estándar internacional (ISBN) que identifica de forma única el libro
- Su autor.
- El número de páginas que contiene.
A continuación, creará controladores HTTP que asignan solicitudes GET, POST y DELETE a las operaciones de base de datos.
Creación de las rutas de Express.js que controlan las solicitudes HTTP
En el editor, abra
app/routes.js
y agregue el siguiente código: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;
Este código crea cuatro rutas para la aplicación. Aquí tiene una breve descripción de cada una.
Verbo HTTP Punto de conexión Descripción GET /book
Recupera todos los libros de la base de datos. POST /book
Crea un objeto Book
en función de los campos que haya proporcionado el usuario en el formulario web y escribe ese objeto en la base de datos.DELETE /book/:isbn
Elimina el libro tal y como identifica su ISBN de la base de datos. GET *
Devuelve la página de índice cuando no coincide ninguna otra ruta. Express.js puede suministrar respuestas HTTP directamente en el código de control de ruta, o bien puede suministrar contenido estático desde los archivos. Este código muestra ambas opciones. Las tres primeras rutas devuelven datos JSON para las solicitudes de la API de libros. La cuarta ruta (el caso predeterminado) devuelve el contenido del archivo de índice
index.html
.
Creación de la aplicación de JavaScript del lado cliente
En el editor, abra
public/script.js
y agregue este código: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); }); }; });
Observe cómo este código define un módulo denominado
myApp
y un controlador denominadomyCtrl
. No entraremos en detalles sobre cómo funcionan aquí el módulo y los controladores, pero usará estos nombres en el paso siguiente para enlazar la interfaz de usuario (código HTML) con la lógica de negocios de la aplicación.Antes ha creado cuatro rutas que controlan varias operaciones GET, POST y DELETE en el servidor. Este código es similar a esas mismas operaciones, pero desde el lado cliente (el explorador web del usuario).
La función
getData
, por ejemplo, envía una solicitud GET al punto de conexión/book
. Recuerde que el servidor controla esta solicitud recuperando información acerca de todos los libros de la base de datos y devolviendo esa información como datos JSON en la respuesta. Observe cómo se asignan los datos JSON de la respuesta a la$scope.books
variable. Obtendrá información sobre cómo este código afecta a lo que ve el usuario en la página web en el paso siguiente.Este código llama a la función
getData
cuando se carga la página. Puede examinar las funcionesdel_book
yadd_book
para hacerse una idea de cómo funcionan. No es necesario que el código del lado cliente coincida con el controlador predeterminado del servidor, ya que el controlador predeterminado devuelve la página de índice y no los datos JSON.
Creación de la interfaz del usuario
En el editor, abra
public/index.html
y agregue este código:<!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>
Este código crea un formulario HTML básico con cuatro campos para enviar datos de libros y una tabla que muestra todos los libros que están almacenados en la base de datos.
Aunque este código HTML es estándar, es posible que el
ng-
atributos HTML no esté familiarizado. Estos atributos HTML conectan el código de AngularJS con la interfaz de usuario. Por ejemplo, al seleccionar Agregar, AngularJS llama a la funciónadd_book
, que envía los datos del formulario al servidor.Puede examinar el código aquí para hacerse una idea de cómo se relaciona cada uno de los atributos
ng-
con la lógica de negocios de la aplicación.
Creación del servidor Express.js para hospedar la aplicación
En el editor, abra
server.js
y agregue este código: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')); });
Este código crea la propia aplicación web. Suministra archivos estáticos desde el directorio
public
y usa las rutas que ha definido anteriormente para controlar las solicitudes.
Definición de las dependencias y la información del paquete
Recuerde que package.json
proporciona información sobre la aplicación, como su nombre, descripción y qué paquetes de Node.js debe ejecutar la aplicación.
En el editor, abra
package.json
y agregue este código:{ "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" } }
Verá información o metadatos sobre la aplicación, como su nombre, descripción y licencia.
El campo repository
especifica dónde se mantiene el código. Como referencia, más adelante puede revisar el código en GitHub en la URL que se muestra aquí.
El campo main
define el punto de entrada de la aplicación. Lo proporcionamos aquí para su integridad. Sin embargo, el punto de entrada solo es importante si planea publicar la aplicación como un paquete de Node.js para que otros usuarios descarguen y usen.
El campo dependencies
es importante. Define los paquetes de Node.js que necesita la aplicación. En breve, se conecta a la máquina virtual una segunda vez y se ejecuta el comando npm install
para instalar estos paquetes.
Los paquetes de Node suelen usar el esquema de control de versiones Versionamiento Semántico. El número de versión contiene tres componentes: versión principal, versión secundaria y revisión. La notación de tilde ~
indica a npm que instale la versión de revisión más reciente de las versiones principales y secundarias proporcionadas. Las versiones que ve aquí son las más recientes con las que se ha probado este módulo. En la práctica, puede aumentar la versión con el paso del tiempo conforme actualiza y prueba la aplicación para usar las características más recientes que proporciona cada paquete dependiente.
Copia de los archivos en la máquina virtual
Antes de continuar, asegúrese de que tiene a mano la dirección IP de la máquina virtual. Si no la tiene, ejecute estos comandos en Cloud Shell para recuperarla:
ipaddress=$(az vm show \
--name MeanStack \
--resource-group "<rgn>[sandbox resource group name]</rgn>" \
--show-details \
--query [publicIps] \
--output tsv)
echo $ipaddress
Ya ha terminado de editar los archivos. Asegúrese de guardar los cambios de cada archivo y, después, cierre el editor.
Para cerrar el editor, seleccione los puntos suspensivos que aparecen en la esquina y elija Cerrar editor.
Ejecute el siguiente comando
scp
para copiar el contenido del directorio~/Books
de la sesión de Cloud Shell en el mismo nombre de directorio de la máquina virtual.scp -r ~/Books azureuser@$ipaddress:~/Books
Instalación de más paquetes de Node
Supongamos que, durante el proceso de desarrollo, ha identificado más paquetes de Node que desea usar. Por ejemplo, recuerde que app/model.js
comienza con esta línea.
var mongoose = require('mongoose');
Recuerde que la aplicación usa Mongoose para ayudar a transferir datos dentro y fuera de la base de datos de MongoDB.
La aplicación también requiere Express.js y los paquetes de body-parser. body-parser es un complemento que permite que Express trabaje con datos del formulario web que ha enviado el cliente.
Conéctese a la máquina virtual e instale los paquetes que ha especificado en package.json
.
Antes de conectarse a la máquina virtual, asegúrese de que tiene a mano la dirección IP de la máquina virtual. Si no lo tiene, ejecute los comandos de Cloud Shell de la sección anterior para recuperarla.
Cree una conexión SSH a la máquina virtual, como hizo anteriormente:
ssh azureuser@$ipaddress
Vaya al directorio
Books
dentro del directorio de inicio:cd ~/Books
Ejecute
npm install
para instalar los paquetes dependientes:sudo apt install npm -y && npm install
Mantenga la conexión SSH abierta para la sección siguiente.
Prueba de la aplicación
Ya está listo para probar su aplicación web de Node.js.
En el directorio
~/Books
, ejecute este comando para iniciar la aplicación web:sudo nodejs server.js
Este comando inicia la aplicación al escuchar en el puerto 80 las solicitudes HTTP entrantes.
En otra pestaña del explorador, vaya a la dirección IP pública de su máquina virtual.
Verá la página de índice, que incluye un formulario web.
Intente agregar algunos libros a la base de datos. Cada vez que agrega un libro, la página actualiza la lista completa de libros.
Para eliminar un libro de la base de datos, también puede seleccionar Eliminar.