Программирование
Учебник по Node.js
Статья, которая позволяет вам создавать и играть с веб-сервером на основе узлов, выполнять операции CRUD и многое другое.

Оглавление:
1. Начало работы
2. Система узловых модулей
- Глобальный объект
- Модули
- Создание модулей
- Загрузка модулей
- Функции оболочки модуля
- Модуль пути
- Модуль ОС
- Модуль событий
- События Аргументы
- Модуль файловой системы
- Модуль HTTP
3. Диспетчер пакетов узлов
- Package.json
- Установить пакет узла
- Использование пакета
- Зависимости пакетов
- Пакеты NPM и система контроля версий
- Семантическое версионирование
- Список всех установленных пакетов
- Просмотр информации реестра для пакета
- Установить определенную версию пакета
- Обновление локальных пакетов
- DevDependencies
- Удаление пакета
- Работа с глобальными пакетами
- Публикация пакета
- Обновление опубликованного пакета
4. Создание RESTful API с помощью Express
- RESTful Services
- Представляем Экспресс
- Создание своего первого веб-сервера
- Нодемон
- Переменные среды
- Параметры маршрута
- Обработка HTTP-запросов GET
- Обработка HTTP-запросов POST
- Вызов конечных точек с помощью Postman или Thunder Client
- Проверка ввода
- Обработка HTTP-запросов PUT
- Обработка HTTP-запросов на удаление
5. Экспресс продвинутые темы
- Промежуточное ПО
- Создание собственного промежуточного программного обеспечения
- Встроенное промежуточное ПО
- Стороннее промежуточное ПО
- Переменные среды
- Конфигурация
- Отладка
- Шаблонизаторы
- Структурирование Кодекса
6. Асинхронный JavaScript
- Асинхронный против синхронного кода
- Шаблоны для работы с асинхронным кодом
- Обратные звонки
- Обещания
- Замена обратных вызовов обещанием
- Параллельная обработка обещаний
- Async & Await
7. CRUD-операции с Mongoose
- "Введение"
- Настройка MongoDB (NoSQL)
- Синхронное против асинхронного исполнения
- Создание схемы, модели, объекта
- СОЗДАТЬ (синхронно)
- СОЗДАТЬ (асинхронно)
- ЧИТАТЬ (синхронно)
- ЧИТАТЬ (асинхронно)
- ОБНОВЛЕНИЕ (синхронное)
- ОБНОВЛЕНИЕ (Асинхронное)
- УДАЛИТЬ (синхронно)
- ОБНОВЛЕНИЕ (Асинхронное)
- Ресурсы и ссылки
8. Проверка данных Mongo
- "Проверка"
- Встроенные валидаторы
- Пользовательские валидаторы
- Асинхронные валидаторы
- Ошибки валидации
- Параметры типа схемы
9. Аутентификация и авторизация
- "Введение"
- Создание схемы модели пользователя
- Регистрация пользователей
- Использование Lodash
- Хеширование паролей
- Аутентификация пользователей
- Веб-токен JSON (JWT)
- Создание токенов аутентификации
- Настройка заголовков ответов
- Промежуточное ПО авторизации
- Получение текущего пользователя
10. Обработка и регистрация ошибок
- Обработка отклоненных обещаний
- Промежуточное ПО для экспресс-ошибок
- Использование экспресс-асинхронных ошибок
- Регистрация ошибок
- Вход в MongoDB
- Обработка неперехваченных исключений
- Необработанные отклонения обещаний
- Рефакторинг кода
Ссылки и ресурсы
Содержит все коды и зависимости, относящиеся к этой статье, можно найти здесь: https://github.com/bala-codes/A-Primer-on-Node.js
Большинство разделов содержат необходимые блоки кода для отслеживания, если вы хотите выполнять коды, пока вы проходите, вы можете клонировать репо.
Список пакетов находится внутри файла package.json в соответствующих каталогах и устанавливается с использованием npm install
I. Начало работы
1. Что такое Node
Node - это среда выполнения для выполнения кода JavaScript вне браузера. Он часто используется для создания серверных служб, таких как API-интерфейсы, которые поддерживают веб-приложения, мобильные приложения и т. Д., А также сверхбыстрый и хорошо масштабируемый в производственной среде. Наряду с такими привилегиями, как он может быть построен быстро, требует меньшего количества строк кода по сравнению с другими серверными службами, меньшего размера файлов, может обрабатывать больше запросов и обеспечивать более быстрое время ответа.
2. Узловая архитектура
Каждый код javascript требует, чтобы механизм javascript преобразовал его в машинный код для понимания машины и выполнения кода. Браузеры предоставляют среду выполнения.
Node использует созданный Google движок javascript под названием V8 engine + другие модули для обработки программы node.js.
Опять же, узел - это не фреймворк, это только среда выполнения для выполнения кода javascript.
3. Как работает узел
Node хорошо масштабируется, требует больших объемов данных и может создавать приложения в реальном времени. Это из-за неблокирующей (асинхронной) формы работы.
Это означает, что один поток обслуживает / обрабатывает несколько запросов асинхронно. Запрос может быть чем угодно, от получения части данных из базы данных или выполнения любых HTTP-глаголов, поскольку они не ждут завершения какого-либо запроса, это эффективно.
Вы можете спросить, что происходит с ответом на запросы, которые ранее были инициированы потоком, эти ответы передаются во что-то, называемое очередью событий, узел постоянно отслеживает очередь событий, когда он находит какие-либо сообщения в очереди событий, он обрабатывает it.
Node идеально подходит для приложений с интенсивным вводом-выводом, а не для операций с интенсивным использованием ЦП, которые требуют больше времени для выполнения вычислений.
4. Установка узла
Загрузите последнюю версию узла отсюда.
Теперь запустите установщик и откройте CMD, чтобы проверить версию, запустив node --version.
5. Программа первого узла
Создайте новый файл app.js. Очевидно привет мир
function greet(){
console.log('Hello World');
}
greet()
Запустить программу в CMD, node app.js
II. Система узловых модулей
1. Глобальный объект
Есть некоторые объекты и функции, к которым можно получить доступ в узле где угодно. Некоторые из них
console.log() (or) global.console.log() setTimeout() clearTimeout() setInterval() clearInterval()
Их называют глобальными объектами.
2. Модули
Возникает конфликт, когда две функции с одинаковыми именами объявляются в разных программах node.js, они создают глобальный конфликт.
Таким образом, чтобы решить эту проблему и для более чистого кода, мы используем модули, в которых мы можем определить одну и ту же переменную или функцию в этих модулях и вызвать их соответственно.
Каждый файл в приложении узла считается модулем. Переменные или функции, определенные внутри файла, являются областями. По умолчанию они не видны вне файла, нам нужно явно их экспортировать.
3. Создание модулей
Давайте создадим простой модуль для отправки HTTP-запроса.
```
first_module.js
```
var url = 'https://google.com'
function get(message){
// Send a HTTP request
{ ... }
console.log(message)
}
// Make get function available outside the program
module.exports = get;
4. Загрузка модулей
Теперь давайте загрузим модуль, который мы экспортировали на предыдущем шаге. Убедитесь, что указанный выше файл и текущий файл находятся в одной папке.
```
app.js
```
const get_method = require('./first_module')
console.log(get_method('Some message'))
5. Функции оболочки модуля
Как правило, узел не выполняет наш код напрямую, а просто оборачивает его внутри функции, подобной показанной ниже, а позже он экспортируется.
(function (exports, require, module, __filename, __dirname){
'''
your codes goes here
'''
})
Так узел обертывает функции, которые мы пишем в любой программе node js, и делает их доступными где угодно.
6. Модуль "Путь"
Есть несколько очень примитивных модулей, которые уже встроены в узел, и мы можем использовать их напрямую. Итак, давайте посмотрим на некоторые из них.
Все встроенные модули вы можете найти здесь.
Теперь давайте обойдем некоторые функции модуля path. Вы можете найти все функции, которые включены в модуль пути, который можно найти здесь.
'''
app.js
'''
// Look modules inside core of Node
const path = require('path')
var pathObj = path.parse(__filename);
console.log('pathObj',pathObj)
// The output will be the properties of the current js file.
Также мы смотрим на некоторые другие модули в узле.
7. Модуль ОС
Модуль ОС проливает свет на свойства, связанные с операционной системой, в которой мы запускаем это приложение узла.
'''
app.js
'''
// Look modules inside core of Node
const os = require('os')
var totalMemory = os.totalmem();
var freeMemory = os.freemem();
console.log('Total Memory', totalMemory);
console.log(`Free Memory: ${freeMemory}`); // ECMAScript 6
Другие функции, доступные внутри ОС, можно найти здесь.
8. Модуль "События"
Событие - это сигнал, указывающий на то, что что-то произошло.
Доступно множество функций, поэтому мы рассмотрим некоторые из них. Здесь мы просто создаем / регистрируем слушателя, и как только он вызывается, мы отправляем сообщение.
'''
app.js
'''
// Look modules inside core of Node
const EventEmitter = require('events'); // CLASS --> EventEmitter
// Since EventEmitter is a Class, let's create a instance of the class to access the methods inside it.
const emitter = new EventEmitter(); // OBJECT --> emitter
// Register a listener
emitter.on('messageReceived', function() {
console.log('Received');
});
// Raise an event
emitter.emit('messageReceived');
Больше функций, доступных внутри событий, можно найти здесь.
9. События Аргументы
Событие - это сигнал, указывающий на то, что что-то произошло. Теперь с некоторыми аргументами событий в сочетании со стрелочными функциями
'''
app.js
'''
// Look modules inside core of Node
const EventEmitter = require('events'); // CLASS --> EventEmitter
// Since EventEmitter is a Class, let's create a instance of the class to access the methods inside it.
const emitter = new EventEmitter(); // OBJECT --> emitter
// Register a listener
emitter.on('messageReceived', (arg) => {
console.log('Received', arg);
});
// Raise an event with arguments
emitter.emit('messageReceived', {id:1 , token:'1234'});
10. Модуль файловой системы
Как работать с файлами в программах на node.js.
С модулем fs каждая операция, определенная в нем, имеет как формы синхронного, так и асинхронного выполнения.
'''
app.js
'''
// Look modules inside core of Node
const fs = require('fs');
const files = fs.readdirSync('./') // Synchronous
console.log('Files present under the directory', files)
// Asynchronous Function - provides a callback
fs.readdir('./', function(err, files){
if (err) console.log('Error', err)
else console.log('Result', files)
});
Дополнительные функции, доступные в файловой системе, можно найти здесь.
11. HTTP-модуль
Модуль HTTP - это основа веб-приложений, где мы можем создать веб-сервер и разместить его с конечной точкой. Здесь мы создаем мини-веб-сервер для работы на локальном хосте.
'''
app.js
'''
// Look modules inside core of Node
const http = require('http');
const server = http.createServer((req, res) => {
if (req.url == '/'){
res.write('Hellow World')
res.end();
}
});
// Event Listener
server.on('connection', (socket) => {
console.log('New connection')
});
server.listen(3000); // PORT Number
console.log('Listening on port 3000...');
Больше функций, доступных внутри HTTP, можно найти здесь.
III. Диспетчер пакетов узлов
Реестр для установки стороннего пакета. Также, если у вас есть идея опубликовать свой собственный модуль, то это место для вас.
Проверьте свою версию npm npm -v
Подробнее об этом здесь.
1. package.json
По сути, это файл JSON, содержащий основную информацию о приложении, такую как имя, версия, описание, зависимости и другие метаданные.
Чтобы запустить новое новое приложение / проект и автоматически создать package.json, введя метаданные, выполните команду npm init.
Рекомендуется запускать указанную выше команду всякий раз, когда мы создаем новый проект.
Позже, если вы захотите установить пакеты в каталоге вашего проекта или если кто-то поделился с вами проектом, и вы хотите установить зависимости, просто запустите команду npm install
2. Установите пакет узла.
Все пакеты можно найти здесь.
Чтобы установить пакет узла, перейдите в соответствующую папку проекта и выполните в CMD команду npm i <package name>.
Теперь этот новый пакет автоматически добавляется в package.json по мере установки пакета, и пакет сохраняется / доступен в каталоге node_modules.
3. Использование пакета
Обычно для импорта библиотеки / модуля.
'''
index.js
'''
// Looks for the modules inside Node Modules Folder
var _ = require('underscore')
var result = _.contains([1,2,3], 3);
console.log('Found match', result)
4. Зависимости пакетов
Когда мы устанавливаем конкретный пакет, этот конкретный пакет можно найти в каталоге node_modules, и в большинстве случаев вы также можете увидеть, что устанавливается другой пакет.
Таким образом, это дополнительные пакеты, от которых может зависеть ваш фактический пакет. Это так называемые зависимости.
5. Пакеты NPM и система контроля версий.
Когда мы фактически создаем веб-приложение в режиме реального времени, для его бесперебойной работы может потребоваться множество пакетов.
Но при включении дополнительных пакетов они добавляются в папку node_modules, и ее вес больше в МБ.
Поэтому при передаче другому разработчику или в приложениях с контролем версий, таких как Github и т. Д., Мы не можем включить этот файл.
Но, к счастью, наш файл package.json содержит зависимости, которые можно построить в любое время в любом месте.
6. Семантическое управление версиями
В семантическом управлении версиями пакет узла состоит из 3 компонентов, например ^ 1.10.4.
Это Major.Minor.Patch
Патч - обычно используется для исправления любых ошибок.
Незначительный - обычно используется для добавления некоторых функций без нарушения существующих API.
Major - обычно используется для добавления некоторых функций, которые могут нарушить работу существующих API.
Наконец, символ ^ в пакете является индикатором того, что узел будет искать ту же основную версию и возможные модификации в версиях Minor и Patch в будущем, когда фактические пакеты будут иметь некоторые обновления, и это не должно повлиять на наши коды.
7. Список всех установленных пакетов
Запустите код, чтобы увидеть все пакеты и их зависимости, node list.
Запустите код, чтобы увидеть только свои пакеты и игнорировать их зависимости npm list --depth=0.
8. Просмотр информации реестра для пакета.
Запустите код npm view <package_name>, чтобы найти метаданные для определенного пакета.
Запустите код, чтобы увидеть зависимости только конкретного пакета npm view <package_name> dependencies.
Запустите код, чтобы увидеть версии определенного пакета npm view <package_name> versions.
9. Установите определенную версию пакета.
Запустите код npm i <package_name>@<Major>.<Minor>.<Patch>
10. Обновление локальных пакетов
Чтобы сравнить наш фактический локальный пакет с текущей версией этого пакета, запустите код npm outdated.
Чтобы обновить пакет, выполните команду npm updateit обновляет только второстепенные версии и версии исправлений для любого пакета.
Перед обновлением основной версии необходимо принять меры предосторожности, так как это может нарушить наш код. Запустите npm-check-updates, npm-check-updates -u, чтобы обновить основную версию, а затем установите ее npm i, чтобы отразить ее в нашем пакете.
11. DevDependencies
На этапе разработки мы можем включать модульный тест, интеграционный тест и т. Д., И мы можем использовать какой-либо пакет для его тестирования, и эти пакеты не должны перемещаться, пока приложение находится в производстве.
Итак, чтобы установить некоторые пакеты разработки, выполните команду npm i <pkg_name> --save-dev. Поскольку они сохраняются в разделе «devDependencies» в файле package.json, обо всем остальном позаботится узел.
12. Удаление пакета
Чтобы удалить пакет, выполните команду npm un <pkg_name>
13. Работа с глобальными пакетами
npm - один из примеров глобальных пакетов, где он универсален для запуска всех узловых приложений.
Чтобы обновить такие глобальные пакеты, выполните команду npm i -g npm, здесь обновляется npm.
Все обновления глобальных пакетов можно найти здесь, npm -g outdated
14. Публикация пакета
Попробуйте выполнить следующую последовательность, чтобы создать пакет с нуля до его публикации, для наших соображений предположим, что наш пакет называется icecream
mkdir icecreamcd icecreamnpm init --yes- Добавьте новый файл index.js, и он станет нашей точкой входа для нашего пакета. Здесь вы можете добавить свои функции и прочее и т. Д.,
- Создайте учетную запись в npm до
npm adduser, в противном случае войдите, используяnpm login. Укажите имя пользователя, пароль, электронную почту. - Чтобы опубликовать пакет, запустите
npm publish - Чтобы использовать вновь созданный пакет в другом проекте, запустите
npm i icecream - Поздравляю, вы опубликовали свой первый пакет.
15. Обновление опубликованного пакета
Теперь, если мы хотим добавить больше функций и создать новую версию нашего пакета.
Запустите npm version <major/minor/patch> & npm publish.
IV. Создание RESTful API с использованием Express
Пакет HTTP полезен для создания веб-сервера, это действительно один из подходов, но в сложном приложении мы не можем полагаться на HTTP. Но, к счастью, у нас есть экспресс-пакет, который представляет собой быструю и легкую структуру, построенную на основе пакета HTTP, которая может обрабатывать сложный рабочий процесс с немного меньшим жестким кодированием.
1. Службы RESTful
REST - Re презентационный S tate T ransfer устанавливает HTTP-команды (GET, POST, PUT, DELETE) на веб-сервере.
2. Представляем Express
Замена нашего старого HTTP-пакета новым экспресс-пакетом. Также, если вы заметили, у нас было много блоков if в наших HTTP-методах для предоставления доступа к нашим конечным точкам.
Выполните команды, чтобы запустить наш новый экспресс-проект.
mkdir express-democd express-demonpm init --yesnpm i express
Подробнее об экспрессе здесь.
3. Создание вашего первого веб-сервера
Создайте новый файл index.js, и здесь мы создадим наш веб-сервер.
Теперь для облегчения понимания давайте сохраним списки фильмов и будем управлять ими с помощью HTTP-глаголов.
const express = require('express')
const app = express() // Contains the HTTP Verbs
// GET METHOD
app.get('/', (req, res) => {
res.send('Hello World');
});
app.get('/api/movies', (req, res) => {
res.send([1,2,3,4,5])
})
// Listen PORT
app.listen(3000, () => console.log('Listening on port 3000'));
Теперь запустите код в cmd, node index.js и откройте браузер с конечной точкой localhost: 3000 и localhost: 3000 / api / movies, вы должны увидеть подобное сообщение, подобное приведенному ниже.


