Упражнение. Создание базового веб-приложения

Завершено

Итак, вы установили MongoDB и Node.js на виртуальной машине Ubuntu. Теперь пора создать базовое веб-приложение, чтобы увидеть все это в действии. Также вы узнаете, какую роль в этом играют AngularJS и Express.

Лучше всего учиться на примере. Вы создадите веб-приложение, которое реализует простую базу данных книг. Веб-приложение позволяет перечислять сведения о книгах, добавлять новые книги и удалять существующие книги.

Веб-приложение, которое вы увидите здесь, демонстрирует принципы, применимые к большинству веб-приложений в стеке MEAN. Исходя из ваших потребностей и интересов, изучите возможности, необходимые для создания ваших приложений в стеке MEAN.

Вот как выглядит веб-приложение с книгами.

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

Вот какую роль играет каждый компонент стека MEAN.

  • MongoDB хранит сведения о книгах.
  • Express.js направляет каждый HTTP-запрос в соответствующий обработчик.
  • AngularJS соединяет пользовательский интерфейс с бизнес-логикой программы.
  • Node.js размещает приложение на стороне сервера.

Важно!

В целях обучения здесь вы создаете базовое веб-приложение. Вы изучаете стек MEAN и принципы его работы. Приложение недостаточно безопасно или готово для использования в рабочей среде.

Как насчет Express?

Пока вы установили MongoDB и Node.js на виртуальной машине. Как насчет Express.js — E в акрониме MEAN?

Express.js — это платформа веб-сервера, построенная для Node.js и упрощающая процесс сборки веб-приложений.

Основная цель Express — обрабатывать маршрутизацию запросов. Маршрутизация определяет, как приложение отвечает на запрос к конкретной конечной точке. Конечная точка состоит из пути или URI, а также метода запроса, например GET или POST. Например, вы можете ответить на запрос GET к конечной точке /book, предоставив список всех книг в базе данных. Вы можете ответить на запрос POST к той же конечной точке, добавив запись в базу данных на основе полей, заполненных пользователем в веб-форме.

В веб-приложении, которое вы создадите чуть позже, вы будете использовать Express для перенаправления HTTP-запросов и возврата веб-содержимого пользователю. Express также помогает веб-приложениям работать с файлами cookie HTTP и обрабатывать строки запроса.

Express — это пакет Node.js. Используйте служебную программу npm, которая поставляется с Node.js, для установки пакетов Node.js и управления ими. Далее в этом уроке вы создадите файл с именем package.json express и других зависимостей, а затем выполните npm install команду, чтобы установить эти зависимости.

Как насчет AngularJS?

Вы еще не установили AngularJS — A в акрониме MEAN.

AngularJS упрощает запись и тестирование веб-приложений, так как позволяет лучше отделять внешний вид веб-страницы — html-код от того, как работает веб-страница. Если вы знакомы с шаблоном "Модель — представление — контроллер" (MVC) или понятием привязки данных, вам будет легко понять AngularJS.

AngularJS называется интерфейсной платформой JavaScript, то есть она должна быть доступна только клиенту, который обращается к приложению. Иными словами, AngularJS выполняется в веб-браузере пользователя, не на веб-сервере. И поскольку AngularJS — это JavaScript, можно использовать его, чтобы легко извлечь данные с веб-сервера для отображения на странице.

Вы не устанавливаете AngularJS в прямом смысле. Вместо этого вы добавляете ссылку на файл JavaScript на HTML-странице, как вы это делаете с другими библиотеками JavaScript. Существует несколько способов включить AngularJS в веб-страницы. Здесь вы загрузите AngularJS из сети доставки содержимого или CDN. CDN — это способ распределения изображений, видео и другого содержимого географически для повышения скорости загрузки.

Пока не добавляйте этот код, но посмотрите пример, который загружает AngularJS из CDN. Обычно этот код добавляется в <head> раздел HTML-страницы.

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

Примечание.

Не путайте AngularJS с Angular. Хотя многие понятия в них похожи, AngularJS предшествует Angular. AngularJS по-прежнему широко используется для создания веб-приложений. AngularJS основан на JavaScript, а Angular основан на TypeScript, языке программирования, который упрощает написание программ JavaScript.

Создание приложения

Здесь вы используете базовый процесс. Вы напишете код приложения в Cloud Shell и с помощью SCP, или протокола безопасного копирования, скопируете файлы на виртуальную машину. Затем вы запустите приложение Node.js и увидите результаты в браузере.

На практике обычно вы будете писать и тестировать веб-приложение в более локальной среде, например с ноутбука или виртуальной машины, которую вы запускаете локально. Затем вы можете сохранить код в системе управления версиями, например Git, и использовать непрерывную интеграцию и непрерывную доставку (или CI/CD), например Azure DevOps для тестирования изменений и отправки их на виртуальную машину. Мы предоставим вам дополнительные ресурсы в конце этого модуля.

Создание веб-приложения базы данных с книгами

