Вступление
Работая сNode.js, вы можете обнаружить, что разрабатываете проект, который хранит и запрашивает данные. В этом случае вам нужно будет выбрать решение для базы данных, которое имеет смысл для данных вашего приложения и типов запросов.
В этом руководстве вы интегрируете базу данныхMongoDB с существующим приложением Node. NoSQL databases, такие как MongoDB, могут быть полезны, если ваши требования к данным включают масштабируемость и гибкость. MongoDB также хорошо интегрируется с Node, поскольку он предназначен для асинхронной работы с объектамиJSON.
Чтобы интегрировать MongoDB в свой проект, вы будете использоватьObject Document Mapper (ODM)Mongoose для создания схем и моделей для данных вашего приложения. Это позволит вам организовать код вашего приложения в соответствии с архитектурным шаблономmodel-view-controller (MVC), который позволяет вам отделить логику того, как ваше приложение обрабатывает ввод пользователя, от того, как ваши данные структурированы и отображаются для пользователя. Использование этого шаблона может облегчить дальнейшее тестирование и разработку путем введения разделения проблем в вашу кодовую базу.
В конце урока у вас будет работающее информационное приложение об акулах, которое примет пользовательские данные о своих любимых акулах и отобразит результаты в браузере:
Предпосылки
-
Локальная машина или сервер разработки под управлением Ubuntu 18.04 вместе с пользователем без полномочий root с привилегиями
sudo
и активным брандмауэром. Инструкции по их настройке на сервере 18.04 см. ВInitial Server Setup guide. -
Node.js and npm installed on your machine or server, following these instructions on installing with the PPA managed by NodeSource.
-
MongoDB, установленный на вашем компьютере или сервере, после шага 1How To Install MongoDB in Ubuntu 18.04.
[[step-1 -—- created-a-mongo-user]] == Шаг 1. Создание пользователя Mongo
Прежде чем мы начнем работать с кодом приложения, мы создадим административного пользователя, который будет иметь доступ к базе данных нашего приложения. Этот пользователь будет иметь административные привилегии для любой базы данных, что даст вам возможность переключаться и создавать новые базы данных по мере необходимости.
Сначала убедитесь, что MongoDB работает на вашем сервере:
sudo systemctl status mongodb
Следующий вывод показывает, что MongoDB работает:
Output● mongodb.service - An object/document-oriented database
Loaded: loaded (/lib/systemd/system/mongodb.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2019-01-31 21:07:25 UTC; 21min ago
...
Затем откройте оболочку Mongo, чтобы создать своего пользователя:
mongo
Это поместит вас в административную оболочку:
OutputMongoDB shell version v3.6.3
connecting to: mongodb://127.0.0.1:27017
MongoDB server version: 3.6.3
...
>
Когда вы откроете оболочку, вы увидите несколько административных предупреждений из-за вашего неограниченного доступа к базе данныхadmin
. Вы можете узнать больше об ограничении этого доступа, прочитавHow To Install and Secure MongoDB on Ubuntu 16.04, когда вы переходите к производственной установке.
На данный момент вы можете использовать свой доступ к базе данныхadmin
, чтобы создать пользователя с привилегиямиuserAdminAnyDatabase
, который позволит защищенный паролем доступ к базам данных вашего приложения.
В оболочке укажите, что вы хотите использовать базу данныхadmin
для создания своего пользователя:
use admin
Затем создайте роль и пароль, добавив имя пользователя и пароль с помощью командыdb.createUser
. После ввода этой команды оболочка будет добавлять три точки перед каждой строкой, пока команда не будет завершена. Обязательно замените предоставленные здесь имя пользователя и пароль на свое имя пользователя и пароль:
db.createUser(
{
user: "sammy",
pwd: "your_password",
roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
}
)
Это создает запись для пользователяsammy
в базе данныхadmin
. Выбранное вами имя пользователя и база данныхadmin
будут служить идентификаторами для вашего пользователя.
Вывод для всего процесса будет выглядеть следующим образом, включая сообщение о том, что запись прошла успешно:
Output> db.createUser(
... {
... user: "sammy",
... pwd: "your_password",
... roles: [ { role: "userAdminAnyDatabase", db: "admin" } ]
... }
...)
Successfully added user: {
"user" : "sammy",
"roles" : [
{
"role" : "userAdminAnyDatabase",
"db" : "admin"
}
]
}
Создав свой пароль и пользователь, вы можете выйти из оболочки Mongo:
exit
Теперь, когда вы создали пользователя базы данных, вы можете перейти к клонированию кода начального проекта и добавлению библиотеки Mongoose, которая позволит вам реализовывать схемы и модели для коллекций в ваших базах данных.
[[step-2 -—- add-mongoose-and-database-information-to-the-project]] == Шаг 2 - Добавление информации о мангусте и базе данных в проект
Следующими нашими шагами будет клонирование начального кода приложения и добавление в проект данных Mongoose и нашей базы данных MongoDB.
В домашнем каталоге пользователя без полномочий root клонируйтеnodejs-image-demo
repository изDigitalOcean Community GitHub account. Этот репозиторий включает код из настройки, описанной вHow To Build a Node.js Application with Docker.
Клонируйте репозиторий в каталог с именемnode_project
:
git clone https://github.com/do-community/nodejs-image-demo.git node_project
Перейдите в каталогnode_project
:
cd node_project
Прежде чем изменять код проекта, давайте взглянем на структуру проекта с помощью командыtree
.
[.Примечание]##
Tip:tree
- полезная команда для просмотра структур файлов и каталогов из командной строки. Вы можете установить его с помощью следующей команды:
sudo apt install tree
Чтобы использовать его, введитеcd
в указанный каталог и введитеtree
. Вы также можете указать путь к начальной точке с помощью такой команды:
tree /home/sammy/sammys-project
Введите следующее, чтобы просмотреть каталогnode_project
:
tree
Структура текущего проекта выглядит следующим образом:
Output├── Dockerfile
├── README.md
├── app.js
├── package-lock.json
├── package.json
└── views
├── css
│ └── styles.css
├── index.html
└── sharks.html
Мы будем добавлять каталоги в этот проект по мере продвижения по руководству, иtree
будет полезной командой, которая поможет нам отслеживать наш прогресс.
Затем добавьте в проект пакетmongoose
npm с помощью командыnpm install
:
npm install mongoose
Эта команда создаст каталогnode_modules
в каталоге вашего проекта, используя зависимости, перечисленные в файле проектаpackage.json
, и добавитmongoose
в этот каталог. Он также добавитmongoose
к зависимостям, перечисленным в вашем файлеpackage.json
. Для более подробного обсужденияpackage.json
см.Step 1 вHow To Build a Node.js Application with Docker.
Перед созданием любых схем или моделей Mongoose мы добавим информацию о подключении к нашей базе данных, чтобы наше приложение могло подключиться к нашей базе данных.
Чтобы максимально разделить проблемы вашего приложения, создайте отдельный файл для информации о соединении с базой данных с именемdb.js
. Вы можете открыть этот файл с помощьюnano
или вашего любимого редактора:
nano db.js
Сначала импортируйтеmongoose
module с помощью функцииrequire
:
~/node_project/db.js
const mongoose = require('mongoose');
Это даст вам доступ к встроенным методам Mongoose, которые вы будете использовать для создания соединения с вашей базой данных.
Затем добавьте следующиеconstants, чтобы определить информацию для URI подключения Mongo. Хотя имя пользователя и пароль необязательны, мы включим их, чтобы мы могли требовать аутентификацию для нашей базы данных. Обязательно замените имя пользователя и пароль, указанные ниже, на свою собственную информацию и не стесняйтесь называть базу данных чем-то другим, кроме'sharkinfo'
, если вы предпочитаете:
~/node_project/db.js
const mongoose = require('mongoose');
const MONGO_USERNAME = 'sammy';
const MONGO_PASSWORD = 'your_password';
const MONGO_HOSTNAME = '127.0.0.1';
const MONGO_PORT = '27017';
const MONGO_DB = 'sharkinfo';
Поскольку наша база данных работает локально, мы использовали127.0.0.1
в качестве имени хоста. Это изменится в других контекстах разработки: например, если вы используете отдельный сервер базы данных или работаете с несколькими узлами в контейнерном рабочем процессе.
Наконец, определите константу для URI и создайте соединение с помощью методаmongoose.connect()
:
~/node_project/db.js
...
const url = `mongodb://${MONGO_USERNAME}:${MONGO_PASSWORD}@${MONGO_HOSTNAME}:${MONGO_PORT}/${MONGO_DB}?authSource=admin`;
mongoose.connect(url, {useNewUrlParser: true});
Обратите внимание, что в URI мы указалиauthSource
для нашего пользователя как базу данныхadmin
. Это необходимо, поскольку мы указали имя пользователя в нашей строке подключения. Использование флагаuseNewUrlParser
сmongoose.connect()
указывает, что мы хотим использовать Mongo'snew URL parser.
Сохраните и закройте файл, когда вы закончите редактирование.
В качестве последнего шага добавьте информацию о подключении к базе данных в файлapp.js
, чтобы приложение могло его использовать. Открытьapp.js
:
nano app.js
Первые строки файла будут выглядеть так:
~/node_project/app.js
const express = require('express');
const app = express();
const router = express.Router();
const path = __dirname + '/views/';
...
Под определением константыrouter
, расположенным в верхней части файла, добавьте следующую строку:
~/node_project/app.js
...
const router = express.Router();
const db = require('./db');
const path = __dirname + '/views/';
...
Это указывает приложению использовать информацию о соединении с базой данных, указанную вdb.js
.
Сохраните и закройте файл, когда вы закончите редактирование.
Имея информацию о вашей базе данных и добавив Mongoose в ваш проект, вы готовы создавать схемы и модели, которые будут формировать данные в вашей коллекцииsharks
.
[[step-3 -—- created-mongoose-schemas-and-models]] == Шаг 3 - Создание схем и моделей Mongoose
Нашим следующим шагом будет подумать о структуре коллекцииsharks
, которую пользователи будут создавать в базе данныхsharkinfo
со своими входными данными. Какую структуру мы хотим, чтобы эти созданные документы имели? Страница с информацией об акулах нашего текущего приложения содержит некоторые сведения о различных акулах и их поведении:
В соответствии с этой темой, мы можем предложить пользователям добавлять новых акул с подробной информацией об их общем характере. Эта цель будет определять, как мы создаем нашу схему.
Чтобы ваши схемы и модели отличались от других частей вашего приложения, создайте каталогmodels
в каталоге текущего проекта:
mkdir models
Затем откройте файл с именемsharks.js
, чтобы создать схему и модель:
nano models/sharks.js
Импортируйте модульmongoose
в начало файла:
~/node_project/models/sharks.js
const mongoose = require('mongoose');
Ниже определите объектSchema
, который будет использоваться в качестве основы для вашей схемы акулы:
~/node_project/models/sharks.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
Теперь вы можете определить поля, которые вы хотели бы включить в свою схему. Поскольку мы хотим создать коллекцию с отдельными акулами и информацией об их поведении, давайте добавим ключиname
key иcharacter
. Добавьте следующую схемуShark
под определениями констант:
~/node_project/models/sharks.js
...
const Shark = new Schema ({
name: { type: String, required: true },
character: { type: String, required: true },
});
Это определение включает информацию о типе ввода, который мы ожидаем от пользователей - в данном случаеstring - и о том, требуется ли этот ввод.
Наконец, создайте модельShark
с помощью Mongoosemodel()
function. Эта модель позволит вам запрашивать документы из вашей коллекции и проверять новые документы. Добавьте следующую строку внизу файла:
~/node_project/models/sharks.js
...
module.exports = mongoose.model('Shark', Shark)
Эта последняя строка делает нашу модельShark
доступной в виде модуля с использованиемmodule.exports
property. Это свойство определяет значения, которые модуль будет экспортировать, делая их доступными для использования в другом месте приложения.
Готовый файлmodels/sharks.js
выглядит так:
~/node_project/models/sharks.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const Shark = new Schema ({
name: { type: String, required: true },
character: { type: String, required: true },
});
module.exports = mongoose.model('Shark', Shark)
Сохраните и закройте файл, когда вы закончите редактирование.
Имея схему и модельShark
, вы можете начать работу над логикой, которая будет определять, как ваше приложение будет обрабатывать ввод пользователя.
[[step-4 -—- Creating-controllers]] == Шаг 4 - Создание контроллеров
Нашим следующим шагом будет создание компонента контроллера, который будет определять, как вводимые пользователем данные сохраняются в нашей базе данных и возвращаются пользователю.
Сначала создайте каталог для контроллера:
mkdir controllers
Затем откройте в этой папке файл с именемsharks.js
:
nano controllers/sharks.js
В верхней части файла мы импортируем модуль с нашей модельюShark
, чтобы мы могли использовать его в логике нашего контроллера. Мы также импортируемpath
module для доступа к утилитам, которые позволят нам указать путь к форме, в которую пользователи будут вводить своих акул.
Добавьте в начало файла следующие функцииrequire
:
~/node_project/controllers/sharks.js
const path = require('path');
const Shark = require('../models/sharks');
Затем мы напишем последовательность функций, которые мы будем экспортировать с модулем контроллера, используяexports
shortcut узла. Эти функции будут включать в себя три задачи, связанные с данными об акулах нашего пользователя:
-
Отправка пользователям формы ввода акулы.
-
Создание новой записи акулы.
-
Отображение акул обратно к пользователям.
Для начала создайте функциюindex
для отображения страницы акул с формой ввода. Добавьте эту функцию ниже вашего импорта:
~/node_project/controllers/sharks.js
...
exports.index = function (req, res) {
res.sendFile(path.resolve('views/sharks.html'));
};
Затем под функциейindex
добавьте функцию с именемcreate
, чтобы сделать новую запись акулы в вашей коллекцииsharks
:
~/node_project/controllers/sharks.js
...
exports.create = function (req, res) {
var newShark = new Shark(req.body);
console.log(req.body);
newShark.save(function (err) {
if(err) {
res.status(400).send('Unable to save shark to database');
} else {
res.redirect('/sharks/getshark');
}
});
};
Эта функция будет вызываться, когда пользователь отправляет данные об акулах в форму на страницеsharks.html
. Мы создадим маршрут с этой конечной точкой POST позже в руководстве, когда создадим маршруты нашего приложения. С помощьюbody
запроса POST наша функцияcreate
создаст новый объект документа акулы, здесь называемыйnewShark
, используя модельShark
, которую мы импортировали. Мы добавилиconsole.log
method для вывода записи об акуле в консоль, чтобы проверить, работает ли наш метод POST, как задумано, но вы можете пропустить это, если хотите.
Используя объектnewShark
, функцияcreate
затем вызоветmodel.save()
method Mongoose, чтобы создать новый документ акулы, используя ключи, которые вы определили в моделиShark
. Этотcallback function следует заstandard Node callback pattern:callback(error, results)
. В случае ошибки мы отправим сообщение об ошибке нашим пользователям, а в случае успеха мы будем использоватьres.redirect()
method для отправки пользователей на конечную точку, которая вернет им информацию об их акулах. в браузере.
Наконец, функцияlist
вернет пользователю содержимое коллекции. Добавьте следующий код под функциейcreate
:
~/node_project/controllers/sharks.js
...
exports.list = function (req, res) {
Shark.find({}).exec(function (err, sharks) {
if (err) {
return res.send(500, err);
}
res.render('getshark', {
sharks: sharks
});
});
};
Эта функция использует модельShark
сmodel.find()
method Mongoose для возврата акул, которые были введены в коллекциюsharks
. Он делает это, возвращая объект запроса - в данном случае все записи в коллекцииsharks
- в качестве обещания, используяexec()
function Mongoose. В случае ошибки функция обратного вызова отправит ошибку 500.
Возвращенный объект запроса с коллекциейsharks
будет отображен на страницеgetshark
, которую мы создадим на следующем шаге с использованием языка шаблоновEJS.
Готовый файл будет выглядеть так:
~/node_project/controllers/sharks.js
const path = require('path');
const Shark = require('../models/sharks');
exports.index = function (req, res) {
res.sendFile(path.resolve('views/sharks.html'));
};
exports.create = function (req, res) {
var newShark = new Shark(req.body);
console.log(req.body);
newShark.save(function (err) {
if(err) {
res.status(400).send('Unable to save shark to database');
} else {
res.redirect('/sharks/getshark');
}
});
};
exports.list = function (req, res) {
Shark.find({}).exec(function (err, sharks) {
if (err) {
return res.send(500, err);
}
res.render('getshark', {
sharks: sharks
});
});
};
Имейте в виду, что хотя мы не используем здесьarrow functions, вы можете захотеть включить их при повторении этого кода в своем собственном процессе разработки.
Сохраните и закройте файл, когда вы закончите редактирование.
Перед тем как перейти к следующему шагу, вы можете снова запуститьtree
из каталогаnode_project
, чтобы просмотреть структуру проекта на этом этапе. На этот раз для краткости мы скажемtree
опустить каталогnode_modules
, используя параметр-I
:
tree -I node_modules
С учетом внесенных вами изменений структура вашего проекта будет выглядеть следующим образом:
Output├── Dockerfile
├── README.md
├── app.js
├── controllers
│ └── sharks.js
├── db.js
├── models
│ └── sharks.js
├── package-lock.json
├── package.json
└── views
├── css
│ └── styles.css
├── index.html
└── sharks.html
Теперь, когда у вас есть компонент контроллера, который управляет тем, как пользовательский ввод сохраняется и возвращается пользователю, вы можете перейти к созданию представлений, которые будут реализовывать логику вашего контроллера.
[[step-5 -—- using-ejs-and-express-middleware-to-collect-and-render-data]] == Шаг 5. Использование EJS и Express Middleware для сбора и визуализации данных
Чтобы наше приложение могло работать с пользовательскими данными, мы сделаем две вещи: во-первых, мы включим встроенную функцию промежуточного программного обеспечения Express,urlencoded()
, которая позволит нашему приложению анализировать введенные пользователем данные. Во-вторых, мы добавим теги шаблона в наши представления, чтобы обеспечить динамическое взаимодействие с пользовательскими данными в нашем коде.
Чтобы работать с функциейurlencoded()
в Express, сначала откройте файлapp.js
:
nano app.js
Над функциейexpress.static()
добавьте следующую строку:
~/node_project/app.js
...
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path));
...
Добавление этой функции позволит получить доступ к проанализированным данным POST из нашей формы информации об акулах. Мы указываемtrue
с параметромextended
, чтобы обеспечить большую гибкость в типе данных, которые наше приложение будет анализировать (включая такие вещи, как вложенные объекты). Пожалуйста, смотритеfunction documentation для получения дополнительной информации о параметрах.
Сохраните и закройте файл, когда вы закончите редактирование.
Далее мы добавим функциональность шаблона к нашим представлениям. Сначала установитеejs
package сnpm install
:
npm install ejs
Затем откройте файлsharks.html
в папкеviews
:
nano views/sharks.html
На шаге 3 мы посмотрели на эту страницу, чтобы определить, как мы должны написать нашу схему и модель Mongoose:
Теперь вместо двух столбцовlayout мы представим третий столбец с формой, в которую пользователи могут вводить информацию об акулах.
В качестве первого шага измените размеры существующих столбцов на4
, чтобы создать три столбца одинакового размера. Обратите внимание, что вам нужно будет внести это изменение в две строки, которые в настоящее время читают<div class="col-lg-6">
. Оба они станут<div class="col-lg-4">
:
~/node_project/views/sharks.html
...
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.
Other sharks are known to be friendly and welcoming!
Для ознакомления с сеточной системой Bootstrap, включая макеты строк и столбцов, см. Этотintroduction to Bootstrap.
Затем добавьте еще один столбец, который включает именованную конечную точку для запроса POST с данными акулы пользователя и тегами шаблона EJS, которые будут захватывать эти данные. Этот столбец будет находиться под закрывающими тегами</p>
и</div>
из предыдущего столбца и над закрывающими тегами для строки, контейнера и HTML-документа. Эти закрывающие теги уже есть в вашем коде; они также отмечены ниже с комментариями. Оставьте их на месте, добавив следующий код для создания нового столбца:
~/node_project/views/sharks.html
...