Подробнее об экспресс-API здесь.
4. Nodemon
Поэтому при каждом внесении изменений мы должны останавливать сервер и перезапускать его снова. Я понимаю, что это утомительно, поэтому мы будем использовать один пакет под названием nodemon (Node monitor), который сам перезапускает сервер, если в коде вносятся какие-либо изменения.
Выполните команду, чтобы установить nodemon, npm i nodemon.
Теперь запустите веб-сервер, запустив nodemon index.js.
5. Переменные среды
Переменная среды, по сути, является частью переменной, в которой выполняется процесс, и выполняется вне нашего приложения. Как наш номер ПОРТА здесь.
Сначала установите значение PORT в Windows, используя set PORT = 5000 или MAC, используя export PORT = 5000.
const express = require('express')
const app = express() // Contains the HTTP Verbs
// GET METHOD
app.get('/', (req, res) => {
res.send('Hello World');
});
app.get('/api/movies', (req, res) => {
res.send([1,2,3,4,5])
})
// PORT
const port = process.env.PORT || 3000;
// Listen PORT
app.listen(port, () => console.log(`Listening on port ${port}`));
6. Параметры маршрута
Теперь давайте добавим новые маршруты в нашу базу данных фильмов и получим их.
Теперь в маршрутах мы можем индексировать их, добавляя значения: id, и запрашивать их, добавляя параметры? Query.
const express = require('express')
const app = express() // Contains the HTTP Verbs
// GET METHOD
app.get('/', (req, res) => {
res.send('Hello World');
});
app.get('/api/movies', (req, res) => {
res.send([1,2,3,4,5])
})
app.get('/api/movies/:id', (req, res) => {
// res.send(req.params)
res.send(req.query)
})
// PORT
const port = process.env.PORT || 3000;
// Listen PORT
app.listen(port, () => console.log(`Listening on port ${port}`));