Здесь вы создадите весь код, скрипт и HTML-файлы, составляющие веб-приложение. Для краткости мы выделим самые важные части каждого файла, но не будем углубляться в подробности.

Если к виртуальной машине по-прежнему установлено подключение по протоколу SSH, выполните команду exit, чтобы завершить сеанс SSH и вернуться к Cloud Shell.

exit

Вы вернулись в сеанс Cloud Shell.

Создание файлов

  1. В Cloud Shell выполните следующие команды, чтобы создать папки и файлы для веб-приложения:

    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
    

    Вот что у вас должно быть:

    • Books — корневой каталог проекта.
      • server.js определяет точку входа в веб-приложение. Он загружает необходимые пакеты Node.js, указывает порт для прослушивания и начинает ожидать входящий HTTP-трафик.
      • package.json предоставляет сведения о приложении, включая имя, описание и необходимые для запуска пакеты Node.js.
    • Books/app содержит код, выполняемый на сервере.
      • model.js определяет подключение к базе данных и схему. Его можно рассматривать как модель данных для приложения.
      • routes.js обрабатывает маршрутизацию запросов. Например, он определяет запросы GET к конечной точке /book, предоставляя список всех книг в базе данных.
    • Books/public содержит файлы, которые обслуживаются непосредственно в клиентском браузере.
      • index.html содержит страницу индекса. Он содержит веб-форму, которая позволяет пользователям отправлять сведения о книгах. Он также отображает все книги в базе данных и позволяет удалять записи из базы данных.
      • script.js содержит код JavaScript, выполняемый в браузере пользователя. Он может отправлять запросы к серверу, чтобы вывести список книг, добавить книги в базу данных и удалить книги из базы данных.
  2. Выполните команду code, чтобы открыть файлы через редактор Cloud Shell.

    code Books
    

Создание модели данных

  1. В редакторе откройте 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;
    

    Важно!

    Когда вы вставляете фрагменты или изменяете код в файле в редакторе, сохраняйте изменения через меню "..." или используя сочетание клавиш (CTRL+S в Windows и Linux и Command+S в macOS).

    Этот код использует Mongoose для упрощения процесса передачи данных в MongoDB и обратно. Mongoose — это система для моделирования данных на основе схемы. Код определяет документ базы данных с именем Book с помощью заданной схемы. Схема определяет четыре поля, которые описывают одну книгу:

    • название книги, или заголовок;
    • Его международный номер стандартной книги (или ISBN), который однозначно идентифицирует книгу
    • автора;
    • количество страниц.

    Затем вы создадите обработчики HTTP-данных, которые сопоставляют запросы GET, POST и DELETE с операциями в базе данных.

Создание маршрутов Express.js, которые обрабатывают HTTP-запросы

  1. В редакторе откройте 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;
    

    Этот код создает четыре маршрута для приложения. Вот краткий обзор каждого.

    HTTP-команда Конечная точка Description
    GET /book Извлекает все книги из базы данных.
    POST /book Создает объект Book на основе полей, заполненных пользователем в веб-форме, и записывает этот объект в базу данных.
    DELETE /book/:isbn Удаляет книги из базы данных по номеру ISBN.
    GET * Возвращает страницу индексов при несовпадении маршрутов.

    Express.js может обслуживать http-ответы непосредственно в коде обработки маршрутов или обслуживать статическое содержимое из файлов. Этот код показывает оба варианта. Первые три маршрута возвращают данные JSON для запросов API книги. Четвертый маршрут (сценарий по умолчанию) возвращает содержимое файла индекса, index.html.

Создание клиентского приложения JavaScript

  1. В редакторе откройте файл public/script.js и добавьте следующий код:

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

    Обратите внимание на то, как этот код определяет модуль с именем myApp и контроллер с именем myCtrl. Мы не будем подробно останавливаться на том, как здесь работают модуль и контроллеры, но вы используете эти имена в следующем шаге для привязки пользовательского интерфейса (HTML-кода) к бизнес-логике приложения.

    Ранее вы создали четыре маршрута, которые обрабатывают различные операции GET, POST и DELETE на сервере. Этот код похож на эти же операции, но на стороне клиента (в браузере пользователя).

    Функция getData, например, отправляет запрос GET к конечной точке /book. Помните, что когда сервер обрабатывает этот запрос, он извлекает сведения обо всех книгах из базы данных и возвращает их как данные JSON. Обратите внимание на то, как полученные данные JSON назначаются переменной $scope.books. Вы узнаете, как это влияет на то, что пользователь видит на веб-странице на следующем шаге.

    Этот код вызывает функцию getData при загрузке страницы. Изучите функции del_book и add_book, чтобы понять, как они работают. Вам не нужен клиентский код для сопоставления обработчика по умолчанию сервера, так как обработчик по умолчанию возвращает страницу индекса, а не данные JSON.

