Промежуточные обработчики - необходимы в большинстве случаев как обработчики авторизации, валидации, аутентификации и других различных проверок. Промежуточные обработчики могут быть как внешними библиотеками, которые подключают глобально, так самописные промежуточные обработчики подключаемые локально в маршрутизаторы. Ниже будет подробно разобраны промежуточные обработчики, которые используются в приложении
⬅️ Подробную информацию обо всех маршрутах смотреть в разделе Маршрутизация.
⬅️ Подробную информацию обо всех контроллерах смотреть в разделе Контроллеры.
↩️ К оглавлению документации.
- Промежуточные обработчики
- Оглавление раздела
- Описание промежуточных обработчиков
- Реализация библиотечных промежуточных обработчиков
- bcryptjs
- crypto
- helmet
- express-session
- connect-flash
- Использование connect-flash в контроллере авторизации с методом запроса get по машруту /auth/login
- Использование connect-flash в контроллере авторизации с методом запроса get по машруту /auth/reset
- Использование connect-flash в контроллере авторизации с методом запроса get по машруту /auth/password/:token
- Использование connect-flash в контроллере авторизации с методом запроса post по машруту /auth/login
- Использование connect-flash в контроллере авторизации с методом запроса post по машруту /auth/register
- Использование connect-flash в контроллере авторизации с методом запроса post по машруту /auth/reset
- Использование connect-flash в контроллере авторизации с методом запроса post по машруту /auth/password
- csurf
- compression
- fileMiddleware
- nodemailer-sendgrid-transport
- Реализация самописных промежуточных обработчиков
Промежуточные обработчики могут быть как внешними библиотеками так и утилиты написаные самим разработчиком. Одновременно с этим, сами промежуточные обработчики могут быть подключение к приложению глобально, так и локально, в зависимости от бизнес-логики приложение.
Пример глобального подключения промежуточного обработчика
const flash = require('connect-flash')
app.use(flash())
Где:
connect-flash
- библиотека из npm, которое отвечает за отображение валидированных ошибок.app.use(flash())
- подключение библиотеки connect-flash к приложению.app.use()
- метод use объекта app, создающий подключение midlleware (промежуточных обработчиков) к приложения. Объект app является результатом работы функции express. Код подключения express ниже:
const express = require('express')
const app = express()
Пример локального промежуточного обработчика
const bcrypt = require('bcryptjs')
router.post('/login', async (req, res) => {
const {email, password} = req.body
const candidate = await User.findOne({ email })
if (candidate) {
const areSame = await bcrypt.compare(password, candidate.password)
}
})
Где:
bcryptjs
- библиотека из npm, которая шифрует строки.router.post('/login', async (req, res) => {})
- маршрутизатор с методом запроса post, который. Подробнее о данном маршрутизаторе смотрите Метод запроса post по маршруту '/auth/login'bcrypt.compare(password, candidate.password)
- метод compare библиотеки bcryptjs, который сравнивает два значение. В данном примере - существующий пароль, который привязан к email пользователя с базы данных и вводимый кандидатом пароль.
Пример самописного промежуточного обработчика
module.exports = function(req, res, next) {
if (!req.session.isAuthenticated) {
return res.redirect('/auth/login')
}
next()
}
Где:
function(req, res, next) {})
- промежуточные обработчики отличаются наличием третъего аргумента next, который должен обязательно быть вызван вконце логики промежуточного обработчика.if (!req.session.isAuthenticated) {}
- проверка на авторизацию пользователя.return res.redirect('/auth/login')
- метод redirect объекта ответа response, который перенаправляет пользователя на страницу по маршуту '/auth/login'.next()
- обязательный параметр, определяет завершение логики кода данного промежуточного обработчика.
Таблица всех промежуточных обработчиков, которые реализованы в приложении:
Название | Подключение | Происхождение | Короткое описание |
---|---|---|---|
bcryptjs | Локально | Библиотека bcryptjs | Хеширует пароли |
crypto | Локально | Библиотека crypto | Генерирует криптографические байты |
helmet | Глобально | Библиотека helmet | Дополняет защиту приложения путём добавление заголовков HTTP |
session | Глобально | Библиотека express-session | Создаёт хранилище сессий |
connect-flash | Глобально | Библиотека flash | Выводит сообщения в случае ошибок валидации |
csurf | Глобально | Библиотека csurf | CSRF защита приложения |
compression | Глобально | Библиотека compression | Сжимает приложение в gzip для повышение быстродействия приложения |
nodemailer | Локально | Библиотека nodemailer | Формирование писем, которые будут отправляться по почте |
fileMiddleware | Глобально | Файл file.js | Обработка загружаемых картинок, которые использутся для аватара |
auth | Локально | Файл auth.js | Проверка на авторизацию пользователя |
errorHandler | Глобально | Файл error.js | Вывод страницы 404 |
registerValidators | Локально | Файл validators.js | Выводит ошибки валидации при регистрации нового пользователя |
courseValidators | Локально | Файл validators.js | Выводит ошибки валидации при создании / редактировании одного курса |
UserMiddleware | Глобально | Файл user.js | Организация допуска авторизированым пользователям |
varMiddleware | Глобально | Файл variables.js | Создание токена CSRF для авторизированого пользователя |
⬆️ К оглавлению раздела.
↩️ К оглавлению документации.
bcryptjs позволяет хешировать пароли. Поскольку манипуляции с пароля в приложении производятся в контроллерах авторизации, то и следовательно модуль подключается лишь здесь. Код подключения bcryptjs в файл auth.js:
const bcrypt = require('bcryptjs')
Библиотека в данном контролере используется для сравнения существующего пароля привязанного к существуему email пользователя с базы данных и вводимый кандидатом пароль на получение авторизации. Часть кода контроллера работающий с промежуточным обработчиком:
router.post('/login', async (req, res) => {
...
const {email, password} = req.body
const candidate = await User.findOne({ email })
if (candidate) {
const areSame = await bcrypt.compare(password, candidate.password)
}
...
...
})
Где:
router.post('/login', async (req, res) => {})
- метод запроса post объекта router, который принимает 2 аргумента: путь '/login' и callback функцию.{email, password}
- конкретные ключи деструктуризированные с тела объекта запроса request.body.User.findOne({ email })
- метод findOne модели User, который ищет пользователя в базе данных MongoDB по его email.bcrypt.compare(password, candidate.password)
- метод compare библиотеки bcryptjs, который сравнивает существующий пароль, который привязан к email пользователя с базы данных и вводимый кандидатом пароль.
⬅️ Подробнее о контролере смотрите Метод запроса post по маршруту '/auth/login'.
⬅️ Подробнее о реализации машрутизатора смотрите Реализация маршрутов.
Библиотека в данном контролере используется для хеширования пароля пользователя, который регистрируется. Часть кода контроллера работающий с промежуточным обработчиком:
router.post('/register', registerValidators, async (req, res) => {
...
const {email, password, name} = req.body
...
const hashPassword = await bcrypt.hash(password, 10)
...
})
Где:
router.post('/register', registerValidators, async (req, res) => {})
- метод запроса post объекта router, который принимает 3 аргумента: путь '/register', промежуточный обработчик registerValidators и callback функцию.registerValidators
- промежуточный обработчик валидирующий ошибки при регистрации. Подробнее о данном промежуточном обработчике смотрите registerValidators.{email, password, name}
- конкретные ключи деструктуризированные с тела объекта запроса request.body.bcrypt.hash(req.body.password, 10)
- метод hash библиотеки bcrypt, который принимает 2 аргумента: вводимый пользователем пароль req.body.password и количество хешируемых символов - 10.
⬅️ Подробнее о контролере смотрите Метод запроса post по маршруту '/auth/register'.
⬅️ Подробнее о реализации машрутизатора смотрите Реализация маршрутов.
Библиотека в данном контролере используется для хеширования вводимого пароля. Часть кода контроллера, работающий с промежуточным обработчиком:
router.post('/password', async (req, res) => {
...
if (user) {
user.password = await bcrypt.hash(req.body.password, 10)
...
})
Где:
router.post('/password', async (req, res) => {})
- метод запроса post объекта router, который принимает 2 аргумента: путь '/password' и callback функцию.bcrypt.hash(req.body.password, 10)
- метод hash библиотеки bcrypt, который принимает 2 аргумента: вводимый пользователем пароль req.body.password и количество хешируемых символов - 10.
⬅️ Подробнее о данном контролере смотрите Метод запроса post по маршруту '/auth/password'.
⬅️ Подробнее о реализации машрутизатора смотрите Реализация маршрутов.
⬆️ К оглавлению раздела.
↩️ К оглавлению документации.
crypto необходим для генерации криптографически хорошо построенных искусственных случайных данных, которые в последствии будут отправлятся пользователю как уникальная ссылка для смены пароля пользователем. Код подключение и использование crypto в файле auth.js:
const crypto = require('crypto')
...
router.post('/reset', (req, res) => {
crypto.randomBytes(32, async (err, buffer) => {})
...
})
Где:
crypto
- библиотека crypto.router.post('/reset', (req, res) => {})
- метод запроса post объекта router, который принимает 2 аргумента: путь '/reset' и callback функцию.crypto.randomBytes(32, async (err, buffer) => {})
- метод randomBytes библиотеки crypto, который генерирует криптографические данные.
⬅️ Подробнее о данном контролере смотрите Метод запроса post по маршруту '/auth/reset'.
⬅️ Подробнее о реализации машрутизатора смотрите Реализация маршрутов.
⬆️ К оглавлению раздела.
helmet помогает защитить приложение от некоторых широко известных веб-уязвимостей путем соответствующей настройки заголовоков HTTP. Код отвечающий за настройку и подключение helmet в главный файл index.js:
app.use(helmet({
contentSecurityPolicy: {
directives: {
...helmet.contentSecurityPolicy.getDefaultDirectives(),
"img-src": ["'self'", "https:"],
"script-src-elem": ["'self'", "https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js", "'unsafe-inline'" ]
},
},
}))
Где:
helmet
- оболочка для различных промежуточных обработчиков, которые установлены по умолчанию.contentSecurityPolicy: {})
- промежуточный обработчик, который устанавливает специфические настройки заголовка Content-Security-Policy.directives
- объект, где ключи - это название директив....helmet.contentSecurityPolicy.getDefaultDirectives()
- объект директив по умолчанию."img-src": ["'self'", "https:"]
- ряд параметров для защиты подключаемых картинок."script-src-elem": ["'self'", "https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js", "'unsafe-inline'" ]
- ряд параметром для защиты подключаемых скриптов.
⬆️ К оглавлению раздела.
↩️ К оглавлению документации.
express-session создаёт объект сессии, данные которой хранятся на стороне сервера. Код, отвечающий за подключение и настройку express-session в главном файле index.js:
const session = require('express-session')
...
app.use(session({
secret: keys.SESSION_SECRET,
resave: false,
saveUninitialized: false,
store
}))
Где:
session
- оболочка принимающая ряд параметров настройки session.secret: keys.SESSION_SECRET
- уникальный ключ сессий.resave: false
- принудительное сохранение сеанса в хранилище сессий, даже если сеанс не изменялся во время запроса, true по умолчание. Значение false выставляется для избежания ошибок в случае параллельных запросов пользователем.saveUninitialized: false
- сохранение неинициализированных сеансов в хранилище сессий, true по умолчанию. Значение false выставляется во избежания ошибок в случае параллельных запросов пользователем и уменьшения использования хранилище сервера.store
- ключ принимающий одноименное значение объекта store. Код объекта store указан ниже:
const MongoStore = require('connect-mongodb-session')(session)
...
const store = new MongoStore({
collection: 'sessions',
uri: keys.MONGODB_URL
})
Где:
MongoStore({})
- объект устанавливающий соединение express приложения и хранилище сессий в MongoDB.collection: 'sessions'
- название коллекции сессий в базе данных MongoDB.uri: keys.MONGODB_URL
- URL путь соединения с базой данных MongoDB.
⬆️ К оглавлению раздела.
↩️ К оглавлению документации.
connect-flash необходим как специальная область сеанса, которая используется для хранения сообщений. Сообщения используется в связке с перенаправлениями во время действий авторизации. Сам connect-flash подключается в главном файле index.js после чего используется локально в контроллерах авторизации. Подробнее о контроллерах авторизации смотрите Контроллеры страницы "Вход / Выход". Код подключения connect-flash в главном файле index.js:
const flash = require('connect-flash')
app.use(flash())
Библиотека в данном контролере используется для вывода ошибок валидации при вводе почты и пароля пользователя. Часть кода контроллера работающий с промежуточным обработчиком:
router.get('/login', async (req, res) => {
res.render('auth/login', {
...
loginError: req.flash('loginError'),
registerError: req.flash('registerError')
})
})
Где:
router.get('/login', async (req, res) => {})
- метод запроса get объекта router, который принимает 2 аргумента: путь '/login' и callback функцию.res.render('auth/login', {})
- метод render объекта res отправляет данные в объекте ответа для последующей визуализации представления шаблонизатором handlebars.loginError: req.flash('loginError')
- ключ loginError принимающий значение сообщения ошибки валидации приходящее с телом запроса req. В зависимости от данных введённых пользователем, может вывести текст "Неверный пароль" или "Такого пользователя не существует". Часть кода представления работающий с валидатором:
registerError: req.flash('registerError')
- ключ registerError принимающий значение сообщения ошибки валидации приходящее с телом запроса req. Часть кода представления работающий с валидатором:
⬅️ Подробнее о данном контролере смотрите Метод запроса get по маршруту '/auth/login'.
⬅️ Подробнее о данном представлении смотрите Вход / Регистрация.
⬅️ Подробнее о реализации машрутизатора смотрите Реализация маршрутов.
Библиотека в данном контролере используется для вывода ошибок валидации при вводе данных для сброса пароля. Часть кода контроллера работающий с промежуточным обработчиком:
router.get('/reset', (req, res) => {
res.render('auth/reset', {
...
error: req.flash('error')
})
})
Где:
router.get('/reset', (req, res) => {})
- метод запроса get объекта router, который принимает 2 аргумента: путь '/reset' и callback функцию.res.render('auth/reset', {})
- метод render объекта res отправляет данные в объекте ответа для последующей визуализации представления шаблонизатором handlebars.error: req.flash('error')
- ключ error принимающий значение сообщения ошибки валидации приходящее с телом запроса req.
⬅️ Подробнее о данном контролере смотрите Метод запроса get по маршруту '/auth/reset'.
⬅️ Подробнее о представлении смотрите Вход / Регистрация.
⬅️ Подробнее о реализации машрутизатора смотрите Реализация маршрутов.
Использование connect-flash в контроллере авторизации с методом запроса get по машруту /auth/password/:token
Библиотека в данном контролере используется для вывода ошибок валидации при вводе данных для создания нового пароля. Часть кода контроллера работающий с промежуточным обработчиком:
router.get('/password/:token', async (req, res) => {
...
res.render('auth/password', {
error: req.flash('error')
})
...
})
Где:
router.get('/password/:token', async (req, res) => {})
- метод запроса get объекта router, который принимает 2 аргумента: путь '/password/:token' и callback функцию.res.render('auth/password', {})
- метод render объекта res отправляет данные в объекте ответа для последующей визуализации представления шаблонизатором handlebars.error: req.flash('error')
- ключ error принимающий значение сообщения ошибки валидации приходящее с телом запроса req.
⬅️ Подробнее о данном контролере смотрите Метод запроса get по маршруту '/auth/password/:token'.
⬅️ Подробнее о представлении смотрите Вход / Регистрация.
⬅️ Подробнее о реализации машрутизатора смотрите Реализация маршрутов.
Библиотека в данном контролере используется для вывода ошибок валидации при вводе ошибочных или несуществующих данных пользователем в окне авторизации в свой аккаунт. Часть кода контроллера работающий с промежуточным обработчиком:
router.post('/login', async (req, res) => {
...
req.session.save(err => {
if (err) {
throw err
}
res.redirect('/')
})
} else {
req.flash('loginError', 'Неверный пароль')
res.redirect('/auth/login#login')
}
} else {
req.flash('loginError', 'Такого пользователя не существует')
res.redirect('/auth/login#login')
}
...
})
Где:
router.post('/login', async (req, res) => {})
- метод запроса post объекта router, который принимает 2 аргумента: путь '/login' и callback функцию.req.flash('loginError', 'Неверный пароль')
- метод flash принимающий 2 аргумента: название ошибки 'loginError' и текст ошибки 'Неверный пароль'.res.redirect('/auth/login#login')
- метод redirect объекта res перенаправляет пользователя на страницу по маршруту /auth/login#login.req.flash('loginError', 'Такого пользователя не существует')
- метод flash принимающий 2 аргумента: название ошибки 'loginError' и текст ошибки 'Такого пользователя не существует'.
⬅️ Подробнее о данном контролере смотрите Метод запроса post по маршруту '/auth/login'.
⬅️ Подробнее о представлении смотрите Вход / Регистрация.
⬅️ Подробнее о реализации машрутизатора смотрите Реализация маршрутов.
Использование connect-flash в контроллере авторизации с методом запроса post по машруту /auth/register
Библиотека в данном контролере используется для вывода ошибок валидации при вводе регистрируемых данных пользователем. Часть кода контроллера работающий с промежуточным обработчиком:
router.post('/register', registerValidators, async (req, res) => {
...
if (!errors.isEmpty()) {
req.flash('registerError', errors.array()[0].msg)
return res.status(422).redirect('/auth/login#register')
...
})
Где:
router.post('/register', registerValidators, async (req, res) => {})
- метод запроса post объекта router, который принимает 2 аргумента: путь '/register' и callback функцию.(!errors.isEmpty()) {}
- если поле валидации наличивает символы - выполняется тело условия.req.flash('registerError', errors.array()[0].msg)
- поскольку валидатор ошибки у нас проверяется в представлении, то текст ошибки нам не обязательно делать смысловым на стороне сервера.
На стороне сервера в случае ошибки валидации будет выводится первое значение встроенных текстов в объекте errors, которое приведённое к массиву, чтобы забрать текст ошибки по его индексу. Сам текст ошибки выглядит так: "Invalid value".return res.status(422).redirect('/auth/login#register')
- метод redirect объекта res перенаправляет пользователя на страницу по маршруту /auth/login#register.
⬅️ Подробнее о данном контролере смотрите Метод запроса post по маршруту '/auth/register'.
⬅️ Подробнее о представлении смотрите Вход / Регистрация.
⬅️ Подробнее о реализации машрутизатора смотрите Реализация маршрутов.
Библиотека в данном контролере используется для вывода ошибок валидации при вводе почты, привязанный пароль к которой пользователь забыл и хочет сбросить. Часть кода контроллера работающий с промежуточным обработчиком:
router.post('/reset', (req, res) => {
...
crypto.randomBytes(32, async (err, buffer) => {
if (err) {
req.flash('error', 'Что-то пошло не так, повторите попытку позже')
return res.redirect('/auth/reset')
}
if (candidate) {
...
await transporter.sendMail(resetEmail(candidate.email, token))
res.redirect('/auth/login')
} else {
req.flash('error', 'Такого email нет')
res.redirect('/auth/reset')
}
...
})
Где:
crypto.randomBytes(32, async (err, buffer) => {})
- метод randomBytes библиотеке crypto, который генерирует хорошо построенные криптографические искусственные данные.req.flash('error', 'Что-то пошло не так, повторите попытку позже')
- метод flash принимающий 2 аргумента: название ошибки 'error' и текст ошибки 'Что-то пошло не так, повторите попытку позже'.return res.redirect('/auth/reset')
- метод redirect объекта res перенаправляет пользователя на страницу по маршруту /auth/reset.transporter.sendMail(resetEmail(candidate.email, token))
- метод sendMail объекта transporter отправляющий форму письма resetEmail по адресу email и токен смены пароля token.res.redirect('/auth/login')
- метод redirect объекта res перенаправляет пользователя на страницу по маршруту /auth/login.req.flash('error', 'Такого email нет')
- метод flash принимающий 2 аргумента: название ошибки 'error' и текст ошибки 'Такого email нет'.res.redirect('/auth/reset')
- метод redirect объекта res перенаправляет пользователя на страницу по маршруту /auth/reset.
⬅️ Подробнее о данном контролере смотрите Метод запроса post по маршруту '/auth/reset'.
⬅️ Подробнее о представлении смотрите Вход / Регистрация.
⬅️ Подробнее о реализации машрутизатора смотрите Реализация маршрутов.
Использование connect-flash в контроллере авторизации с методом запроса post по машруту /auth/password
Библиотека в данном контролере используется для вывода ошибок валидации вводе нового пароля пользователем. Часть кода контроллера работающий с промежуточным обработчиком:
router.post('/password', async (req, res) => {
if (user) {
user.password = await bcrypt.hash(req.body.password, 10)
...
res.redirect('/auth/login')
} else {
req.flash('loginError', 'Время жизни токена истекло')
res.redirect('/auth/login')
}
...
})
Где:
router.post('/password', async (req, res) => {})
- метод запроса post объекта router, который принимает 2 аргумента: путь '/password' и callback функцию.bcrypt.hash(req.body.password, 10)
- метод hash библиотеки bcrypt который принимает два аргумента: вводимый пользователем пароль req.body.password и количество хешируемых символов - 10.res.redirect('/auth/login')
- метод redirect объекта res перенаправляет пользователя на страницу по маршруту /auth/login.req.flash('loginError', 'Время жизни токена истекло')
- метод flash принимающий 2 аргумента: название ошибки 'loginError' и текст ошибки 'Время жизни токена истекло'.
⬅️ Подробнее о данном контролере смотрите Метод запроса post по маршруту '/auth/password'.
⬅️ Подробнее о представлении смотрите Вход / Регистрация.
⬅️ Подробнее о реализации машрутизатора смотрите Реализация маршрутов.
⬆️ К оглавлению раздела.
↩️ К оглавлению документации.
csrf используется для защиты от подделки межасайтовых запросов (CSRF). Код подключения connect-flash в главный файл index.js:
const csrf = require('csurf')
app.use(csrf())
Использование csrf защиты выходит путём подключения скритого обработчика csrf в шаблони handlebars. Часть кода шаблонизатора cтраницы "Добавить курс" add.hbs который работает с промежуточным обработчиком csurf:
Где:
form action="/add" method="POST"
- форма контроллера с методом запроса post по машруту /add.<input type="hidden" name="_csrf" value="{{csrf}}">
- промежуточный обработчик csurf не отображающийся в представлении, но при этом обеспечивает создание токена. Этот токен проверяется по сеансу пользователя.
Часть кода шаблонизатора cтраницы "Корзины" card.hbs который работает с промежуточным обработчиком csurf:
Где:
data-csrf="{{@root.csrf}}"
- поле, которое визуализирует кнопку "Удалить" только для авторизированного пользователя владеющего токеном защиты CSRF.form action="/orders" method="POST"
- форма контроллера с методом запроса post по машруту /orders.<input type="hidden" name="_csrf" value="{{csrf}}">
- токен промежуточного обработчика csurf, который проверяется по сеансу пользователя.
Часть кода шаблонизатора cтраницы редактирования одного курса course-edit.hbs который работает с промежуточным обработчиком csurf:
Где:
form action="/courses/edit" method="POST"
- форма контроллера с методом запроса post по машруту /courses/edit.form action="/courses/remove" method="POST"
- форма контроллера с методом запроса post по машруту /courses/remove.<input type="hidden" name="_csrf" value="{{csrf}}">
- токен промежуточного обработчика csurf, который проверяется по сеансу пользователя.
Часть кода шаблонизатора cтраницы "Курсы" courses.hbs который работает с промежуточным обработчиком csurf:
Где:
@root.isAuth
- права авторизированного пользовтаеля. Подробнее о правах смотрите промежуточных обработчик varMiddleware.form action="/card/add" method="POST"
- форма контроллера с методом запроса post по машруту /card/add.<input type="hidden" name="_csrf" value="{{csrf}}">
- токен промежуточного обработчика csurf, который проверяется по сеансу пользователя.
Часть кода шаблонизатора cтраницы "Профиль" profile.hbs который работает с промежуточным обработчиком csurf:
Где:
form action="/profile" method="POST" enctype="multipart/form-data"
- форма контроллера с методом запроса post по машруту /profile.<input type="hidden" name="_csrf" value="{{csrf}}">
- токен промежуточного обработчика csurf, который проверяется по сеансу пользователя.
Раздел логин
Часть кода шаблонизатора cтраницы авторизации раздела "Логин" auth/login.hbs который работает с промежуточным обработчиком csurf:
Где:
form action="/auth/login" method="POST"
- форма контроллера с методом запроса post по машруту /auth/login.<input type="hidden" name="_csrf" value="{{csrf}}">
- токен промежуточного обработчика csurf, который проверяется по сеансу пользователя.
⬅️ Подробнее о данном контролере смотрите Метод запроса post по маршруту '/auth/login'.
Раздел Регистрация
Часть кода шаблонизатора cтраницы авторизации раздела "Регистрация" auth/login.hbs который работает с промежуточным обработчиком csurf:
Где:
form action="/auth/register" method="POST" novalidate
- форма контроллера с методом запроса post по машруту /auth/register.<input type="hidden" name="_csrf" value="{{csrf}}">
- токен промежуточного обработчика csurf, который проверяется по сеансу пользователя.
⬅️ Подробнее о данном контролере смотрите Метод запроса post по маршруту '/auth/register'.
Раздел сброса пароля
Часть кода шаблонизатора cтраницы авторизации раздела "Востановление пароля" auth/password.hbs который работает с промежуточным обработчиком csurf:
Где:
form action="/auth/password" method="POST"
- форма контроллера с методом запроса post по машруту /auth/password.<input type="hidden" name="_csrf" value="{{csrf}}">
- токен промежуточного обработчика csurf, который проверяется по сеансу пользователя.
⬅️ Подробнее о данном контролере смотрите Метод запроса post по маршруту '/auth/password'.
Раздел смены пароля
Часть кода шаблонизатора cтраницы авторизации раздела "Сбросить пароль" auth/reset.hbs который работает с промежуточным обработчиком csurf:
Где:
form action="/auth/reset" method="POST"
- форма контроллера с методом запроса post по машруту /auth/reset.<input type="hidden" name="_csrf" value="{{csrf}}">
- токен промежуточного обработчика csurf, который проверяется по сеансу пользователя.
⬅️ Подробнее о данном контролере смотрите Метод запроса post по маршруту '/auth/reset'.
⬅️ Подробнее о представлении, которое использует данные переданные вместе с методом render объекта ответа response смотрите Вход / Регистрация.
⬆️ К оглавлению раздела.
↩️ К оглавлению документации.
compression необходим как модуль для сжатия gzip, чтобы уменьшенить размер тела ответа и соответственно, увеличить быстродействие веб-приложения. Код подключения промежуточного обработчика в главний файл index.js:
const compression = require('compression')
app.use(compression())
⬆️ К оглавлению раздела.
↩️ К оглавлению документации.
fileMiddleware использует библиотеку multer для обработки загружаемых файлов. Код подключения и реализации логики обработки загружаемых картинок:
const multer = require('multer')
const storage = multer.diskStorage({
destination(req, file, cb) {
cb(null, 'images')
},
filename(req, file, cb) {
cb(null, file.originalname)
}
})
const allowedTypes = ['image/png', 'image/jpg', 'image/jpeg']
const fileFilter = (req, file, cb) => {
if (allowedTypes.includes(file.mimetype)) {
cb(null, true)
} else {
cb(null, false)
}
}
module.exports = multer({
storage, fileFilter
})
Где:
multer.diskStorage({})
- метод diskStorage библиотеки multer, которая сохраняет файлы. Принимает 2 аргумента, каждый из которых является функцией:
destination(req, file, cb) {}
- метод определяющий место сохранения загружаемых файлов, в нашем случае - папка "images".filename(req, file, cb) {}
- метод определяющий имя для загружаемых файлов, в нашем случае сохраняется оригинальное имя файла.
allowedTypes = ['image/png', 'image/jpg', 'image/jpeg']
- массив расширений файлов, которые могут загружатся.fileFilter = (req, file, cb) => {}
- метод определяющий фильтр файлов, которые могут принимается.if (allowedTypes.includes(file.mimetype)) {}
- условие, при котором, если файл имеет расширение указаное в массиве allowedTypes, то выполняется тело условия.cb(null, true)
- если условие проходит сохраняем файл (значение true).cb(null, false)
- если условие не проходит, тогда файл не сохраняется (значение false).multer({storage, fileFilter})
- объект multer принимающий параметры хранилища storage и фильтр fileFilter.
Подключение промежуточного обработчика в главный файл index.js:
const fileMiddleware = require('./midlleware/file')
app.use(fileMiddleware.single('avatar'))
Где:
fileMiddleware.single('avatar')
- привязка промежуточного обработчика к полю в шаблонизаторе с названием "avatar".
⬆️ К оглавлению раздела.
↩️ К оглавлению документации.
nodemailer-sendgrid-transport необходим для отправки пользователю писем на почту, которую пользователь вводил при регистрации в следующих ситуациях:
Тип письма | Задача письма |
---|---|
Подтверждение регистрации | Уведомить пользователя об подтверждении успешной регистрации |
Восстановление пароля | Прислать токен с ограниченным сроком жизни для смены пароля |
nodemailer-sendgrid-transport подключается локально в контроллеры отвечающие за регистрацию и восстановление пароля пользователя. Код подключения сервиса nodemailer и промежуточного обработчика отправки писем nodemailer-sendgrid-transport в файл auth.js:
const nodemailer = require('nodemailer')
const sendgrid = require('nodemailer-sendgrid-transport')
Где:
nodemailer
- сервис nodemailer.sendgrid
- плагин сервиса nodemailer позволяющий создать связь Node.js сервера с сервисом nodemailer.
Подключение файлов с формой письма с уведомлением и подтверждении успешной регистрации и письма с уведомлением о смене пароля в файл auth.js:
const regEmail = require('../emails/registration')
const resetEmail = require('../emails/reset')
Где:
regEmail
- форма письма об успешной регистрации.resetEmail
- форма письма о сбросе пароля.
Для создание механизма отправки писем необходимо связать сервис nodemailer и промежуточный обработчик sendGrid путём создания транспорта с уникалньым ключём авторизации в сервисе nodemailer. Код подключения уникального ключа nodemailer к промежуточному обработчику sendGrid:
const transporter = nodemailer.createTransport(sendgrid({
auth: {api_key: keys.SENDGRID_API_KEY}
}))
Где:
nodemailer.createTransport(sendgrid({}))
- метод createTransport объекта nodemailer создающий связь между сервисом nodemailer и сервером Node.js.auth: {api_key: keys.SENDGRID_API_KEY}
- объект авторизации auth принимающий значение секретного ключа nodemailer, зарезервированным свойством api_key. Секретный ключ nodemailer является уникальным и генерируется при прохождении процедуры создания акаунта в сервисе nodemailer. Чтобы узнать как получить секретный ключ nodemailer смотрите Получение SENDGRID_API_KEY.
В случае успешной регистрации контроллер регистрации с методом post по маршруту /auth/register отправляет данные нового пользователя в базу данных, а после отправляет письмо об успешной регистрации на почту, котору указатель пользователь. Часть кода контроллера работающего с промежуточным обработчиком nodemailer-sendgrid-transport:
router.post('/register', registerValidators, async (req, res) => {
const {email, password, name} = req.body
...
await user.save()
await transporter.sendMail(regEmail(email))
...
})
Где:
router.post('/register', registerValidators, async (req, res) => {})
- метод запроса post объекта router, который принимает 3 аргумента:
'/edit'
- обязательный параметр, который определяет маршрут запроса.registerValidators
- необязательный параметр. Промежуточный обработчик, проверяющий права пользователя на редактирование курса.async (req, res) => {})
- обязательный параметр. Callback-функция.
{email, password, name}
- одноимённые ключи деструктуризированные из тела объекта запроса req.body.user.save()
- метод save модели user сохраняющая данные в базы данных MongoDB.transporter.sendMail(regEmail(email))
- метод sendMail объекта transporter отправляющий форму письма regEmail по адресу email.
Код формы письма успешной регистрации:
module.exports = function(email) {
return {
to: email,
from: keys.EMAIL_FROM,
subject: 'Аккаунт создан',
html: `
<h1>Добро пожаловать в наш магазин</h1>
<p>Вы успешно создали аккаунт c email - ${email}</p>
<hr />
<a href="${keys.BASE_URL}">Магазин курсов</a>
`
}
}
Где:
function(email) {}
- функция принимащая аргумент почты email.return {}
- функция возвращает объект.to: email
- ключ to принимающий значение адреса получателя письма.from: keys.EMAIL_FROM
- ключ from принимающий значение адреса отправителя письма.subject: 'Аккаунт создан'
- ключ subject определяющий тему письма.html: ...
- текст письма, определённый в структуре HTML форме.
⬅️ Подробнее о данном контролере смотрите Метод запроса post по маршруту '/auth/register'.
⬆️ К оглавлению раздела.
В случае успешной регистрации контроллер регистрации с методом post по маршруту /auth/reset отправляет токен для сброса пароля, а после отправляет письмо с токеном сброса пароля на почту пользователя. Часть кода контроллера работающего с промежуточным обработчиком nodemailer-sendgrid-transport:
router.post('/reset', (req, res) => {
...
const token = buffer.toString('hex')
const candidate = await User.findOne({email: req.body.email})
...
await transporter.sendMail(resetEmail(candidate.email, token))
...
})
Где:
router.post('/reset', (req, res) => {})
- метод запроса post объекта router, который принимает 2 аргумента:
'/reset'
- обязательный параметр, который определяет маршрут запроса.async (req, res) => {})
- обязательный параметр. Callback-функция.
buffer.toString('hex')
- метод toString привядящий к строке аргумент buffer выводящий 32значное число.User.findOne({email: req.body.email})
- метод findOne моделит User, находит почту пользователя из базы данных email которая совпадает со значением почты приходящим в поле запроса req.body.email.transporter.sendMail(resetEmail(candidate.email, token))
- метод sendMail объекта transporter отправляющий форму письма resetEmail по адресу email и токен смены пароля token.
Код формы письма сброса пароля:
module.exports = function(email, token) {
return {
to: email,
from: keys.EMAIL_FROM,
subject: 'Восстановление доступа',
html: `
<h1>Вы забыли пароль?</h1>
<p>Если нет, то проигнорируйте данное письмо</p>
<p>Иначе нажмите на ссылку ниже: </p>
<p><a href="${keys.BASE_URL}/auth/password/${token}">Восстановить пароль</a></p>
<hr />
<a href="${keys.BASE_URL}">Магазин курсов</a>
`
}
}
Где:
function(email, token) {}
- функция принимащая аргумент почты email.return {}
- функция возвращает объект.to: email
- ключ to принимающий значение адреса получателя письма.from: keys.EMAIL_FROM
- ключ from принимающий значение адреса отправителя письма.subject: 'Восстановление доступа'
- ключ subject определяющий тему письма.html: ...
- текст письма, определённый в структуре HTML форме.
⬅️ Подробнее о данном контролере смотрите Метод запроса post по маршруту '/auth/reset'.
⬆️ К оглавлению раздела.
↩️ К оглавлению документации.
auth необходим для проверки сессии пользователя. Код промежуточного обработчика:
module.exports = function(req, res, next) {
if (!req.session.isAuthenticated) {
return res.redirect('/auth/login')
}
next()
}
Где:
if (!req.session.isAuthenticated) {}
- условие, которое выполняется если сессия пользователя не авторизована.return res.redirect('/auth/login')
- метод redirect объекта ответа res, который перенаправляет пользователя по машруту '/auth/login'.next()
обязательный параметр промежуточного обработчика, отвечающий за определение окончание кода промежуточного обработчика и позволяющий компилятору считывать дальнейший код приложения.
Подключение промежуточного обработчика происходит локально в соответствующие машрутизаторы. Перечень всех страниц с типами запроса и машрутизаторами показан в таблице "Все страницы с типами запросами и маршрутизаторами которые обрабатываются промежуточным обработчиком auth".
Все страницы с типами запросами и маршрутизаторами которые обрабатываются промежуточным обработчиком auth
Название страницы | Метод запроса | URL-путь запроса | Краткое описание |
---|---|---|---|
Главная | get | "/" | Получение страницы "Главная" |
Курсы | get | "/courses" | Получение страницы "Курсы" и получение всех курсов магазина |
Курсы | get | "/courses/:id" | Получение одного курса из магазина |
Курсы | get | "/courses/:id/edit" | Получение страницы "Редактирование одного курса" |
Курсы | post | "/courses/edit" | Отправка редактируемых данных старого курса |
Курсы | post | "/courses/remove" | Удаление существующего курса |
Добавить курс | get | "/add" | Получение страницы "Добавить курс" |
Добавить курс | post | "/add" | Отправка и сохранение одного курса и последующего перенаправления на страницу курсов |
Профиль | get | "/profile" | Получение страницы профиля пользователя |
Профиль | post | "/profile" | Отправка редактируемых данных профиля пользователя |
Корзина | get | "/card" | Получение страницы "Корзина" и всех товаров |
Корзина | post | "/card/add" | Добавление товара в "Корзину" c помощью клавиши "Купить" на странице "Курсы" |
Корзина | delete | "/card/remove/:id" | Удаление одного курса |
Заказы | get | "/orders" | Получение страницы "Заказы" |
Заказы | post | "/orders" | Отправка данных на страницу "Заказы" через клавишу "Сделать заказ" на странице "Корзина" |
Интеграция промежуточного обработчика auth в машрутизаторы выглядит следующим образом:
router.post('/', auth, async (req, res) => {})
Где:
router.post()
- метод запросаpost
объектаrouter
, который принимает 3 аргумента:
'/'
- обязательный параметр, который определяет маршрут запроса.auth
- необязательный параметр. Промежуточный обработчик, проверяющий права пользователя на редактирование курса.async (req, res) => {}
- обязательный параметр, callback функция.
⬅️ Подробнее о контроллерах смотрите раздел Контроллеры.
⬅️ Подробнее о маршрутизаторах смотрите раздел Маршрутизация.
⬆️ К оглавлению раздела.
↩️ К оглавлению документации.
errorHandler необходим для перенаправление на страницу 404, если пользователь делает get запрос на несуществующую страницу. Код промежуточного обработчика errorHandler:
module.exports = function(req, res, next) {
res.status(404).render('404', {
title: 'Страница не найдена'
})
}
Где:
function(req, res, next) {}
- функция промежуточного обработчика принимающая 3 аргумента: объект запроса req, объект ответа res и функцию next определяющую окончание кода промежуточного обработчика и позволяющая компилятору считывать следующий, за промежуточным обработчиком, код программы.res.status(404)
- метод status объекта ответа res, который отправляет тип кода сервера (404).render('404', {}
- метод render объекта ответа res, которая вторым аргументом отправляет объект с вложенными данными, а первым аргументом определяет шаблон последующей визулизации данных.title: 'Страница не найдена'
- вложенные данные отправленные в шаблон, где ключ title принимающий значение строки 'Страница не найдена'.
Подключение промежуточного обработчика errorHandler в главный файл index.js:
const errorHandler = require('./midlleware/error')
app.use(errorHanler)
Важно! обработчик должен быть подключён после маршрутизаторов (порядок подключения модулей машрутизаторов и промежуточного обработчика errorHandler в файл index.js неважен), чтобы сервер верно считывал код существующий страниц:
...
app.use('/', homeRoutes)
app.use('/add', addRoutes)
app.use('/courses', coursesRoutes)
app.use('/card', cardRoutes)
app.use('/orders', ordersRoutes)
app.use('/auth', authRoutes)
app.use('/profile', profileRoutes)
app.use(errorHanler)
Где:
app.use('/', homeRoutes)
- метод use объекта app принимающий 2 параметра: корневой путь машрутизатора '/' и callback-функцию homeRoutes.app.use(errorHanler)
- метод use объекта app принимающий промежуточный обработчик errorHanler.
⬅️ Подробнее о маршрутизаторах смотрите раздел Маршрутизация.
⬆️ К оглавлению раздела.
↩️ К оглавлению документации.
registerValidators валидирует данные, вводимые пользователем во время регистрации нового аккаунта, поскольку каждое поле имеет свои собственные условия валидации, то registerValidators это массив состоящий из объектов с различными свойствами:
registerValidators = []
Код, отвечающий за валидацию регистрируемой почты:
body('email')
.isEmail()
.withMessage('Введите корректный email')
.custom(async (value, req) => {
try {
const user = await User.findOne({email: value})
if (user) {
return Promise.reject('Такой email уже занят')
}
} catch (e) {
console.log(e)
}
.normalizeEmail()
})
Где:
body('email')
- поле с названием контейнера email..isEmail()
- валидация на наличие символа "@"..withMessage('Введите корректный email')
- функция withMessage выводящая текст ошибки валидации 'Введите корректный email'..custom(async (value, req) => {})
- уникальная функция.User.findOne({email: value})
- метод findOne модели User, который находит почту пользователя в базе данных по значению value.Promise.reject('Такой email уже занят')
- объект Promise возвращающий ошибку reject с текстом 'Такой email уже занят' в случае, если в базе существует пользователь с таким email.normalizeEmail()
- приводит строку со значением email к формату email.
Код, отвечающий за валидацию регистрируемого пароля:
...
body('password', 'Пароль должен быть минимум 6 символов')
.isLength({min: 6, max: 56})
.isAlphanumeric()
.trim()
...
Где:
body('password', 'Пароль должен быть минимум 6 символов')
- поле с названием контейнера password, при заполнении меньше 6 символов выводит ошибку валидации с текстом 'Пароль должен быть минимум 6 символов'..isLength({min: 6, max: 56})
- функция isLength принимающая два параметра: минимальное количество символов - 6 и максимальное количество символов - 56..isAlphanumeric()
- валидация, которая позволяет заполнять поле только английским алфавитом..trim()
- функция убирающая пробелы.
Код отвечающий за валидацию подтверждение пароля:
...
body('confirm')
.custom((value, {req}) => {
if (value !== req.body.password) {
throw new Error('Пароли должны совпадать')
}
return true
})
.trim()
...
Где:
body('confirm')
- поле с названием контейнера confirm..custom((value, {req}) => {})
- уникальная функция.if (value !== req.body.password) {}
- условие, при котором, если значение в поле confirm не равняется значению из тела запроса req.body.password, тогда выполняется тело условия.throw new Error('Пароли должны совпадать')
- при несовпадении паролей выводится ошибка валидации с текстом 'Пароли должны совпадать'..trim()
- функция убирающая пробелы.
Кода отвечающий за валидацию регистрируемого имени пользователя:
...
body('name')
.isLength({min: 3})
.withMessage('Имя не может быть короче трёх символов')
.trim()
...
Где:
body('name')
- поле с названием контейнера name..isLength({min: 3})
- функция isLength, которая при заполнении поля name меньше чем 3 символа выводит ошибку валидации с текстом 'Имя не может быть короче трёх символов'..withMessage('Имя не может быть короче трёх символов')
- функция withMessage выводящая текст ошибки валидации 'Имя не может быть короче трёх символов'..trim()
- функция убирающая пробелы.
Подключение промежуточного обработчика происходит локально в маршрутизатор регистрации с методом запроса post:
router.post('/register', registerValidators, async (req, res) => {})
Где:
router.post()
- метод запросаpost
объектаrouter
, который принимает 3 аргумента:
'/register'
- обязательный параметр, который определяет маршрут запроса.registerValidators
- необязательный параметр. Промежуточный обработчик, валидирующий вводимые данные при регистрации нового пользователям.async (req, res) => {}
- обязательный параметр, callback-функция.
⬅️ Подробнее о данном контролере смотрите раздел Метод запроса post по маршруту '/auth/register'.
⬆️ К оглавлению раздела.
↩️ К оглавлению документации.
courseValidators валидирует данные вводимые при создании или редактировании одного курса, поскольку каждое поле имеет свои собственные условия валидации, то courseValidators это массив состоящий из объектов с различными свойствами:
courseValidators = [
body('title').isLength({min: 3}).withMessage('Минимальная длина названия три символа').trim(),
body('price').isNumeric().withMessage('Введите корректную цену'),
body('img', 'Введите корректный Url картинки').isURL()
]
Где:
body('title')
- поле с названием контейнера title.
.isLength({min: 3})
- функция isLength, которая при заполнении поля name меньше чем 3 символа выводит ошибку валидации с текстом 'Минимальная длина названия три символа'..withMessage('Минимальная длина названия три символа')
- функция withMessage выводящая текст ошибки валидации 'Минимальная длина названия три символа'..trim()
- функция убирающая пробелы.
body('price')
- поле с названием контейнера price.
.isNumeric()
- функция валидации позволяющая вводить только цифровые символы в поле price..withMessage('Введите корректную цену')
- функция withMessage выводящая текст ошибки валидации 'Введите корректную цену'.
body('img', 'Введите корректный Url картинки')
- поле с названием контейнера img, при ошибке валидации вовыводит текст 'Введите корректный Url картинки'.
.isURL()
- функция валидации проверяющая строку img как ссылку.
Подключение промежуточного обработчика происходит локально в соответствующие машрутизаторы. Перечень всех страниц с типами запроса и машрутизаторами показан в таблице "Все страницы с типами запросами и маршрутизаторами которые обрабатываются промежуточным обработчиком courseValidators".
Все страницы с типами запросами и маршрутизаторами которые обрабатываются промежуточным обработчиком courseValidators
Название страницы | Метод запроса | URL-путь запроса | Краткое описание |
---|---|---|---|
Курсы | post | "/courses/edit" | Отправка редактируемых данных старого курса |
Добавить курс | post | "/add" | Отправка и сохранение одного курса и последующего перенаправления на страницу курсов |
Интеграция промежуточного обработчика courseValidators в машрутизаторы выглядит следующим образом:
router.post('/', auth, async (req, res) => {})
Где:
router.post()
- метод запросаpost
объектаrouter
, который принимает 3 аргумента:
'/'
- обязательный параметр, который определяет маршрут запроса.auth
- необязательный параметр. Промежуточный обработчик, проверяющий сеанс пользователя.async (req, res) => {}
- обязательный параметр, callback-функция.
⬅️ Подробнее о контроллерах смотрите раздел Контроллеры.
⬅️ Подробнее о маршрутизаторах смотрите раздел Маршрутизация.
⬆️ К оглавлению раздела.
↩️ К оглавлению документации.
UserMiddleware необходим для организации сесии для одного пользователя. Код предоставления сесии для одного пользователя:
module.exports = async function(req, res, next) {
if (!req.session.user) {
return next()
}
req.user = await User.findById(req.session.user._id)
next()
}
Где:
function(req, res, next) {})
- функция промежуточного обработчика принимающая 3 аргумента: объект запроса req, объект ответа res и функцию next определяющую окончание кода промежуточного обработчика и позволяющая компилятору считывать следующий, за промежуточным обработчиком, код программы.if (!req.session.user) {}
- условие, которое выполняется если отсутствие сеанс пользователя.req.user = await User.findById(req.session.user._id)
- метод findById модели User находит пользователя по его ID из тела запроса сессий пользователей и присваивает свойству user значение этого ID пользователя.next()
- обязательный аргумент промежуточного обработчика, отвечающий за определение окончание кода промежуточного обработчика и позволяющий компилятору считывать дальнейший код приложения.
Подключение промежуточного обработчика UserMiddleware из файла user.js в главный файл index.js:
const UserMiddleware = require('./midlleware/user')
app.use(UserMiddleware)
⬆️ К оглавлению раздела.
↩️ К оглавлению документации.
varMiddleware необходим для присванивание значений авторизации, которые в представлениях будут организовывать условия для визуализации тех или иных данных. Код промежуточного обработчика:
module.exports = function(req, res, next) {
res.locals.isAuth = req.session.isAuthenticated
res.locals.csrf = req.csrfToken()
next()
}
Где:
res.locals.isAuth
- специальное свойство locals объекта ответа res определяющие локальный ключ isAuth.req.session.isAuthenticated
- флаг аутентификации, которое хранится в свойстве session объекта запроса req.res.locals.csrf
- специальное свойство locals объекта ответа res определяющие локальный ключ csrf.req.csrfToken()
- токен CSURF защиты, который приходит с объектом запроса.
Подключение промежуточного обработчика varMiddleware из файла variables.js в главный файл index.js:
const varMiddleware = require('./midlleware/variables')
app.use(UserMiddleware)