7. Обработка HTTP-запросов GET
Теперь давайте начнем получать запросы GET и предоставлять фильм на основе идентификатора, предоставленного пользователем. Итак, мы пишем небольшую функцию для получения фильма по идентификатору.
const express = require('express')
const app = express() // Contains the HTTP Verbs
const movies = [{ id:1, name: 'Tenet'},{ id:2, name: 'Quantum of Solace'},{ id:3, name: 'Inception'}]
// GET METHOD
app.get('/', (req, res) => {
res.send('Hello World');
});
app.get('/api/movies', (req, res) => {
res.send(movies)
})
app.get('/api/movies/:id', (req, res) => {
const movie = movies.find(m => m.id === parseInt(req.params.id));
if (!movie) res.status(404).send('No movie found for the given id') res.send(movie)
});
// PORT
const port = process.env.PORT || 3000;
// Listen PORT
app.listen(port, () => console.log(`Listening on port ${port}`));


8. Обработка запросов HTTP POST.
Теперь мы можем использовать метод POST для создания нового курса в базе данных фильмов на основе запроса от пользователя.
const express = require('express')
const app = express() // Contains the HTTP Verbs
app.use(express.json());
const movies = [{ id:1, name: 'Tenet'},{ id:2, name: 'Quantum of Solace'},{ id:3, name: 'Inception'}]
// GET METHOD
app.get('/', (req, res) => {
res.send('Hello World');
});
app.get('/api/movies', (req, res) => {
res.send(movies)
})
app.get('/api/movies/:id', (req, res) => {
const movie = movies.find(m => m.id === parseInt(req.params.id));
if (!movie) res.status(404).send('No movie found for the given id') res.send(movie)
});
app.post('/api/movies/', (req, res) => {
const movie = {id: movies.length + 1,name: req.body.name};
movies.push(movie);
res.send(movie)
});
// PORT
const port = process.env.PORT || 3000;
// Listen PORT
app.listen(port, () => console.log(`Listening on port ${port}`));
9. Вызов конечных точек с помощью Postman или Thunder Client.
Чтобы проверить, успешен ли вышеуказанный запрос POST, нам необходимо проверить его с помощью служб, предоставляемых почтальоном или громом, в зависимости от вашего выбора.
Очевидно, браузеры могут отправлять запросы GET только на эти конечные точки, поскольку они предназначены для этого.
Теперь мы видим, получаем успешный ответ 200.