Создание пользовательского интерфейса

  1. В редакторе откройте файл public/index.html и добавьте следующий код:

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

    Этот код создает базовую HTML-форму с четырьмя полями для отправки данных книг и таблицы, отображающей все книги, хранящиеся в базе данных.

    Хотя это стандартный код HTML, атрибуты HTML ng- могут быть вам незнакомы. Эти атрибуты HTML подключают код AngularJS к пользовательскому интерфейсу. Например, при нажатии кнопки Добавить AngularJS вызывает функцию add_book, которая отправляет данные формы на сервер.

    Изучите код, чтобы получить представление о том, как каждый из атрибутов ng- связан с бизнес-логикой приложения.

Создание сервера Express.js для размещения приложения

  1. В редакторе откройте файл server.js и добавьте следующий код:

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

    Этот код создает веб-приложение. Он обслуживает статические файлы из public каталога и использует маршруты, определенные ранее для обработки запросов.

Определение сведений о пакете и зависимостей

Как вы помните, package.json предоставляет сведения о приложении, включая имя, описание и необходимые для запуска пакеты Node.js.

  1. В редакторе откройте файл package.json и добавьте следующий код:

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

Вы увидите данные или метаданные о своем приложении, включая его имя, описание и лицензию.

Поле repository указывает, где размещен код. Для справки вы можете позже просмотреть код на GitHub по URL-адресу ниже.

Поле main определяет точку входа приложения. Оно предоставляется здесь для полноты, но это не важно, так как вы не планируете публиковать приложение в виде пакета Node.js для других пользователей, чтобы скачать и использовать его.

Поле dependencies важное. Оно определяет пакеты Node.js, необходимые приложению. Скоро вы подключитесь к виртуальной машине еще раз и выполните команду npm install, чтобы установить эти пакеты.

В пакетах Node обычно используется схема Семантического версионирования. Номер версии состоит из трех компонентов: основной номер версии, дополнительный номер версии и номер исправления. Нотация тильды ~ здесь сообщает npm, что нужно установить последнюю версию исправлений для указанной основной и дополнительной версии. Ниже приведены последние версии, с помощью которых был протестирован этот модуль. На практике со временем можно увеличивать версию при обновлении и тестировании приложения, чтобы использовать новейшие функции, предоставляемые каждым зависимым пакетом.

Копирование файлов на виртуальную машину

Прежде чем продолжать, убедитесь, что у вас под рукой есть IP-адрес вашей виртуальной машины. Если у вас его нет, выполните приведенные ниже команды из Cloud Shell, чтобы получить IP-адрес.

ipaddress=$(az vm show \
  --name MeanStack \
  --resource-group "<rgn>[sandbox resource group name]</rgn>" \
  --show-details \
  --query [publicIps] \
  --output tsv)
echo $ipaddress
  1. Вы завершили редактирование файлов. Обязательно сохраните изменения в каждом файле и закройте редактор.

    Чтобы закрыть редактор, выберите многоточие в правом верхнем углу, а затем нажмите кнопку "Закрыть редактор".

  2. Выполните следующую scp команду, чтобы скопировать содержимое ~/Books каталога в сеансе Cloud Shell в то же имя каталога на виртуальной машине:

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

Установка дополнительных пакетов Node

Предположим, что во время разработки вы решили, что хотите использовать дополнительные пакеты Node. Помните, что app/model.js начинается со следующей строки.

var mongoose = require('mongoose');

Приложение использует Mongoose для передачи данных из базы данных MongoDB и в нее.

Приложению также требуются Express.js и пакеты body-parser. Body-parser — это подключаемый модуль, позволяющий Express работать с данными из веб-формы, отправленной клиентом.

Давайте подключимся к виртуальной машине и установим пакеты, указанные в package.json.

  1. Перед подключением к виртуальной машине убедитесь, что у вас есть IP-адрес виртуальной машины. Если у вас его нет, выполните команды Cloud Shell из предыдущего раздела, чтобы получить его.

  2. Как и ранее, создайте SSH-подключение к виртуальной машине:

    ssh azureuser@$ipaddress
    
  3. Перейдите Books в каталог под домашним каталогом:

    cd ~/Books
    
  4. Запустите npm install , чтобы установить зависимые пакеты:

    sudo apt install npm -y && npm install
    

Не закрывайте подключение SSH для следующего раздела.

Тестирование приложения

Теперь вы готовы протестировать веб-приложение Node.js.

  1. В каталоге ~/Books выполните следующую команду, чтобы запустить веб-приложение:

    sudo nodejs server.js
    

    Эта команда запускает приложение, ожидая входящие HTTP-запросы на порте 80.

  2. В отдельной вкладке браузера перейдите по общедоступному IP-адресу виртуальной машины.

    Откроется страница индексов с веб-формой.

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

    Попробуйте добавить несколько книг в базу данных. Каждый раз при добавлении книги страница будет обновляться и показывать полный список книг.

    Screenshot of the book web page with sample data populated.

    Можно также нажать кнопку Удалить, чтобы удалить книгу из базы данных.