При разработке проекта в Node.js вам может понадобиться инструмент для хранения и запроса данных. В таком случае вам нужно будет выбрать систему управления базами данных, которая лучше всего подойдет для работы с вашим типом данных.
Данная серия мануалов поможет интегрировать MongoDB в существующее приложение Node. Базы данных NoSQL (к которым относится MongoDB) подходят для управления данными, которым нужна высокая масштабируемость и гибкость. Также MongoDB хорошо интегрируется с Node, поскольку она разработана для асинхронной работы с объектами JSON.
Читайте также: Основы работы с JSON
В предыдущем мануале вы создали модель и контроллер в соответствии с архитектурой MVC, что позволяет отделить логику обработки пользовательского ввода от структурирования данных и представления их пользователю. Этот шаблон может облегчить дальнейшее тестирование и разработку путем разделения элементов.
В конце этого мануала у вас будет рабочее приложение, которое будет принимать пользовательские данные и отображать результаты в браузере.
Примечание: Прежде чем приступить к работе, выполните первую часть этой серии – мануал Интеграция MongoDB в приложение Node.js: Создание модели и контроллера.
1: Использование EJS и Express
Чтобы приложение могло работать с пользовательскими данными, мы сделаем две вещи: во-первых, мы включим встроенную функцию промежуточного программного обеспечения Express, urlencoded(), которая позволит приложению анализировать введенные данные пользователя. Во-вторых, мы добавим теги шаблона в представления, чтобы обеспечить динамическое взаимодействие с пользовательскими данными в коде.
Для работы с функцией urlencoded() сначала откройте файл app.js:
nano app.js
Перед функцией express.static() добавьте следующую строку:
...
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path));
...
Добавление этой функции позволит получить доступ к проанализированным данным POST из формы ввода. Мы указали true с опцией extended, чтобы обеспечить гибкость при анализе типов данных (включая такие вещи, как вложенные объекты). Пожалуйста, прочитайте документацию по функциям для получения дополнительной информации.
Сохраните и закройте файл.
Далее мы добавим функции шаблона в представления. Сначала установите пакет ejs с помощью npm install:
npm install ejs
Затем откройте файл sharks.html в папке views:
nano views/sharks.html
В разделе 3 предыдущей части мы открывали эту страницу, чтобы определить, как написать схему и модель Mongoose.
Теперь в макет из двух столбцов мы добавим третий столбец с формой, где пользователи смогут вводить информацию об акулах.
Для начала измените размеры существующих столбцов до 4, чтобы создать три столбца одинакового размера. Обратите внимание, что вам нужно будет внести это изменение в две строки <div class=”col-lg-6″>. Обе они должны выглядеть стать <div class=”col-lg-4″>
...
<div class="container">
<div class="row">
<div class="col-lg-4">
<p>
<div class="caption">Some sharks are known to be dangerous to humans, though many more are not. The sawshark, for example, is not considered a threat to humans.
</div>
<img src="https://assets.digitalocean.com/articles/docker_node_image/sawshark.jpg" alt="Sawshark">
</p>
</div>
<div class="col-lg-4">
<p>
<div class="caption">Other sharks are known to be friendly and welcoming!</div>
<img src="https://assets.digitalocean.com/articles/docker_node_image/sammy.png" alt="Sammy the Shark">
</p>
</div>
</div>
</div>
</html>
Читайте также: Введение в Bootstrap
Затем добавьте еще один столбец, который включает в себя именованную конечную точку для POST запроса с данными пользователя и тегами шаблона EJS для захвата этих данных. Этот столбец будет идти после закрывающих тегов </p> и </div> из предыдущего столбца и перед закрывающими тегами строки, контейнера и HTML-документа. Эти закрывающие теги уже есть в вашем коде; ниже они отмечены комментариями. Оставьте их на месте, просто добавьте следующий код для создания нового столбца:
...
</p> <!-- closing p from previous column -->
</div> <!-- closing div from previous column -->
<div class="col-lg-4">
<p>
<form action="/sharks/addshark" method="post">
<div class="caption">Enter Your Shark</div>
<input type="text" placeholder="Shark Name" name="name" <%=sharks[i].name; %>
<input type="text" placeholder="Shark Character" name="character" <%=sharks[i].character; %>
<button type="submit">Submit</button>
</form>
</p>
</div>
</div> <!-- closing div for row -->
</div> <!-- closing div for container -->
</html> <!-- closing html tag -->
В теге формы мы добавили конечную точку “/sharks/addshark” для ввода пользовательских данных и указали метод POST для их отправки. В форме будут поля «Shark Name» и «Shark Character».
Чтобы добавить пользовательский ввод в коллекцию, используются теги шаблонов EJS (<%=, %>) вместе с синтаксисом JavaScript (для связи записей пользователя с соответствующими полями в новом документе).
Читайте также: Объекты в JavaScript
Много дополнительной информации о тегах шаблонов EJS можно найти в документации EJS.
Полный контейнер со всеми тремя столбцами, включая столбец с формой ввода пользовательских данных, будет выглядеть следующим образом:
...
<div class="container">
<div class="row">
<div class="col-lg-4">
<p>
<div class="caption">Some sharks are known to be dangerous to humans, though many more are not. The sawshark, for example, is not considered a threat to humans.
</div>
<img src="https://assets.digitalocean.com/articles/docker_node_image/sawshark.jpg" alt="Sawshark">
</p>
</div>
<div class="col-lg-4">
<p>
<div class="caption">Other sharks are known to be friendly and welcoming!</div>
<img src="https://assets.digitalocean.com/articles/docker_node_image/sammy.png" alt="Sammy the Shark">
</p>
</div>
<div class="col-lg-4">
<p>
<form action="/sharks/addshark" method="post">
<div class="caption">Enter Your Shark</div>
<input type="text" placeholder="Shark Name" name="name" <%=sharks[i].name; %>
<input type="text" placeholder="Shark Character" name="character" <%=sharks[i].character; %>
<button type="submit">Submit</button>
</form>
</p>
</div>
</div>
</div>
</html>
Сохраните и закройте файл.
Теперь, когда приложение может собирать входные данные пользователя, пора создать конечную точку для отображения этой информации на экране пользователя.
Скопируйте недавно измененный файл sharks.html в файл getshark.html:
cp views/sharks.html views/getshark.html
Откройте getshark.html:
nano views/getshark.html
Внутри файла нужно изменить столбец, который мы использовали для создания формы для ввода данных. Его нужно заменить столбцом, который будет отображать данные в нашей коллекции sharks. Опять же, ваш код будет находиться между тегами </p> и </div> из предыдущего столбца и закрывающими тегами строки, контейнера и HTML-документа. Не забудьте оставить эти теги на месте при добавлении следующего кода для столбца:
...
</p> <!-- closing p from previous column -->
</div> <!-- closing div from previous column -->
<div class="col-lg-4">
<p>
<div class="caption">Your Sharks</div>
<ul>
<% sharks.forEach(function(shark) { %>
<p>Name: <%= shark.name %></p>
<p>Character: <%= shark.character %></p>
<% }); %>
</ul>
</p>
</div>
</div> <!-- closing div for row -->
</div> <!-- closing div for container -->
Здесь используются теги шаблонов EJS и метод forEach() для вывода каждого значения в коллекции, включая последнюю добавленную информацию.
Весь контейнер со всеми тремя столбцами, включая столбец с коллекцией sharks, будет выглядеть следующим образом:
...
<div class="container">
<div class="row">
<div class="col-lg-4">
<p>
<div class="caption">Some sharks are known to be dangerous to humans, though many more are not. The sawshark, for example, is not considered a threat to humans.
</div>
<img src="https://assets.digitalocean.com/articles/docker_node_image/sawshark.jpg" alt="Sawshark">
</p>
</div>
<div class="col-lg-4">
<p>
<div class="caption">Other sharks are known to be friendly and welcoming!</div>
<img src="https://assets.digitalocean.com/articles/docker_node_image/sammy.png" alt="Sammy the Shark">
</p>
</div>
<div class="col-lg-4">
<p>
<div class="caption">Your Sharks</div>
<ul>
<% sharks.forEach(function(shark) { %>
<p>Name: <%= shark.name %></p>
<p>Character: <%= shark.character %></p>
<% }); %>
</ul>
</p>
</div>
</div>
</div>
</html>
Сохраните и закройте файл.
Чтобы приложение могло использовать созданные вами шаблоны, вам нужно добавить несколько строк в файл app.js. Откройте его снова:
nano app.js
Выше, где вы добавили функцию express.urlencoded(), добавьте следующие строки:
...
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path));
...
Метод app.engine позволяет приложению сопоставить механизм шаблонов EJS с файлами HTML, а app.set определяет механизм представления по умолчанию.
Ваш файл app.js должен теперь выглядеть так:
const express = require('express');
const app = express();
const router = express.Router();
const db = require('./db');
const path = __dirname + '/views/';
const port = 8080;
router.use(function (req,res,next) {
console.log('/' + req.method);
next();
});
router.get('/',function(req,res){
res.sendFile(path + 'index.html');
});
router.get('/sharks',function(req,res){
res.sendFile(path + 'sharks.html');
});
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path));
app.use('/', router);
app.listen(port, function () {
console.log('Example app listening on port 8080!')
})
Теперь, когда вы создали представления, которые могут динамически работать с пользовательскими данными, пришло время создать маршруты вашего проекта, чтобы объединить ваши представления и логику контроллера.
2: Создание маршрутов
Последним шагом в объединении компонентов приложения будет создание маршрутов. Мы разделим маршруты по функциям, включая маршрут к целевой странице приложения и отдельный маршрут к странице Sharks. В маршруте sharks мы интегрируем логику контроллера с представлениями, которые мы создали на предыдущем этапе.
Сначала создайте каталог routes:
mkdir routes
Затем откройте файл index.js в этом каталоге:
nano routes/index.js
Этот файл сначала импортирует объекты express, router и path, что позволяет определять маршруты, которые мы хотим экспортировать с объектом router, и динамически работать с путями файлов. Добавьте следующий код в верхнюю часть файла:
const express = require('express');
const router = express.Router();
const path = require('path');
Затем добавьте следующую функцию router.use, которая загружает функцию промежуточного программного обеспечения. Она будет регистрировать запросы маршрутизатора и передавать их в маршрут приложения:
...
router.use (function (req,res,next) {
console.log('/' + req.method);
next();
});
Сначала запросы в корень приложения будут направлены сюда, а отсюда пользователи будут перенаправляться на целевую страницу приложения, маршрут которой мы определим далее. Добавьте следующий код под функцией router.use, чтобы определить маршрут к целевой странице:
...
router.get('/',function(req,res){
res.sendFile(path.resolve('views/index.html'));
});
Когда пользователи посещают приложение, они в первую очередь должны попадать на целевую страницу index.html, которая находится в каталоге views.
Наконец, чтобы сделать эти маршруты доступными в виде импортируемых модулей в другом месте приложения, добавьте закрывающее выражение в конец файла (для экспорта объекта маршрутизатора):
...
module.exports = router;
В итоге получится такой файл:
const express = require('express');
const router = express.Router();
const path = require('path');
router.use (function (req,res,next) {
console.log('/' + req.method);
next();
});
router.get('/',function(req,res){
res.sendFile(path.resolve('views/index.html'));
});
module.exports = router;
Сохраните и закройте этот файл.
Затем откройте файл sharks.js, чтобы определить, как приложение должно использовать различные конечные точки и представления, которые мы создали для работы с входными данными:
nano routes/sharks.js
В верхней части файла импортируйте объекты express и router:
const express = require('express');
const router = express.Router();
Затем импортируйте модуль shark, который позволит вам работать с экспортированными функциями для контроллера:
const express = require('express');
const router = express.Router();
const shark = require('../controllers/sharks');
Теперь вы можете создавать маршруты, используя функции index, create и list, которые вы определили в файле контроллера sharks. Каждый маршрут будет связан с соответствующим методом HTTP: GET в случае рендеринга целевой страницы и возврата пользователю списка введенной информации, а POST в случае создания новой записи.
...
router.get('/', function(req, res){
shark.index(req,res);
});
router.post('/addshark', function(req, res) {
shark.create(req,res);
});
router.get('/getshark', function(req, res) {
shark.list(req,res);
});
Каждый маршрут использует связанную функцию в controllers/sharks.js, так как мы сделали этот модуль доступным, импортировав его в начале этого файла.
Закройте файл, прикрепив эти маршруты к объекту router и экспортировав их:
...
module.exports = router;
Готовый файл будет выглядеть так:
const express = require('express');
const router = express.Router();
const shark = require('../controllers/sharks');
router.get('/', function(req, res){
shark.index(req,res);
});
router.post('/addshark', function(req, res) {
shark.create(req,res);
});
router.get('/getshark', function(req, res) {
shark.list(req,res);
});
module.exports = router;
Сохраните и закройте файл.
Последнее, что нужно сделать, чтобы эти маршруты стали доступными для приложения, – добавить их в app.js. Откройте этот файл:
nano app.js
Ниже константы db добавьте следующий импорт для ваших маршрутов:
...
const db = require('./db');
const sharks = require('./routes/sharks');
Затем замените функцию app.use, которая в данный момент монтирует объект router, следующей строкой, которая будет монтировать модуль sharks:
...
app.use(express.static(path));
app.use('/sharks', sharks);
app.listen(port, function () {
console.log("Example app listening on port 8080!")
})
Теперь вы можете удалить маршруты, которые были определены в этом файле ранее, так как сейчас вы импортируете маршруты приложения, используя модуль sharks.
Окончательная версия вашего файла app.js будет выглядеть так:
const express = require('express');
const app = express();
const router = express.Router();
const db = require('./db');
const sharks = require('./routes/sharks');
const path = __dirname + '/views/';
const port = 8080;
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path));
app.use('/sharks', sharks);
app.listen(port, function () {
console.log('Example app listening on port 8080!')
})
Сохраните и закройте файл.
Теперь вы можете снова запустить tree, чтобы увидеть окончательную структуру вашего проекта:
tree -I node_modules
Структура проекта теперь будет выглядеть так:
├── Dockerfile
├── README.md
├── app.js
├── controllers
│ └── sharks.js
├── db.js
├── models
│ └── sharks.js
├── package-lock.json
├── package.json
├── routes
│ ├── index.js
│ └── sharks.js
└── views
. ├── css
. │ └── styles.css
. ├── getshark.html
. ├── index.html
. └── sharks.html
Создав и установив все компоненты приложения, вы можете добавить тестовые данные в свою БД.
Если вы выполнили мануал по начальной настройке сервера, вам нужно будет настроить брандмауэр, поскольку в настоящее время он поддерживает только трафик SSH. Чтобы разблокировать трафик по порту 8080, введите:
sudo ufw allow 8080
Запустите приложение:
node app.js
Затем в браузере перейдите по адресу http://your_server_ip:8080. Вы увидите свою целевую страницу:
Want to Learn About Sharks?
Are you ready to learn about sharks?
Get Shark Info
Нажмите на кнопку Get Shark Info. Вы увидите страницу Shark Info с маленькой формой ввода Enter your shark в правой части экрана. Форма состоит из полей Shark Name и Shark Character.
Чтобы убедиться, что все работает правильно, добавьте в это поле информацию о любой акуле. Например, можно попробовать ввести в первое поле Megalodon Shark, а во второе – Ancient
Нажмите на кнопку Submit. Вы увидите, что добавленная вами информация появилась на странице.
Вы также увидите в своей консоли вывод, указывающий, что ваша информация была добавлена в коллекцию:
Example app listening on port 8080!
{ name: 'Megalodon Shark', character: 'Ancient' }
Если вы хотите создать новую запись, вернитесь на страницу Sharks и добавьте новую информацию.
Теперь у вас есть рабочее приложение, которое позволяет пользователям добавлять информацию об акулах.
Заключение
В этой серии вы создали простое приложение Node и интегрировали базу данных MongoDB, переписав логику приложения по архитектуре MVC. Это приложение может послужить хорошей основой для полноценного приложения CRUD.
Читайте также:
Много дополнительной информации о работе с MongoDB можно найти в нашем Информатории по тегу MongoDB.