10. Проверка ввода
Мы всегда должны проверять содержимое нашего ввода и выполнять некоторые примитивные проверки, чтобы убедиться, что они в порядке. Итак, мы заверяем, что имя фильма, которое они вставляют, не пустое, и говорим, что оно должно содержать минимум 3 символа.
Мы можем использовать простую логику, например if (! Req.body.name || req.body.name.length ‹3), но по мере того, как наш проект становится сложным, мы можем использовать пакет проверки ввода под названием Joi, подробнее об этом здесь .
Joi - API и документация здесь.
const express = require('express')
const app = express() // Contains the HTTP Verbs
const Joi = require('joi');
app.use(express.json());
const movies = [{ id:1, name: 'Tenet'},{ id:2, name: 'Quantum of Solace'},{ id:3, name: 'Inception'}]
// GET METHOD
app.get('/', (req, res) => {
res.send('Hello World');
});
app.get('/api/movies', (req, res) => {
res.send(movies)
})
app.get('/api/movies/:id', (req, res) => {
const movie = movies.find(m => m.id === parseInt(req.params.id));
if (!movie) res.status(404).send('No movie found for the given id') res.send(movie)
});
app.post('/api/movies/', (req, res) => {
const schema = Joi.object({name: Joi.string().min(3).required()
});
const result = schema.validate(req.body)
console.log('result', result)
if (result.error){
res.status(400).send(result.error.details[0].message)
return; // Don't execute the rest of the program
}
const movie = {id: movies.length + 1,name: req.body.name};
movies.push(movie);
res.send(movie)
});
// PORT
const port = process.env.PORT || 3000;
// Listen PORT
app.listen(port, () => console.log(`Listening on port ${port}`));


11. Обработка HTTP-запросов PUT
Теперь давайте обновим название фильма на основе идентификатора, предоставленного пользователем.
const express = require('express')
const app = express() // Contains the HTTP Verbs
const Joi = require('joi');
app.use(express.json());
const movies = [{ id:1, name: 'Tenet'},{ id:2, name: 'Quantum of Solace'},{ id:3, name: 'Inception'}]
// GET METHOD
app.get('/', (req, res) => {
res.send('Hello World');
});
app.get('/api/movies', (req, res) => {
res.send(movies)
})
app.get('/api/movies/:id', (req, res) => {
const movie = movies.find(m => m.id === parseInt(req.params.id));
if (!movie) res.status(404).send('No movie found for the given id') res.send(movie)
});
app.post('/api/movies/', (req, res) => {
const schema = Joi.object({name: Joi.string().min(3).required()
});
const result = schema.validate(req.body)
console.log('result', result)
if (result.error){
res.status(400).send(result.error.details[0].message)
return; // Don't execute the rest of the program
}
const movie = {id: movies.length + 1,name: req.body.name};
movies.push(movie);
res.send(movie)
});
app.put('/api/movies/:id', (req, res) => {
// Look up the movie
// If not existing, return 404
const movie = movies.find(m => m.id === parseInt(req.params.id));
if (!movie) res.status(404).send('No movie found for the given id') // 404
const { error } = validateMovies(req.body); // Equivalent to result.error
if (error){
res.status(400).send(error.details[0].message)
return; // Don't execute the rest of the program
}
// Update movie
// Return the updated movie
movie.name = req.body.name
res.send(movie)
})
function validateMovies(movie){
// Validate
// If invalid, return 400 - Bad request
const schema = Joi.object({
name: Joi.string().min(3).required() });
return schema.validate(movie);
};
// PORT
const port = process.env.PORT || 3000;
// Listen PORT
app.listen(port, () => console.log(`Listening on port ${port}`));


