Model-View-Controller (MVC) — это популярный шаблон архитектуры программного обеспечения, который разделяет приложение на три основных компонента: модель, представление и контроллер. Модель представляет данные и бизнес-логику приложения, представление представляет пользовательский интерфейс, а контроллер обрабатывает пользовательский ввод и взаимодействия. В этой статье мы покажем вам, как создать приложение MVC с помощью экспресс, популярной веб-инфраструктуры для Node.js.
Шаг 1: Установите экспресс и установите проект
Для начала вам понадобится пустая папка, в которой вы хотите создать проект. Откройте новое окно терминала, перейдите к базе вашего нового проекта и запустите:
npm init
Это настроит NPM для вашего проекта, создав файл package.json в корне вашего проекта.
Затем выполните следующие команды, чтобы установить Express и связанные зависимости:
npm install express helmet compression cors xss-clean dotenv joi validator bcryptjs nodemailer winston --save
Помимо того, что вы можете создать HTTP-сервер, зависимости также позволяют сжимать gzip, защищать от разных источников, очищать межсайтовые сценарии, SMTP (отправка электронной почты), проверку полей и ведение журнала.
Создать папку статических документов
В корне вашего проекта создайте папку с именем «public». Внутри вы можете создать файл «index.html», если хотите, загрузить статические документы/файлы и организовать свою центральную сеть распространения. Поместите весь свой клиентский javascript в «общедоступную» папку (предпочтительно в папку «js»). Все статические документы в «общедоступной» папке будут сопоставлены с корневым URL-адресом вашего приложения.
Создать конфигурацию
В корне вашего проекта создайте папку с именем «config», создайте внутри файл с именем «config.js», добавьте следующее:
const dotenv = require('dotenv'); const path = require('path'); const Joi = require('joi'); dotenv.config({ path: path.join(__dirname, '../../.env') }); // Enforce type on the following properties: const envVarsSchema = Joi.object() .keys({ NODE_ENV: Joi.string().valid('production', 'development', 'test').required(), PORT: Joi.number().default(3000), DB_HOST: Joi.string().description('database server hostname (usually localhost)'), DB_USERNAME: Joi.string().description('username to connect to database'), DB_PASSWORD: Joi.string().description('password to connect to database'), DB_NAME: Joi.string().description('name of database'), SMTP_HOST: Joi.string().description('server that will send the emails'), SMTP_PORT: Joi.number().description('port to connect to the email server'), SMTP_USERNAME: Joi.string().description('username for email server'), SMTP_PASSWORD: Joi.string().description('password for email server'), EMAIL_FROM: Joi.string().description('the from field in the emails sent by the app'), }) .unknown(); const { value: envVars, error } = envVarsSchema.prefs({ errors: { label: 'key' } }).validate(process.env); if (error) { throw new Error(`Config validation error: ${error.message}`); } // Define Config module.exports = { env: envVars.NODE_ENV, port: envVars.PORT, db: { host: envVars.DB_HOST, username: envVars.DB_USERNAME, password: envVars.DB_PASSWORD, database: envVars.DB_NAME, /* Uncomment if using MongoDB settings: { useNewUrlParser: true, useUnifiedTopology: true } */ }, email: { smtp: { host: envVars.SMTP_HOST, port: envVars.SMTP_PORT, auth: { user: envVars.SMTP_USERNAME, pass: envVars.SMTP_PASSWORD, }, }, from: envVars.EMAIL_FROM, } };
Затем создайте файл с именем «logger.js» внутри папки «config», это настроит регистратор, добавьте следующее содержимое:
/* Logger Config @author: hagopj13 @Source: https://github.com/hagopj13/node-express-boilerplate/blob/master/src/confog/logger.js */ const winston = require('winston'); const config = require('./config'); const enumerateErrorFormat = winston.format((info) => { if (info instanceof Error) { Object.assign(info, { message: info.stack }); } return info; }); const logger = winston.createLogger({ level: config.env === 'development' ? 'debug' : 'info', format: winston.format.combine( enumerateErrorFormat(), config.env === 'development' ? winston.format.colorize() : winston.format.uncolorize(), winston.format.splat(), winston.format.printf(({ level, message }) => `${level}: ${message}`) ), transports: [ new winston.transports.Console({ stderrLevels: ['error'], }), ], }); module.exports = logger;
Создать маршруты
В корне вашего проекта создайте папку с именем «routes», создайте внутри файл с именем «index.js», добавьте следующее содержимое:
const express = require('express'); //const myRoute = require('./my.route'); const welcomeRoute = require('./welcome.route'); const config = require('../config/config'); const router = express.Router(); // { path: '/my', route: myRoute } const defaultRoutes = [ { path: '/', route: welcomeRoute }, ]; defaultRoutes.forEach((route) => { router.use(route.path, route.route); }); module.exports = router;
Затем создайте файл в том же каталоге (маршруты) с именем «welcome.route.js», добавьте следующее содержимое:
const express = require('express'); const welcomeController = require('../../controllers/welcome.controller'); const router = express.Router(); router.route('/').get(welcomeController.example); module.exports = router;
Шаг 2: Создайте модель
Модель — это компонент архитектуры MVC, который представляет данные и бизнес-логику приложения. В экспресс-приложении модель обычно реализуется как база данных или хранилище данных, например база данных SQL или база данных NoSQL, такая как MongoDB.
Для начала создайте папку в корне вашего проекта под названием «models», внутри создайте файл с именем «index.js» и добавьте следующее содержимое:
module.exports.User = require('./user.model');
Чтобы создать модель для вашего приложения, вам необходимо установить и настроить соответствующую библиотеку базы данных и создать необходимые таблицы или коллекции базы данных. Например, чтобы использовать MongoDB в качестве базы данных, вам необходимо установить драйвер MongoDB и создать коллекцию MongoDB для данных вашей модели.
MySQL/MariaDB
Mongoose — это библиотека моделирования объектных данных (ODM) для MongoDB и Node.js. Он предоставляет простое решение на основе схемы для моделирования данных вашего приложения. Он включает в себя встроенное приведение типов, проверку, построение запросов, перехватчики бизнес-логики и многое другое.
Чтобы использовать Mongoose с базой данных MySQL, вам необходимо использовать дополнительную библиотеку, которая позволяет использовать MySQL в качестве источника данных для Mongoose. Одной из таких библиотек является mongoose-mysql-connector
.
Вот пример того, как вы можете использовать Mongoose с базой данных MySQL в приложении Node.js и Express:
- Установите необходимые пакеты:
npm install mongoose mongoose-mysql-connector
- Добавьте следующий код в index.js для подключения к MySQL:
const mongoose = require('mongoose'); const mysqlConnector = require('mongoose-mysql-connector'); const config = require('../config/config'); let server; mongoose.connect(mysqlConnector, { host: config.db.host, user: config.db.username, password: config.db.password, database: config.db.database }).then(() => { logger.info('Connected to database'); server = app.listen(config.port, () => { logger.info(`Listening to port ${config.port}`); }); }); const exitHandler = () => { if (server) { server.close(() => { logger.info('Server closed'); process.exit(1); }); } else { process.exit(1); } }; const unexpectedErrorHandler = (error) => { logger.error(error); exitHandler(); }; process.on('uncaughtException', unexpectedErrorHandler); process.on('unhandledRejection', unexpectedErrorHandler); process.on('SIGTERM', () => { logger.info('SIGTERM received'); if (server) { server.close(); } });
- Создайте модель для каждого типа данных, которые вы хотите смоделировать, создайте файл внутри «моделей» с именем «user.model.js», добавьте следующее содержимое:
const mongoose = require('mongoose'); const validator = require('validator'); const bcrypt = require('bcryptjs'); const Schema = mongoose.Schema; const UserSchema = new Schema({ name: { type: String, required: true }, email: { type: String, required: true, unique: true, trim: true, lowercase: true, validate(value) { if (!validator.isEmail(value)) { throw new Error('Invalid email'); } }, }, password: { type: String, required: true, trim: true, minlength: 8, validate(value) { if (!value.match(/\d/) || !value.match(/[a-zA-Z]/)) { throw new Error('Password must contain at least one letter and one number'); } } }, isEmailVerified: { type: Boolean, default: false, }, date: { type: Date, default: Date.now } }); /** * Check if email is taken * @param {string} email - The user's email * @param {ObjectId} [excludeUserId] - The id of the user to be excluded * @returns {Promise<boolean>} */ userSchema.statics.isEmailTaken = async function (email, excludeUserId) { const user = await this.findOne({ email, _id: { $ne: excludeUserId } }); return !!user; }; /** * Check if password matches the user's password * @param {string} password * @returns {Promise<boolean>} */ userSchema.methods.isPasswordMatch = async function (password) { const user = this; return bcrypt.compare(password, user.password); }; /** * @typedef User */ const User = mongoose.model('User', userSchema); module.exports = User;
- Используйте модель для запроса и управления данными в базе данных MySQL:
// Find all users User.find({}, (err, users) => { if (err) throw err; console.log(users); }); // Create a new user const newUser = new User({ name: 'John Doe', email: '[email protected]', password: 'password123' }); newUser.save((err) => { if (err) throw err; console.log('User saved successfully'); }); // Update a user User.findOneAndUpdate( { email: '[email protected]' }, { name: 'John Smith' }, (err) => { if (err) throw err; console.log('User updated successfully'); } ); // Delete a user User.findOneAndDelete({ email: '[email protected]' }, (err) => { if (err) throw err; console.log('User deleted successfully'); });
MongoDB
Mongoose — это инструмент объектного моделирования MongoDB, предназначенный для работы в асинхронной среде. Он помогает использовать базу данных MongoDB с Node.js, предоставляя простое решение на основе схемы для моделирования данных вашего приложения.
- Чтобы использовать Mongoose с Node.js и Express, сначала необходимо установить Mongoose с помощью следующей команды:
npm install mongoose
- Добавьте следующий код в app.js для подключения к MongoDB:
const mongoose = require('mongoose'); const config = require('./config/config'); let server; mongoose.connect(config.database.host, config.database.settings).then(() => { logger.info('Connected to database'); server = app.listen(config.port, () => { logger.info(`Listening to port ${config.port}`); }); }); const exitHandler = () => { if (server) { server.close(() => { logger.info('Server closed'); process.exit(1); }); } else { process.exit(1); } }; const unexpectedErrorHandler = (error) => { logger.error(error); exitHandler(); }; process.on('uncaughtException', unexpectedErrorHandler); process.on('unhandledRejection', unexpectedErrorHandler); process.on('SIGTERM', () => { logger.info('SIGTERM received'); if (server) { server.close(); } });
- Создайте модель для каждого типа данных, которые вы хотите смоделировать, создайте файл внутри «моделей» с именем «user.model.js», добавьте следующее содержимое:
const userSchema = new mongoose.Schema({ name: String, email: String, password: String }); const User = mongoose.model('User', userSchema); module.exports = User;
- Затем вы можете использовать модель
User
для создания нового пользовательского документа следующим образом:
const user = new User({ name: 'John', email: '[email protected]', password: 'password' }); user.save(function(error) { if (error) { console.log(error); } else { console.log('User saved successfully!'); } });
- Вы также можете использовать модель для запроса документов в коллекции и их обновления или удаления. Например, вот как вы можете найти пользователя по его электронной почте и обновить его имя:
User.findOne({ email: '[email protected]' }, function(error, user) { if (error) { console.log(error); } else { user.name = 'Jane'; user.save(function(error) { if (error) { console.log(error); } else { console.log('User updated successfully!'); } }); } });
Шаг 3: Создайте представление
Представление — это компонент архитектуры MVC, представляющий пользовательский интерфейс приложения. В экспресс-приложении представление обычно реализуется с помощью механизма шаблонов, такого как Pug, EJS или Handlebars.
Чтобы создать представление для вашего приложения, вам необходимо установить соответствующий механизм шаблонов и создать файл шаблона для каждого из представлений в вашем приложении. Например, если вы используете EJS в качестве механизма шаблонов, вы можете создать файл шаблона с именем index.ejs
для основного представления вашего приложения.
Шаг 4: Создайте контроллер
Контроллер — это компонент архитектуры MVC, который обрабатывает пользовательский ввод и взаимодействие. В экспресс-приложении контроллер обычно реализуется как набор функций маршрутов и промежуточного программного обеспечения, которые обрабатывают HTTP-запросы и ответы.
Чтобы создать контроллер для вашего приложения, вам нужно создать набор функций маршрутов и промежуточного программного обеспечения, которые обрабатывают различные действия вашего приложения. Например, вы можете создать маршрут, который обрабатывает запрос GET к корневому пути вашего приложения и отображает основное представление с использованием соответствующего файла шаблона.
Начните с создания папки в корне вашего проекта под названием «контроллеры». Создайте внутри файл с именем «index.js», добавьте следующее содержимое:
// module.exports.myController = require('./my.controller'); module.exports.welcomeController = require('./welcome.controller');
Затем создайте файл внутри «контроллеров» с именем «welcome.controller.js», добавьте следующее содержимое:
//const { emailService } = require('../services'); const example = (req, res) => { res.send('Hello World!'); };
Шаг 5. Настройте экспресс-приложение
Чтобы настроить экспресс-приложение, вам нужно создать файл app.js
и настроить необходимое промежуточное ПО и маршруты. Вот пример простого экспресс-приложения, которое устанавливает базовую структуру MVC:
app.js
const path = require('path'); const express = require('express'); const helmet = require('helmet'); const xss = require('xss-clean'); const compression = require('compression'); const cors = require('cors'); const config = require('./config/config'); const routes = require('./routes'); const app = express(); app.use(helmet()); app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(xss()); app.use(compression()); app.use(cors()); app.options('*', cors()); app.use('/', routes); app.use(express.static(path.join(__dirname, 'public'))); // send back a 404 error for any unknown api request app.use((req, res, next) => { next(new ApiError(httpStatus.NOT_FOUND, 'Not found')); }); module.exports = app;
index.js
const app = require('./app'); const config = require('./config/config'); const logger = require('./config/logger'); app.listen(config.port, () => { console.log(`Example app listening on port ${config.port}`) })
Это приложение настраивает экспресс с промежуточным программным обеспечением для синтаксического анализа тела в формате JSON и URL-кодирования и определяет два маршрута: маршрут GET, который отображает основное представление, и маршрут POST, который создает новый элемент в модели.
Создать услуги
В корне вашего проекта создайте папку под названием «services», мы добавим простую службу электронной почты, чтобы проиллюстрировать, как создавать службы. Создайте внутри файл с именем «index.js», добавьте следующее содержимое:
module.exports.emailService = require('./email.service');
Затем создайте внутри файл с именем «email.service.js», добавьте следующее содержимое:
/* Email Service @author: hagopj13 @Source: https://github.com/hagopj13/node-express-boilerplate/blob/master/src/services/email.service.js */ const nodemailer = require('nodemailer'); const config = require('../config/config'); const logger = require('../config/logger'); const transport = nodemailer.createTransport(config.email.smtp); /* istanbul ignore next */ if (config.env !== 'test') { transport .verify() .then(() => logger.info('Connected to email server')) .catch(() => logger.warn('Unable to connect to email server. Make sure you have configured the SMTP options in .env')); } /** * Send an email * @param {string} to * @param {string} subject * @param {string} text * @returns {Promise} */ const sendEmail = async (to, subject, text) => { const msg = { from: config.email.from, to, subject, text }; await transport.sendMail(msg); }; /** * Send reset password email * @param {string} to * @param {string} token * @returns {Promise} */ const sendResetPasswordEmail = async (to, token) => { const subject = 'Reset password'; // replace this url with the link to the reset password page of your front-end app const resetPasswordUrl = `http://link-to-app/reset-password?token=${token}`; const text = `Dear user, To reset your password, click on this link: ${resetPasswordUrl} If you did not request any password resets, then ignore this email.`; await sendEmail(to, subject, text); }; /** * Send verification email * @param {string} to * @param {string} token * @returns {Promise} */ const sendVerificationEmail = async (to, token) => { const subject = 'Email Verification'; // replace this url with the link to the email verification page of your front-end app const verificationEmailUrl = `http://link-to-app/verify-email?token=${token}`; const text = `Dear user, To verify your email, click on this link: ${verificationEmailUrl} If you did not create an account, then ignore this email.`; await sendEmail(to, subject, text); }; module.exports = { transport, sendEmail, sendResetPasswordEmail, sendVerificationEmail, };
Шаг 6. Протестируйте приложение MVC
Чтобы протестировать приложение MVC, запустите сервер, выполнив в терминале следующую команду:
node app.js
Затем перейдите по корневому URL вашего приложения в браузере (например, http://localhost:3000
). Вы должны увидеть основное представление своего приложения и иметь возможность создавать новые элементы, отправляя форму или отправляя запрос POST на маршрут /create
.
Скачать стартовый проект
Нажмите здесь, чтобы загрузить/клонировать стартовый проект с GitHub.