12. Обработка HTTP-запросов на удаление
Теперь давайте удалим название фильма на основе идентификатора, предоставленного пользователем.
app.delete('/api/movies/:id', (req, res) => {
// Look up the course
// Not Existing, return 404
const movie = movies.find(m => m.id === parseInt(req.params.id));
if (!movie) res.status(404).send('No movie found for the given
id') // 404
// Delete
const index = movies.indexOf(movie);
movies.splice(index, 1)
// Return the same movie
res.send(movie)
})


V. Экспресс продвинутые темы
1. Промежуточное
Промежуточное ПО - это то, что принимает объект запроса и возвращает ответ клиенту.
Обработка запроса иногда может проходить через конвейер процессов, каждый из которых содержит функцию промежуточного программного обеспечения. В приведенном ниже процессе операции JSON и маршрута являются промежуточным программным обеспечением.
app.use(express.json())
Нравится Запрос → json () → маршрут () → Ответ
2. Создание специального промежуточного программного обеспечения
Итак, для этой цели давайте создадим промежуточное программное обеспечение аутентификации для каждого получаемого нами запроса, а затем передадим управление следующему промежуточному программному обеспечению в нашем проекте. На более легкой ноте, если не передать его другому промежуточному программному обеспечению, он застрял бы внутри себя.
// Middleware - 1
app.use(express.json());
// Middleware - 2
app.use(function(req, res, next){
console.log('Logging request...');
next(); // Pass control to the next middleware; otherwise stuck
})
// Middleware - 2
app.use(authentication);
function authentication(req, res, next){
console.log('Authentication Starts...');
next();
}
Теперь, глядя на консоль, мы видим, что каждый полученный запрос каждый раз проходит через наше настраиваемое промежуточное программное обеспечение.

3. Встроенное промежуточное
Есть много встроенного промежуточного программного обеспечения, которое мы можем использовать. Например, app.use(express.json());, который автоматически анализирует req.body на предмет любых объектов JSON и предоставляет нам формат JSON для использования.
app.use (express.urlencoded ({extended: true}));
app.use (express.static («общедоступный»));
app.use(express.urlencoded({extended: true})); - Для получения данных формы в запросе.
app.use(express.static('public')); - для обслуживания статических файлов на веб-сервере, поэтому, если мы создадим общую папку и будем использовать некоторые статические файлы, к ней можно будет получить доступ из Интернета.
4. Стороннее промежуточное
Итак, давайте воспользуемся промежуточным программным обеспечением для шлема для демонстрации.
- шлем - ссылка, помогает защитить приложения Express с различными заголовками HTTP.
- morgan - ссылка, промежуточное ПО регистратора HTTP-запросов для node.js.
const express = require('express');
const helmet = require('helmet')
const morgan = require('morgan')
const app = express() // Contains the HTTP Verbs
app.use(express.json());
app.use(helmet());
app.use(morgan('tiny'));
Теперь, если мы попытаемся поразить конечные точки нашей программы node.js, это будет зарегистрировано, как показано ниже.

Подробнее об этом здесь.
5. Переменные среды
Иногда нам нужно хранить переменные среды, такие как порт, среда производства / разработки, ключи API и т. Д., Чтобы мы могли экспортировать или устанавливать их с помощью команды set NODE_ENV=production и использовать их в коде, обращаясь к ней. Вот простой пример использования:
console.log(`Node Environment : ${process.env.NODE_ENV}`)
if (app.get('env') == 'development'){
app.use(morgan('tiny'));
console.log('Morgan Enabled')
};
Таким образом, здесь мы регистрируем только наши входящие запросы, только если мы находимся на сервере разработки.
6. Конфигурация
7. Отладка
Иногда во время разработки нам может потребоваться просмотреть журналы большинства наших функций или маршрутов, а позже мы обычно удаляем их или комментируем их, а в будущем, если мы будем участвовать в каких-либо будущих обновлениях, мы, вероятно, вставим коды отладки или раскомментируйте их. Это утомительный и трудоемкий процесс.
Итак, мы рассмотрим пакет, чтобы сделать этот процесс более эффективным.
Чтобы установить пакет отладки, npm i debug,
const debug = require('debug')('app:debug')
// export DEBUG=app:debug in CMD
console.log(`Node Environment : ${process.env.NODE_ENV}`)
if (app.get('env') == 'development'){
app.use(morgan('tiny'));
debug('Morgan Enabled')
};
Итак, если мы установим переменную DEBUG как app: debug в cmd, а затем выполним код, то вы увидите инструкцию «Morgan Enabled», иначе она распечатает не все.
8. Шаблоны движков
Иногда нам нужно отправить разметку HTML для клиента вместо необработанного ответа JSON на глаголы HTTP. Итак, мы воспользуемся пакетом pug, чтобы установить его, запустив npm i pug.
Позже установите тип движка в коде, а также создайте новую папку «views» и создайте файл с именем index.pug, который содержит элементы HTML.
'''
index.pug
'''
html
head
title=title
body
h1=messgae
'''
index.js
'''
app.set('views', './views');
app.set('view engine', 'pug');
app.get('/', (req, res) => {
res.render('index', {'title':'My Express Movie App', message: 'Welcome to Movie Database'})
});

9. Структурирование Кодекса.
Вы можете проверить реализацию, которую мы сделали до сих пор, и все они выполнены в одном index.js, а также проверить встроенную суть, чтобы убедиться, что мы находимся на одной странице.
Кроме того, я согласен, это неэффективный способ, поэтому нам нужно аккуратно организовать весь код.
Организованный код разделен на несколько файлов и папок, и в нижнем разделе этой статьи добавлена ссылка на репозиторий GitHub.
VI. Асинхронный JavaScript
1. Асинхронный или синхронный код
Как мы уже выяснили, Node Js выполняет асинхронное или неблокирующее выполнение, что означает, что он не будет ждать завершения функции, поскольку выполняется строка за строкой.
Итак, давайте рассмотрим простой пример.
console.log('Before')
setTimeout(() => {
console.log('Simulating a database operation')
}, 2000);
console.log('After')
OUTPUTS :
Before
After
Simulating a database operation
Теперь из выходных данных мы видим, что он не ждал 2 секунды для выполнения вставленной нами функции тайм-аута, а скорее запустил функцию и перешел к следующему фрагменту кода. Позже в очереди событий поток информируется о том, что вывод готов, он собирает и отображает его.
Таким образом, мы видим вывод функции последним, а не вторым.
2. Шаблоны для работы с асинхронным кодом
Допустим, мы хотим вернуть переменную из асинхронной функции для дальнейшей обработки. Итак, если мы запустим приведенный ниже фрагмент кода,
console.log('Before')
const movie = getMovies(1)
console.log(movie)
console.log('After')
function getMovies(id){
setTimeout(() => {
console.log('Simulating a movie fetching operation')
return {id:id, Name:'Tenet'};
}, 2000);
}
OUTPUT:
Before
undefined
After
Simulating a movie fetching operation
Мы видим, что переменная movie не определена, потому что в данный момент эта переменная еще недоступна.
Итак, есть 3 шаблона, на которые следует обратить внимание при написании асинхронной программы.
- Обратные вызовы
- Обещания
- Асинхронный / ожидание
Разберем каждый из них и сделаем наш асинхронный код более понятным.
3. Обратные вызовы
Раньше переменная movie не была определена, теперь мы будем использовать
параметры обратного вызова для заполнения переменной.
Обратный вызов - это функция, которую мы вызываем, когда готов результат асинхронной операции.
Как показано в приведенном ниже фрагменте кода, мы создаем функцию getMovies с двумя аргументами, id и обратным вызовом, которая требует своего времени для предоставления вывода. Итак, как только вывод будет готов, обратный вызов соберет его.
И при вызове функции мы используем стрелочные функции / анонимную функцию для сбора полученного обратного вызова и отображения его в console.log. Здесь я вложил две функции с обратными вызовами для экспериментов.
4. Обещания
Обещание содержит конечный результат асинхронной операции.
Таким образом, асинхронная функция после завершения либо решает вернуть ответ, либо ошибку.
Итак, у обещания есть 3 состояния,
- В ожидании - текущая асинхронная операция
- Решено - выполнено, ответ получен
- Отклонено - ошибка
Итак, давайте посмотрим, как использовать обещания с простыми асинхронными операциями.
const p = new Promise((resolve, reject) => {
// Do some async work
setTimeout(() => {
resolve(1); // Return Output - 1
// reject(new Error('Some error')) // Return Error
}, 2000);
});
p
.then(result => console.log(`Result : ${result}`))
.catch(err => console.log(`Error : ${err.message}`))
Таким образом, обещание принимает два аргумента для разрешения и отклонения, решение содержит выходные данные, а отклонение содержит сообщения об ошибках, если таковые имеются.
Поскольку мы сохраняем обещание в переменной p, мы можем использовать встроенные функции, такие как .then, чтобы получить результат или .catch, чтобы отобразить вывод.
5. Замена обратных вызовов обещаниями
Поскольку мы создавали выборку фильма и его функцию выборки директора в примере обратных вызовов (раздел - 3), давайте заменим их обещаниями, как мы описали в разделе - 4.
А также посмотрите, как поглотить эту решимость и отказы, вытекающие из обещаний.
6. Параллельная обработка обещаний
Бывают случаи, когда мы создаем два обещания для двух разных функций или вызовов API, а затем выполняем некоторые операции после выполнения этих двух обещаний.
Поэтому для обработки таких сценариев у нас есть статический метод в Promise, который нам пригодится.
promise.all[(p1, p2)]- разрешает оба обещания, затем разрешает вывод или, в противном случае, если обнаруживает ошибку, отклоняет любое решение из любого из этих обещаний.promise.race[(p1, p2)]- обрабатывает обещания, которые выполняются первыми, затем разрешает вывод или иным образом, если обнаруживает ошибку. Не ждите другого текущего обещания и двигайтесь дальше.
7. Асинхронный режим и ожидание
Async и Await - это новые методы из Node.js, и мы перепишем наш фильм и режиссер, получая асинхронные методы.
На самом деле они написаны поверх промисов, но дают ощущение синхронного программирования.
Но есть некоторые изменения, которые необходимо сделать на уровне функций, так как нам нужно украсить функции, связанные с ключевым словом async.
Итак, мы создаем новую асинхронную функцию, которая выполняет все наши обещания, а также помещаем их в блок try & catch, чтобы отображать ошибку, если она вообще отображается.
VII. CRUD Операции с Mongoose
CRUD - это аббревиатура от CREATE, READ, UPDATE, DELETE, и мы будем выполнять CRUD в базе данных NoSQL, такой как Mongoose.
Следует отметить, что поскольку это более широкая тема, я решил написать ее отдельно и опубликовать как независимую статью.
Статью можно здесь.
Но не волнуйтесь, это снова построено на наших примерах, связанных с нашей базой данных фильмов.
VIII. Проверка данных Mongo
1. Проверка
В нашей базе данных фильмов, если мы попытаемся сохранить новый документ со всеми необходимыми полями на основе нашей определенной схемы, он примет его. Но если мы попытаемся сохранить его, не указав ни одно из полей, он снова сохранит его, потому что мы не упомянули, действительна конкретная запись или нет. Этот сценарий иногда важен, так как мы не можем позволить себе оставить его на волю случая.
Итак, давайте изменим нашу схему, чтобы обеспечить проверку данных.
const movieSchema = new mongoose.Schema({
movieName : {type: String, required: true}, // Validation Check
director: String,
imdbRating: Number,
cast: [String],
releaseDate: Date,
genre: String,
sequel: Boolean
});
Позже нам также потребуется обрабатывать отказы, на всякий случай, если пользователь не передал обязательный параметр, иначе наш код приведет к ошибке и не пройдет. Итак, мы будем использовать простой блок try-catch для обработки отклоненных обещаний, если таковые имеются.
async function insertMovie(name_, director_, rating_, cast_, date_, genre_, sequel_){
const movieObject = new movieClass({
movieName : name_,
director: director_,
imdbRating: rating_,
cast: cast_,
releaseDate: date_,
genre: genre_,
sequel: sequel_
});
// Handling rejected promises here.
try{
result = await movieObject.save()
return result
}catch (ex){
console.log('Error in Promises', ex.message)
}
}
2. Встроенные валидаторы
Ранее мы использовали встроенный валидатор для String, обязательно: true.
Мы можем добавить гораздо больше для строки и числа для проверки, не обращаясь к сторонним библиотекам. Также есть условный валидатор, который проходит, только если условие выполнено.
Затем давайте посмотрим на валидаторы String и Number, большинство из которых не требуют пояснений.
// String Validators
movieName : {
type: String,
required: true,
minlength: 2,
maxlength: 255,
// match: /pattern/ // Some Regex Pattern
},
genre: {
type: String,
required: true,
enum: ['horror', 'Action', 'science-fiction', 'supernatural- horror', 'comedy', 'romance', 'adventure']
// Input must be one of them enumerated above.
},
// Number Validators
releaseDate: Date,
imdbRating: {
type: Number,
min: 0,
max: 10,
required: function() { return this.releaseDate(); }
// Conditionally make the property required
}
Итак, вверху, в imdbRating, вместо того, чтобы передавать обязательное поле, которое должно быть истинным или ложным, мы передали условие, что только при наличии releaseDate мы хотим, чтобы рейтинг был заполнен. Это называется условной проверкой, свойство this. относится к объекту класса, который создается во время вызова функции.
3. Пользовательские валидаторы
Иногда нам нужно настроить условие, которого нет во встроенных валидаторах.
Итак, возьмем случай, поле cast в нашей базе данных фильмов должно иметь как минимум 2 актеров, указанных при создании записи в базе данных, но мы не можем просто использовать required в true, так как это потребуется, даже если есть одна запись приведения .
Поэтому мы изменим схему с помощью специальной проверки, а также укажем настраиваемое сообщение в случае сбоя.
cast: {
type: Array,
validate: {
validator: function(v){
return v && v.length > 2;
// Should not be a null and should have atleast two cast
},message: 'The Cast must contain atleast two character names'
}
}
4. Асинхронные валидаторы
Иногда нашей логике валидатора необходимо прочитать что-то из базы данных или выполнить HTTP-вызов и т. Д., Чтобы проверить логику, поэтому в этих случаях мы используем асинхронные валидаторы.
Поэтому мы используем логику обратных вызовов, как описано в предыдущих разделах.
cast: {
type: Array,
validate: {
isAsync: true,
validator: function(v, callback){
// Do some async work or here I simulated timeout
setTimeout(()=>{
const result = v && v.length > 2;
callback(result)
}, 5000);
},
message: 'The Cast must contain atleast two character names,
}
}
5. Ошибки валидации
Итак, в настоящее время в нашем дизайне, только если есть отклонение обещания, мы получаем сообщение об ошибке, поэтому трудно узнать, есть ли какие-либо ошибки в таких полях в нашей базе данных. Поэтому мы будем отображать сообщения об ошибках, если они есть, путем итерации по доступным полям.
async function insertMovie(name_, director_, rating_, cast_, date_, genre_, sequel_){const movieObject = new movieClass({
movieName : name_,
director: director_,
imdbRating: rating_,
cast: cast_,
releaseDate: date_,
genre: genre_,
sequel: sequel_});
try{
result = await movieObject.save()
return result
}catch (ex){
// Iterate throught the fields for error message
console.log('movieName Error', ex.errors.movieName)
for (field in ex.errors){
console.log(`${field} - Errors ${ex.errors[field]}`)
}}}
6. Параметры типа схемы
Мы уже видели некоторые типы схем. Поэтому мы добавим еще два для String и Number.
// Type - String
genre: {
type: String,
required: true,
enum: ['horror', 'Action', 'science-fiction', 'supernatural- horror', 'comedy', 'romance', 'adventure'],
lowercase: true, // Converts the entry to lower case
uppercase: true, // Converts the entry to lower case
trim: true // Removes padding or extra spaces
},
// Type - Number
imdbRating: {
type: Number,
min: 0,
max: 10,
get: v => Math.round(v), // Rounds the value during fetching.
set: v => Math.round(v), // Rounds the value during saving.
required: function() { return this.releaseDate; }
// Conditionally make the property required
}
XI. Аутентификация и авторизация
1. Введение
Аутентификация - это процесс идентификации пользователя, за которого они выдвигаются.
Авторизация определяет, есть ли у пользователя права на выполнение данной операции.
Итак, мы обновим нашу базу данных фильмов, чтобы включить эту авторизацию и аутентификацию.
Итак, мы создадим новые конечные точки в нашем приложении с пользователями, которые могут регистрироваться и входить в систему, используя REST API.
2. Создание схемы модели пользователя
Итак, давайте создадим базу данных пользователей и схему для включения таких полей, как имя, адрес электронной почты, пароль.
// MongoDb Schema
const userSchema = new mongoose.Schema({
username : {
type: String, required: true,
minlength: 2, maxlength: 50,
},
email: {
type: String,
required: true, unique: true,
minlength: 2, maxlength: 255,
},
password: {
type: String, required: true,
minlength: 2, maxlength: 1024,
},
});
const userClass = mongoose.model('userClass', userSchema);
function validateUser(user){
const schema = Joi.object({
name: Joi.string().min(3).max(50).required(),
email: Joi.string().min(3).max(255).required().email(),
password: Joi.string().min(3).max(255).required()
});
return schema.validate(user);
}
3. Регистрация пользователей
Итак, как только мы создадим схему пользователя того, как она должна быть, затем мы можем добавить маршруты, содержащие, предпочтительно, запрос POST для добавления пользователя, вместе с проверками имени, электронной почты, состояния пароля, уже зарегистрирован или нет, и т. Д.
router.post('/', async (req, res) => {
const { error } = validateUser(req.body)
if (error) return res.status(400).send(error.details[0].message);
let user = await userClass.findOne({email: req.body.email})
if (user) return res.status(400).send('User Already Registered.');
user = new userClass({
name: req.body.name,
email: req.body.email,
password: req.body.password
});
try{
await user.save()
res.send(user)
}catch (ex){
console.log('Error in Promises', ex.message)
res.status(400).send( ex.message)
}
})
4. Использование Lodash
На самом деле сложно каждый раз читать req.body внутри маршрутов, индексируя каждый элемент вручную. Поэтому мы будем использовать служебную библиотеку lodash, чтобы упростить нашу работу и сэкономить время.
Lodash упрощает JavaScript, избавляя от хлопот при работе с массивами, числами, объектами, строками и т. Д.
Поэтому я заменил несколько кодов из указанной выше регистрации пользователя с помощью служебной библиотеки lodash. Особенно .pick из библиотеки lodash для автоматического выбора элементов из req.body
router.post('/', async (req, res) => {
const { error } = validateUser(req.body)
if (error) return res.status(400).send(error.details[0].message);
let user = await userClass.findOne({email: req.body.email})
if (user) return res.status(400).send('User Already Registered.');
user = new userClass(_.pick(req.body, ['name', 'email',
'password']));
try{
await user.save()
res.send(_.pick(user, ['_id', 'name', 'email']))
}catch (ex){
console.log('Error in Promises', ex.message)
res.status(400).send( ex.message)
}
})
Подробнее об этом здесь.
5. Хеширование паролей.
Мы не можем хранить пароли, поскольку когда хакер взламывает базу данных, тогда будут видны все учетные записи пользователей и их пароли.
Поэтому мы используем популярную библиотеку под названием bcrypt для хеширования их паролей.
Нам необходимо понять концепцию SALT, которая используется в библиотеке bcrypt для шифрования и дешифрования пароля.
SALT - случайная строка, добавленная перед паролем или добавленным паролем.
Как уже упоминалось, он добавляет случайную строку к паролю, чтобы хакеры не могли угадать шаблоны.
Таким образом, перед сохранением пароля мы просто хешируем пароль и сохраняем его в базе данных.
const salt = await bcrypt.genSalt(10); user.password = await bcrypt.hash(user.password, salt);
Подробнее об этом здесь.
6. Аутентификация пользователей
Теперь, когда мы успешно сохранили пользователей в нашей базе данных, теперь мы должны получить логин пользователя и отправить их аутентификацию, проверив их в нашей базе данных.
Итак, я добавил новые маршруты POST для действия.
router.post('/', async (req, res) => {
const { error } = validateUser(req.body)
if (error) return res.status(400).send(error.details[0].message);
let user = await userClass.findOne({email: req.body.email})
if (!user) return res.status(400).send('Invalid Email.');
const validPassword = await bcrypt.compare(req.body.password,
user.password)
if (!validPassword) return res.status(400).send('Invalid
Password.');
try{
res.send(true) // Sending True
}catch (ex){
console.log('Error in Promises', ex.message)
res.status(400).send( ex.message)
}
})
На данный момент, если мы успешно аутентифицируем пользователя, мы просто отправляем истинный ответ. Но нам нужно отправить что-то более элегантное, чтобы пользователь мог продолжать выполнять некоторые операции API и т. Д.,
7. Веб-токен JSON (JWT)
Веб-токен JSON JWT - это длинная строка, идентифицирующая пользователя.
Перейдите по указанной ниже ссылке, чтобы увидеть, как это выглядит.

С левой стороны у нас есть токен JWT, окрашенный в красный, фиолетовый и синий цвета.
С правой стороны у нас есть соответствующие свойства цвета, красный цвет указывает на заголовок, то есть указанный алгоритм, фиолетовый указывает на секцию полезной нагрузки, т.е. которая содержит данные, связанные с пользователем, а синий цвет указывает на свойство цифровой подписи, то есть которая создается на основе содержимое тела полезной нагрузки вместе с секретным ключом, который доступен только на сервере.
Таким образом, хакер не может сгенерировать эту цифровую подпись, потому что она хранится на веб-сервере.
Подробнее об этом здесь.
8. Создание токенов аутентификации.
Теперь вместо того, чтобы отправлять простой истинный ответ клиентам при аутентификации, мы сгенерируем токен JWT для аутентификации пользователя, а затем проверим его на сайте jwt.io, чтобы убедиться, что наши токены работают правильно.
router.post('/', async (req, res) => {
const { error } = validateUser(req.body)
if (error) return res.status(400).send(error.details[0].message);
let user = await userClass.findOne({email: req.body.email})
if (!user) return res.status(400).send('Invalid Email.');
const validPassword = await bcrypt.compare(req.body.password,
user.password)
if (!validPassword) return res.status(400).send('Invalid
Password.');
try{
const token = jwt.sign({ _id: user._id, email: user.email }, 'jwtPrivateKey') // Payload + Private Key
res.send(token)
}catch (ex){
console.log('Error in Promises', ex.message)
res.status(400).send( ex.message)
}
})
Позвольте мне показать вам, как мы обращаемся к API, а затем проверяем полученный токен JWT.


Теперь, когда мы вставляем сюда токен JWT, который мы получили через запрос POST, мы видим, что соответствующая полезная нагрузка - это то, что мы отправили ранее для генерации JWT.
9. Установка заголовков ответов
Итак, раньше, когда мы создавали нового пользователя, мы отправляли только имя, адрес электронной почты в теле. Теперь мы также можем отправить токен JWT, но было бы целесообразно сохранить токен JWT в ответе заголовка полученного запроса POST.
Чтобы браузер мог сохранить этот токен JWT в переменной заголовка, и мы могли напрямую его аутентифицировать.
Итак, давайте внесем некоторые изменения в создание нового пользователя и отправим токен в заголовок возврата с переменными x-auth-jwtoken.
router.post('/', async (req, res) => {
const { error } = validateUser(req.body)
if (error) return res.status(400).send(error.details[0].message);
let user = await userClass.findOne({email: req.body.email})
if (user) return res.status(400).send('User Already Registered.');
user = new userClass(_.pick(req.body, ['name', 'email',
'password']));
const salt = await bcrypt.genSalt(10);
user.password = await bcrypt.hash(user.password, salt);
try{
await user.save()
const token = jwt.sign({ _id: user._id, email: user.email }, 'MySecurePrivateKey')
res.header('x-auth-jwtoken', token).send(_.pick(user, ['_id', 'name', 'email']))
}catch (ex){
console.log('Error in Promises', ex.message)
res.status(400).send( ex.message)
}
})

10. ПО промежуточного слоя авторизации
Теперь, когда у нас есть отдельная база пользователей, мы отправляем идентификационный ключ токена JWT в их заголовки браузера непосредственно после создания учетной записи. Теперь, когда эти пользователи выполняют какие-либо операции с нашими базами данных фильмов, такие как Get или POST - создают новую запись, нам нужно авторизовать их на основе их токена JWT в их заголовках.
Итак, если вы помните функцию промежуточного программного обеспечения из предыдущего раздела, мы использовали промежуточное программное обеспечение, которое аутентифицирует почти все запросы, не указывая их явно во всех кодах.
Так создадим новую функцию в папке промежуточного программного обеспечения для авторизации.
// CUSTOM FUNCTIONS - For Auth
function auth(req, res, next){
console.log('Authentication Starts...');
const token = req.header('x-auth-token');
if (!token) return res.status(401).send('Access Denied. No Token Provided')
try{
const decoded = jwt.verify(token, 'MyPrivateKeyJWT');
req.user = decoded;
next();
}catch(ex){
res.status(400).send('Invalid Token')
}
}
Теперь мы не хотим авторизовать пользователей во всех местах, поэтому, скажем, только когда пользователь выполняет какую-либо операцию рядом с базой данных фильмов, мы добавляем аутентификацию для выполнения.
Итак, в обработчиках маршрута фильмов мы добавим код.
const auth = require('../middleware/auth')
// Route, middleware, function
router.get('/', auth, async (req, res) => {
const allMovies = await movieClass.find().sort('movieName')
res.send(allMovies)
})
Теперь, если мы попытаемся использовать GET API, мы должны получить ошибку. Потому что мы не отправили токен аутентификации в заголовках.



Точно так же вы можете защитить большинство маршрутов, которые вам нужно защитить.
11. Получение текущего пользователя
Иногда нам нужно записать, кто какой API-запрос выполняет, поэтому в обработчиках маршрутов мы хотели бы знать подробности о пользователе. Итак, давайте включим идентификацию пользователей внутри маршрутов.
const auth = require('../middleware/auth')
const {userClass, validateUser} = require('../models/users')
router.get('/me', auth, async(req, res) => {
const user = await userClass.findById(req.user._id).select('-
password')
res.send(user);
});

X. Обработка и регистрация ошибок
Иногда в реальной жизни мы можем столкнуться с проблемами. Таким образом, мы не можем отправить пользователю сообщение о том, что у нас возникла проблема, вместо этого мы можем отправить пользователю понятное сообщение об ошибке, а также зарегистрировать ошибку, чтобы мы могли проверить ее и устранить как можно скорее. Итак, в следующем разделе мы можем узнать, как обрабатывать и регистрировать ошибки.
1. Обработка отклоненных обещаний
Когда мы имеем дело с обещаниями, нам нужно либо поместить блоки .then, .catch для обработки этих обещаний.
Но если мы используем функции async и await, тогда мы должны использовать блоки try, catch для обработки отклоненных обещаний, в противном случае процесс будет завершен.
Таким образом, при получении операции с фильмом, если база данных не готова, мы отправим дружественную ошибку, в которой говорится, что мы не готовы не обслуживать.
// Route, mware, function
router.get('/', auth, async (req, res) => {
try{
const allMovies = await movieClass.find().sort('movieName')
res.send(allMovies)
}catch (ex){
// Log the error
res.status(500).send('Something failed. Please try again')
}})
2. ПО промежуточного слоя Express Error
Мы могли бы разработать тысячи маршрутов с настраиваемыми сообщениями об ошибках и блоками try-catch, мы не можем изменять каждый из них независимо, поэтому нам нужно найти способ обрабатывать их все в одном месте, особенно в промежуточном программном обеспечении.
Так что эта реализация будет сложной, поэтому я бы предпочел, чтобы вы видели репозиторий GitHub, прикрепленный к этой статье, для большей ясности.
Мы создаем функцию обработки ошибок, чтобы использовать ее везде.
// Error handling
function errorHandling(err, req, res, next){
// Log the Exception
res.status(500).send('Something failed. Please try again')
}
module.exports = errorHandling
И используйте его как промежуточное ПО и назовите его в файле index.js app.use(errorHandling).
Кроме того, мы используем множество блоков try-catch почти на каждом маршруте, поэтому мы определяем универсальную функцию для ее обработки и передачи аргументов нашим маршрутам.
function asyncMiddleware(handler){
return async (req,res, next) => {
// Standard express route handler
try{
await handler(req, res);
}catch(ex){
next(ex)
}}}
module.exports = asyncMiddleware
Затем оберните наши маршруты и передайте обработчик функции.
router.get('/', auth, asyncMiddleware(async (req, res) => {
const allMovies = await movieClass.find().sort('movieName')
res.send(allMovies)
}));
3. Использование экспресс-асинхронных ошибок.
Поскольку мы заключаем все наши маршруты в функцию asyncMiddleware, это снова становится громоздко, к счастью, есть пакет express-async-errors, который выполняет эту работу за нас.
Чтобы установить его и вызвать в index.js, например require('express-asunc-errors'), он позаботится обо всех этих маршрутах, поэтому вы можете удалить обертку asyncMiddleware из маршрутов.
4. Регистрация ошибок
Для регистрации ошибок мы можем использовать пакет Winston.
Он имеет транспортные методы, такие как консоль, HTTP, файл, чтобы либо просто отобразить ошибку, либо отправить ошибку на адрес HTTP или сохранить ее в файл.
winston.add(new winston.transports.File({ filename: 'combined.log' }))
Кроме того, у него есть плагины расширения для хранения в MongoDB, Redis и т. Д.,
Итак, мы модифицируем обработчик ошибок, чтобы записать сообщение в файл.
function errorHandling(err, req, res, next){
// Log the Exception
winston.log('error', err.message, err)
// Logging level - error, warning, info, verbose, debug, silly
res.status(500).send('Something failed. Please try again')
}
module.exports = errorHandling

Подробнее об этом здесь.
5. Вход в MongoDB.
Чтобы хранить журналы в MongoDB напрямую, без создания операции CRUD вручную, мы можем использовать другую библиотеку от Winston.
// npm install [email protected] winston.add(new winston.transports.MongoDB({ db: 'mongodb://localhost/movies', level: 'error' }))
Вот и все, он будет записывать ошибки прямо в MongoDB, если мы столкнемся с какими-либо ошибками.

6. Обработка неперехваченных исключений
Не все ошибки возникают внутри маршрутов. Иногда мы можем получить внешние ошибки, и настоящая реализация не может их найти, поэтому нам нужно добавить в Winston новый метод для регистрации этих ошибок.
process.on('uncaughtException', (ex) => {
console.log('Some uncaught Excpetion')
winston.error(ex.message, ex)
})
// throw new Error('Something went down')
7. Необработанные отклонения обещаний
Здесь мы обрабатываем как необработанные исключения, так и необработанные отклонения обещаний. Внимание: это остановит сервер узла.
winston.handleExceptions(
new winston.transports.File({filename: 'uncaughtExceptions.log'})
)
process.on('unhandledRejection', (ex) => {
throw ex;
})
// Error simulation
const p = Promise.reject(new Error('Promise failure'))
p.then( () => console.log('Done'))
8. Рефакторинг кода.
Файл запуска index.js не должен содержать все параметры, которые мы используем в нашем проекте, поэтому мы должны разделить маршруты, ведение журнала, базу данных и т. Д. В другом файле для упрощения отладки.
Итак, я сделал это и разместил новые коды в репозитории Github, где вы можете вытащить их и поиграть.
Поздравляю с достижением конца.
Ссылки и репозитории
Содержит все коды и зависимости, относящиеся к этой статье, можно найти здесь: https://github.com/bala-codes/A-Primer-on-Node.js
А пока увидимся в следующий раз.
Автор статьи:
БАЛАКРИШНАКУМАР V