Владимир Дронов Санкт-Петербург «БХВ-Петербург» 2024
УДК 004.438JavaScript ББК 32.973.26-018.1 Д75 Дронов В. А. Д75 Node.js, Express, MongoDB и React. 23 урока для начинающих. — СПб.: БХВ-Петербург, 2024. — 608 с.: ил. — (Для начинающих) ISBN 978-5-9775-1853-6 В книге 23 урока, более 40 практических упражнений и заданий для самостоятельной работы. Даны основы программирования на Node.js: проекты и манифесты, установка дополнительных библиотек, импорт и экспорт. Описан серверный веб-фреймворк Express: работа с запросами и ответами, маршрутизация, шаблонизатор EJS, обработка пользовательских данных, статических файлов, валидация, сохранение выгруженных файлов, вывод сообщений об ошибках и разграничение доступа. Показана работа с СУБД MongoDB: запись и выборка данных, фильтрация, сортировка, применение моделей Mongoose. Рассказано о двухзвенных веб-приложениях, бэкендах и фронтендах. Рассмотрено программирование бэкендов средствами Express и фронтендов с применением клиентского веб-фреймворка React. Затронуты вопросы повышения производительности и защищенности веб-приложений, ввод их в эксплуатацию. Электронный архив на сайте издательства содержит полный исходный код описанных в книге примеров. Для начинающих веб-разработчиков УДК 004.438JavaScript ББК 32.973.26-018.1 Группа подготовки издания: Руководитель проекта Евгений Рыбаков Зав. редакцией Людмила Гауль Редактор Григорий Добин Компьютерная верстка Ольги Сергиенко Дизайн обложки Зои Канторович "БХВ-Петербург", 191036, Санкт-Петербург, Гончарная ул., 20 ISBN 978-5-9775-1853-6 © ООО "БХВ", 2024 © Оформление. ООО "БХВ-Петербург", 2024
Оглавление Предисловие ................................................................................................................... 15 Почему Node.js? ............................................................................................................................. 15 Что такое MERN? .......................................................................................................................... 16 Использованные программные продукты ................................................................................... 17 Типографские соглашения ............................................................................................................ 18 ЧАСТЬ I. НАЧАЛА ПРОГРАММИРОВАНИЯ НА NODE.JS .............................. 21 Урок 1. Введение в Node.js ........................................................................................... 23 1.1. Упражнение. Запуск Node.js и работа в интерактивном режиме ....................................... 24 1.2. Упражнение. Написание простейшего консольного Node-приложения ........................... 25 Теория ...................................................................................................................................... 25 Практика .................................................................................................................................. 26 1.3. Упражнение. Создание манифеста проекта ......................................................................... 28 Теория ...................................................................................................................................... 28 Практика .................................................................................................................................. 29 1.4. Манифест проекта: подробности .......................................................................................... 32 1.4.1. Создание манифеста проекта ...................................................................................... 32 1.4.2. Формат JSON ................................................................................................................ 32 1.4.3. Структура манифеста проекта ..................................................................................... 34 1.4.4. Нумерация версий ........................................................................................................ 35 1.5. Упражнение. Расцвечивание вывода приложения. Использование дополнительных библиотек ................................................................................................... 36 Теория ...................................................................................................................................... 36 Практика .................................................................................................................................. 37 1.6. Установка, обновление и удаление зависимостей. Реестр проекта ................................... 38 1.7. Экспорт, импорт и прогон ..................................................................................................... 41 1.7.1. Экспорт .......................................................................................................................... 42 1.7.1.1. Экспорт по умолчанию ................................................................................... 42 1.7.1.2. Множественный экспорт ................................................................................ 42 Синтаксис № 1 ..................................................................................................... 43 Синтаксис № 2 ..................................................................................................... 43 1.7.1.3. Гибридный экспорт ......................................................................................... 44 1.7.2. Импорт ........................................................................................................................... 44 1.7.2.1. Импорт по умолчанию .................................................................................... 44 1.7.2.2. Множественный импорт ................................................................................. 45 Синтаксис № 1 ..................................................................................................... 45 Синтаксис № 2 ..................................................................................................... 46 1.7.2.3. Гибридный импорт .......................................................................................... 46 1.7.2.4. Написание путей к модулям ........................................................................... 46 1.7.2.5. Как выполняется импорт? ............................................................................... 48 1.7.3. Прогон модулей ............................................................................................................ 48
4 Оглавление 1.8. Окружение и переменные окружения ................................................................................... 49 1.9. Упражнение. Поддержка переменных окружения .............................................................. 53 1.10. Упражнение. Написание сценариев проекта ...................................................................... 55 Теория .................................................................................................................................... 55 Практика ................................................................................................................................ 55 1.11. Восстановление проекта ...................................................................................................... 56 1.12. Самостоятельные упражнения ............................................................................................ 57 Урок 2. Основные принципы веб-разработки на Node.js ...................................... 58 2.1. Как функционирует Всемирная паутина? ............................................................................ 58 2.1.1. Клиенты и серверы. Ресурсы. Веб-серверы ............................................................... 58 2.1.2. Интернет-адрес и его составляющие .......................................................................... 59 2.1.3. Взаимодействие веб-обозревателя и веб-сервера ...................................................... 62 2.1.3.1. Клиентский запрос .......................................................................................... 62 2.1.3.2. Серверный ответ .............................................................................................. 63 2.1.4. Веб-сайты и веб-приложения ...................................................................................... 64 2.1.4.1. Веб-сайт ............................................................................................................ 64 2.1.4.2. Веб-приложение .............................................................................................. 65 2.2. Упражнение. Написание простейшего тестового веб-приложения ................................... 66 2.3. События и их обработка ......................................................................................................... 68 2.4. Упражнение. Использование утилиты nodemon .................................................................. 69 2.5. Утилита nodemon: подробности ............................................................................................ 71 2.6. Упражнение. Реализация маршрутизатора .......................................................................... 73 Теория ...................................................................................................................................... 74 Практика .................................................................................................................................. 74 2.7. Упражнение. Использование базы данных и модели .......................................................... 78 Теория ...................................................................................................................................... 78 Практика .................................................................................................................................. 78 2.8. Синхронные и асинхронные функции .................................................................................. 83 2.9. Самостоятельное упражнение ............................................................................................... 86 ЧАСТЬ II. ВЕБ-ФРЕЙМВОРК EXPRESS ................................................................ 87 Урок 3. Введение в Express .......................................................................................... 89 3.1. Упражнение. Простейшее веб-приложение на Express ....................................................... 89 3.2. Приложение Express ............................................................................................................... 95 3.2.1. Создание объекта приложения .................................................................................... 95 3.2.2. Параметры приложения ............................................................................................... 95 3.2.3. Запуск приложения ...................................................................................................... 96 3.3. Клиентский запрос Express .................................................................................................... 97 3.4. Серверный ответ Express ..................................................................................................... 100 Урок 4. Маршрутизация и контроллеры ................................................................ 104 4.1. Маршрутизатор Express. Маршруты и URL-параметры ................................................... 104 4.1.1. Встроенный маршрутизатор ...................................................................................... 104 4.1.2. Маршруты ................................................................................................................... 104 4.1.2.1. Создание маршрутов ..................................................................................... 105 4.1.2.2. Написание шаблонных путей ....................................................................... 106 4.1.2.3. URL-параметры и параметризованные маршруты ..................................... 108 4.1.2.4. Выполнение маршрутизации: устранение коллизий .................................. 109
Оглавление 5 4.2. Контроллеры ......................................................................................................................... 109 4.2.1. Объявление контроллеров ......................................................................................... 109 4.2.2. Указание контроллеров в маршрутах ....................................................................... 111 4.2.3. Доступ к URL-параметрам и сведениям о совпавшем маршруте .......................... 111 4.3. Упражнение. Реализация маршрутизатора ........................................................................ 112 4.4. Выделенный маршрутизатор ............................................................................................... 114 4.4.1. Вложенные маршрутизаторы .................................................................................... 116 4.5. Упражнение. Использование выделенного маршрутизатора ........................................... 118 4.6. Настройки маршрутизаторов ............................................................................................... 118 4.6.1. Настройки встроенного маршрутизатора ................................................................. 118 4.6.2. Настройки выделенных и вложенных маршрутизаторов ....................................... 119 4.7. Вложенные приложения Express ......................................................................................... 119 Урок 5. Шаблоны. Шаблонизатор EJS .................................................................... 121 5.1. Упражнение. Использование шаблонов ............................................................................. 122 5.2. Интеграция Express и EJS .................................................................................................... 125 5.3. Написание шаблонов EJS. Команды EJS ............................................................................ 125 5.4. Рендеринг шаблонов ............................................................................................................ 127 5.4.1. Задание контекста шаблона ....................................................................................... 128 5.5. Включения ............................................................................................................................. 129 5.6. Упражнение. Использование включений ........................................................................... 130 5.7. Настройки шаблонизатора EJS ............................................................................................ 131 5.8. Упражнение. Стилизация приложения. CSS-фреймворк Picnic CSS ............................... 132 Теория .................................................................................................................................. 132 Практика .............................................................................................................................. 132 Урок 6. Получение данных, отправленных пользователем ................................ 138 6.1. Настройка приложения для получения данных ................................................................. 138 6.1.1. Настройка обработки GET-параметров .................................................................... 138 6.1.2. Настройка обработки POST-параметров .................................................................. 139 6.2. Получение отправленных данных ....................................................................................... 141 6.3. Упражнение. Поиск дел ....................................................................................................... 141 6.4. Добавление, правка и удаление данных ............................................................................. 144 6.4.1. Старый подход ............................................................................................................ 144 6.4.2. Новый подход ............................................................................................................. 145 6.4.3. Перекрытие HTTP-метода. Библиотека method-override ........................................ 146 6.5. Упражнение. Добавление, пометка и удаление дел .......................................................... 147 Урок 7. Посредники, статические файлы и обработка ошибок ......................... 155 7.1. Посредники ........................................................................................................................... 155 7.1.1. Привязка посредников ............................................................................................... 156 7.1.2. Объявление посредников ........................................................................................... 158 7.1.2.1. Объявление фабрик функций, выдающих посредники .............................. 162 7.2. Упражнение. Написание первого посредника ................................................................... 162 7.3. Обработка статических файлов ........................................................................................... 163 7.4. Упражнение. Стилизация приложения с помощью собственной таблицы стилей ......... 167 7.5. Обработка ошибок ................................................................................................................ 170 7.5.1. Оповещение о возникновении ошибки .................................................................... 170 7.5.1.1. Оповещение об ошибке средствами Node.js и Express .............................. 171 7.5.1.2. Оповещение об ошибке средствами библиотеки http-errors ...................... 172
6 Оглавление 7.5.1.3. Оповещение об ошибках в асинхронных контроллерах и посредниках ............................................................................................................. 173 7.5.2. Обработчики ошибок ................................................................................................. 174 7.5.2.1. Обработчик ошибок по умолчанию ............................................................. 174 7.5.2.2. Собственные обработчики ошибок .............................................................. 174 7.6. Упражнение. Реализация обработки ошибок ..................................................................... 175 Урок 8. Валидация ....................................................................................................... 180 8.1. Валидация данных, полученных от пользователя ............................................................. 180 8.1.1. Валидация средствами JavaScript .............................................................................. 180 8.1.2. Валидация средствами библиотеки express-validator .............................................. 181 8.1.2.1. Введение в express-validator ......................................................................... 181 8.1.2.2. Создание валидаторов ................................................................................... 182 8.1.2.3. Задание правил валидации............................................................................ 182 8.1.2.4. Задание очистителей ..................................................................................... 190 8.1.2.5. Задание модификаторов ................................................................................ 191 8.1.2.6. Последовательность выполнения правил валидации и очистителей ........ 192 8.1.2.7. Получение сообщений об ошибках ввода ................................................... 193 8.1.2.8. Получение данных, прошедших валидацию ............................................... 194 8.2. Упражнение. Валидация добавляемых дел ........................................................................ 195 8.3. Валидация значений URL-параметров ............................................................................... 197 Урок 9. Cookie, серверные сессии и всплывающие сообщения .......................... 200 9.1. Cookie .................................................................................................................................... 200 9.1.1. Подготовка Express для работы с cookie. Библиотека cookie-parser ...................... 200 9.1.2. Работа с cookie ............................................................................................................ 201 9.1.2.1. Создание cookie ............................................................................................. 201 9.1.2.2. Получение cookie ........................................................................................... 203 9.1.2.3. Удаление cookie ............................................................................................. 203 9.2. Упражнение. Задание порядка вывода дел ......................................................................... 203 9.3. Серверные сессии ................................................................................................................. 206 9.3.1. Подготовка Express для работы с серверными сессиями ........................................ 206 9.3.1.1. Библиотека express-session: обработка сессий ............................................ 206 9.3.1.2. Библиотека session-file-store: файловое хранилище сессий ....................... 209 9.3.2. Работа с серверными сессиями ................................................................................. 211 9.4. Всплывающие сообщения .................................................................................................... 212 9.4.1. Работа со всплывающими сообщениями средствами библиотеки express-session ....................................................................................................................... 213 9.4.2. Использование библиотеки express-flash-message ................................................... 213 9.5. Упражнение. Отображение сообщений об ошибках ввода............................................... 215 Урок 10. Выгрузка файлов ........................................................................................ 220 10.1. Выгрузка файлов средствами библиотеки multer ............................................................ 220 10.1.1. Получение генератора посредников ..................................................................... 220 10.1.1.1. Задание хранилища выгруженных файлов ........................................... 221 Хранилище DiskStorage .................................................................................... 221 Хранилище MemoryStorage .............................................................................. 223 10.1.1.2. Задание правил валидации выгруженных файлов ................................ 224 10.1.1.3. Задание функции валидации выгруженных файлов ............................ 225 10.1.2. Получение посредников, сохраняющих выгруженные файлы ........................... 227
Оглавление 7 10.1.3. Использование полученных посредников ............................................................ 229 10.1.4. Получение сведений о выгруженном файле ........................................................ 230 Особенности использования хранилища MemoryStorage ................................... 231 10.2. Вывод выгруженных файлов ............................................................................................. 231 10.2.1. Вывод средствами посредника, обрабатывающего статические файлы ............ 231 10.2.2. Вывод в контроллерах ............................................................................................ 232 10.3. Упражнение. Добавление иллюстраций к запланированным делам .............................. 234 Урок 11. Разграничение доступа и промисификация........................................... 240 11.1. Основы разграничения доступа ......................................................................................... 240 11.1.1. Собственно разграничение доступа ...................................................................... 240 11.1.2. Многопользовательские веб-приложения ............................................................ 242 11.1.3. Реализация разграничения доступа ....................................................................... 242 11.2. Хеширование ....................................................................................................................... 243 11.2.1. Генерирование хешей ............................................................................................. 243 11.2.1.1. Генерирование случайной соли ............................................................. 244 11.2.2. Сверка хешей .......................................................................................................... 245 11.3. Промисификация ................................................................................................................ 246 11.4. Упражнение. Превращение веб-приложения в многопользовательское ....................... 246 11.4.1. Регистрация новых пользователей ........................................................................ 246 11.4.2. Вход и выход ........................................................................................................... 254 11.4.3. Разграничение доступа к запланированным делам ............................................. 260 11.5. Самостоятельные упражнения .......................................................................................... 262 ЧАСТЬ III. СУБД MONGODB. МОДЕЛИ MONGOOSE .................................... 263 Урок 12. СУБД MongoDB ........................................................................................... 265 12.1. Серверные СУБД и их преимущества перед «самоделками» ......................................... 265 12.2. Введение в MongoDB ......................................................................................................... 266 12.2.1. Базы данных, коллекции и документы ................................................................. 267 12.2.2. Типы данных, поддерживаемые MongoDB .......................................................... 268 12.2.3. Встраивание и связывание документов ................................................................ 269 12.2.3.1. Встраивание вторичных документов в первичный .............................. 269 12.2.3.2. Связывание вторичных документов с первичным ............................... 271 12.2.4. Операции, выполняемые над документами .......................................................... 272 12.2.5. Индексы ................................................................................................................... 273 12.3. Клиентская библиотека MongoDB .................................................................................... 274 12.3.1. Подключение к серверу MongoDB ........................................................................ 274 12.3.2. Выбор базы данных ................................................................................................ 275 12.3.3. Выбор коллекции .................................................................................................... 275 12.3.4. Добавление документов ......................................................................................... 276 12.3.4.1. Добавление одного документа ............................................................... 276 12.3.4.2. Добавление нескольких документов ..................................................... 277 12.3.5. Поиск документа ..................................................................................................... 278 12.3.5.1. Написание условий поиска ..................................................................... 278 Условия простого сравнения ............................................................................ 279 Поиск документа по объектному идентификатору ........................................ 280 Условия, использующие операторы сравнения .............................................. 280 Составные условия, содержащие логические операторы .............................. 281 12.3.5.2. Указание набора выдаваемых атрибутов .............................................. 284 12.3.5.3. Типы значений атрибутов у выдаваемого документа .......................... 285
8 Оглавление 12.3.6. Фильтрация документов ......................................................................................... 285 12.3.6.1. Выдача всех документов из коллекции ................................................. 286 12.3.6.2. Сортировка выданных документов........................................................ 286 12.3.6.3. Выдача части отфильтрованных документов ....................................... 287 12.3.6.4. Курсор. Получение отфильтрованных документов ............................. 287 12.3.7. Исправление документов ....................................................................................... 289 12.3.7.1. Исправление одного документа ............................................................. 289 12.3.7.2. Исправление нескольких документов ................................................... 291 12.3.8. Удаление документов ............................................................................................. 292 12.3.8.1. Удаление одного документа ................................................................... 292 12.3.8.2. Удаление нескольких документов ......................................................... 292 12.4. Вместо упражнения ............................................................................................................ 292 Урок 13. Mongoose: объектные модели и их написание ....................................... 294 13.1. Введение в объектные модели. Mongoose ........................................................................ 294 13.2. Mongoose: подключение к базе данных ............................................................................ 295 13.3. Mongoose: создание моделей ............................................................................................. 296 13.3.1. Схемы Mongoose ..................................................................................................... 297 13.3.1.1. Написание перечней атрибутов ............................................................. 297 13.3.1.2. Типы значений атрибутов, поддерживаемые Mongoose ...................... 297 13.3.1.3. Указание простых индексов ................................................................... 298 13.3.1.4. Дополнительные параметры атрибутов ................................................ 299 13.3.1.5. Создание схем .......................................................................................... 301 13.3.1.6. Указание индексов любой сложности ................................................... 303 13.3.1.7. Виртуальные атрибуты и создание ........................................................ 303 13.3.2. Создание моделей ................................................................................................... 305 13.4. Упражнение. Перевод веб-приложения на работу с базой данных MongoDB (часть 1) ................................................................................................................................ 305 Урок 14. Mongoose: запись данных .......................................................................... 310 14.1. Mongoose: добавление, правка и удаление документов .................................................. 310 14.1.1. Добавление документов ......................................................................................... 310 14.1.1.1. Добавление одного документа ............................................................... 310 14.1.1.2. Добавление нескольких документов ..................................................... 311 14.1.2. Правка документов ................................................................................................. 312 14.1.2.1. Правка одного документа ....................................................................... 312 Способ первый: в стиле моделей Mongoose .................................................... 312 Способ второй: в стиле клиентской библиотеки MongoDB .......................... 314 14.1.2.2. Правка нескольких документов ............................................................. 316 14.1.3. Удаление документов ............................................................................................. 316 14.1.3.1. Удаление одного документа ................................................................... 316 14.1.3.2. Удаление нескольких документов ......................................................... 317 14.2. Низкоуровневые и высокоуровневые инструменты ........................................................ 317 14.3. Упражнение. Перевод веб-приложения на работу с базой данных MongoDB (часть 2) ................................................................................................................................ 318 Урок 15. Mongoose: выборка данных ...................................................................... 324 15.1. Mongoose: поиск документов ............................................................................................ 324 15.1.1. Базовые инструменты поиска ................................................................................ 324 15.1.1.1. Особенности указания значений в условиях поиска ............................ 325
Оглавление 9 15.1.2. Запрос к базе данных и получение результата ..................................................... 325 15.1.3. Конструирование запросов на поиск .................................................................... 326 15.1.3.1. Условия простого сравнения .................................................................. 327 15.1.3.2. Условия, использующие операторы сравнения .................................... 328 15.1.3.3. Составные условия с логическими операторами ................................. 330 15.1.3.4. Указание выдаваемых атрибутов ........................................................... 330 15.2. Mongoose: фильтрация документов .................................................................................. 331 15.2.1. Базовые инструменты фильтрации ....................................................................... 331 15.2.2. Конструирование запросов на фильтрацию ......................................................... 331 15.2.2.1. Сортировка документов .......................................................................... 331 15.2.2.2. Выдача части отфильтрованных документов ....................................... 332 15.3. Mongoose: получение количества документов................................................................. 332 15.4. Упражнение. Перевод веб-приложения на работу с базой данных MongoDB (часть 3) ................................................................................................................................ 333 Урок 16. Mongoose: агрегатные вычисления и расширение моделей ............... 334 16.1. Агрегатные вычисления ..................................................................................................... 334 16.1.1. Конвейер агрегатных вычислений и его создание................................................. 334 16.1.1.1. Стадия фильтрации документов ............................................................ 336 16.1.1.2. Стадия сортировки документов ............................................................. 336 16.1.1.3. Стадия формирования групп .................................................................. 336 Поддерживаемые агрегатные вычисления ...................................................... 337 Выполнение агрегатных вычислений по всем документам из коллекции ...... 339 16.1.1.4. Стадия фильтрации групп ...................................................................... 339 16.1.1.5. Стадия сортировки групп ....................................................................... 340 16.1.1.6. Стадия выборки части групп .................................................................. 340 16.1.2. Получение результатов агрегатных вычислений ................................................. 341 16.1.3. Выборка связанных документов............................................................................ 341 16.1.3.1. Стадия связывания документов ............................................................. 341 16.1.3.2. Доступ к атрибутам поддокументов ...................................................... 342 16.1.3.3. Стадия разворачивания массива ............................................................ 343 16.2. Упражнение. Создание веб-страницы наиболее активных пользователей ................... 344 16.3. Расширение функциональности моделей ......................................................................... 346 16.3.1. Обычные методы моделей ..................................................................................... 346 16.3.2. Статические методы моделей ................................................................................ 348 16.3.3. Методы, задающие условия при конструировании запросов ............................. 348 16.4. Упражнение. Расширение моделей ................................................................................... 349 16.5. Самостоятельные упражнения .......................................................................................... 351 ЧАСТЬ IV. ФРОНТЕНДЫ И БЭКЕНДЫ. ВЕБ-ФРЕЙМВОРК REACT .......... 353 Урок 17. Программирование бэкендов средствами Express ............................... 355 17.1. Традиционные и двухзвенные веб-приложения. Фронтенды и бэкенды ...................... 355 17.2. REST .................................................................................................................................... 357 17.2.1. Введение в REST .................................................................................................... 357 17.2.2. Практическая реализация REST ............................................................................ 357 17.2.3. Разграничение доступа в стиле REST. Жетонная аутентификация ................... 359 17.3. Программные инструменты, применяемые при разработке бэкендов .......................... 360 17.3.1. Инструменты Express для отправки ответов ........................................................ 360 17.3.2. Инструменты Express для проверки типа требуемых клиентом данных ........... 361
10 Оглавление 17.3.3. Инструменты Express для получения от пользователя данных в формате JSON .................................................................................................................. 362 17.3.4. Инструменты Mongoose для преобразования документов в JSON .................... 363 17.4. Работа с жетонами пользователей средствами библиотеки jsonwebtoken .................... 364 17.5. Обработка межсайтовых запросов. Библиотека cors ....................................................... 367 17.5.1. Проблема с доступом к «чужим» cookie ............................................................... 370 17.6. Упражнение. Превращение веб-приложения в бэкенд ................................................... 370 Урок 18. Программирование фронтендов. React: компоненты ......................... 382 18.1. Одностраничные фронтенды. Экраны .............................................................................. 382 18.2. Веб-фреймворк React ......................................................................................................... 383 18.2.1. Введение в React ..................................................................................................... 383 18.2.1.1. Компоненты ............................................................................................. 383 18.2.1.2. Реактивные данные. Пропы и состояния компонентов ....................... 385 18.2.1.3. Передача данных между компонентами. Спуск и подъем данных ..... 385 18.2.1.4. Разработка React-фронтендов ................................................................ 386 18.2.2. React-проект: создание и структура ...................................................................... 387 18.2.2.1. Создание React-проекта .......................................................................... 387 18.2.2.2. Структура React-проекта ........................................................................ 387 18.2.3. Разработка компонентов ........................................................................................ 389 18.2.3.1. Объявление компонентов ....................................................................... 389 18.2.3.2. Формирование содержимого компонентов. Язык JSX. Фрагменты ..... 391 Указание встроенных стилей ........................................................................... 394 Условный вывод ................................................................................................ 394 Вывод последовательностей ............................................................................. 395 Вывод потомков................................................................................................. 396 18.2.3.3. Работа с состояниями компонентов....................................................... 397 18.2.3.4. Обработка событий ................................................................................. 398 18.2.3.5. Взаимодействие с элементами управления ........................................... 399 18.2.3.6. Прямое обращение к DOM компонента. Рефы ..................................... 400 Использование рефов для постоянного хранения данных ............................ 401 18.2.3.7. Хуки .......................................................................................................... 401 18.2.3.8. Стилизация фронтенда ............................................................................ 404 18.2.3.9. Использование внедренных элементов в компонентах ....................... 406 18.2.4. Использование компонентов ................................................................................. 406 18.2.5. Запуск React-проекта на выполнение.................................................................... 407 18.3. Упражнение. Написание фронтенда (часть 1).................................................................. 407 18.3.1. Вывод перечня дел .................................................................................................. 407 18.3.2. Поиск и сортировка дел ......................................................................................... 412 18.3.3. Реализация входа и выхода .................................................................................... 415 Урок 19. React: контексты и маршрутизация ........................................................ 420 19.1. Контексты React .................................................................................................................. 420 19.2. Упражнение. Написание фронтенда (часть 2).................................................................. 422 19.3. Навигация во фронтендах. Пакет React Router ................................................................ 425 19.3.1. Основные понятия React Router ............................................................................ 426 19.3.2. Маршрутизация средствами React Router ............................................................ 426 19.3.2.1. Маршрутизатор ....................................................................................... 426 19.3.2.2. Коммутатор .............................................................................................. 427 19.3.2.3. Маршруты ................................................................................................ 428
Оглавление 11 19.3.3. Навигация средствами React Router ...................................................................... 429 19.3.3.1. Гиперссылки ............................................................................................ 429 Link: простая гиперссылка ................................................................................ 429 NavLink: расширенная гиперссылка................................................................. 430 19.3.3.2. Перенаправление ..................................................................................... 431 Компонент Navigate .......................................................................................... 431 Хук useNavigate() ............................................................................................... 432 19.3.4. Получение значений URL-параметров ................................................................. 432 19.3.5. Получение дополнительного значения ................................................................. 433 19.4. Упражнение. Написание фронтенда (часть 3).................................................................. 433 19.5. Самостоятельные упражнения .......................................................................................... 444 ЧАСТЬ V. ВВОД ВЕБ-ПРИЛОЖЕНИЙ В ЭКСПЛУАТАЦИЮ ........................ 445 Урок 20. Повышение производительности веб-приложений .............................. 447 20.1. Кеширование ....................................................................................................................... 447 20.1.1. Кеширование на стороне клиента ......................................................................... 448 20.1.1.1. Кеширование статических файлов средствами Express ....................... 448 20.1.1.2. Кеширование сгенерированных программно ресурсов средствами Express ................................................................................................. 450 20.1.1.3. Управление кешированием средствами библиотеки express-cache-ctrl ..................................................................................................... 451 20.1.2. Кеширование на стороне сервера .......................................................................... 453 20.1.2.1. Кеширование шаблонов средствами Express ........................................ 453 20.1.2.2. Кеширование произвольных данных средствами библиотеки cache-manager.......................................................................................................... 453 Библиотека cache-manager-fs-hash: файловое хранилище кешированных данных ...................................................................................... 456 20.2. Сжатие ответов ................................................................................................................... 457 20.3. Самостоятельные упражнения .......................................................................................... 458 Урок 21. Дополнительная защита веб-приложений ............................................. 459 21.1. Защита от других распространенных сетевых атак средствами библиотеки helmet ....... 459 21.2. Защита от атак CSRF .......................................................................................................... 460 21.3. Защита базы данных MongoDB ......................................................................................... 463 21.3.1. Создание суперадминистратора ............................................................................ 463 21.3.2. Создание пользователей ......................................................................................... 465 21.3.3. Вход на сервер MongoDB в веб-приложении ....................................................... 466 21.4. Самостоятельные упражнения .......................................................................................... 467 Урок 22. Журналирование. Библиотека morgan ................................................... 468 22.1. Введение в библиотеку morgan ......................................................................................... 468 22.2. Задание формата записей ................................................................................................... 469 22.2.1. Предопределенные форматы ................................................................................. 469 22.2.2. Написание строк форматов .................................................................................... 470 22.2.3. Расширенные инструменты для работы с форматами записей .......................... 471 22.2.3.1. Создание своих предопределенных форматов ..................................... 471 22.2.3.2. Создание своих литералов для строк форматов ................................... 472
12 Оглавление 22.3. Задание параметров журналирования ............................................................................... 473 22.3.1. Вывод журнала в файл ........................................................................................... 473 22.3.2. Вывод журнала в последовательность файлов .................................................... 474 22.4. Самостоятельное упражнение ........................................................................................... 476 Урок 23. Ввод веб-приложений в эксплуатацию ................................................... 477 23.1. Отладочный и эксплуатационный режимы работы веб-приложения ............................ 477 23.2. Подготовка фронтенда и бэкенда к эксплуатации ........................................................... 478 23.2.1. Подготовка бэкенда ................................................................................................ 479 23.2.2. Подготовка фронтенда ........................................................................................... 480 23.2.3. Создание эксплуатационной редакции фронтенда и встраивание ее в бэкенд ................................................................................................................................ 481 23.3. Подготовка веб-приложения к эксплуатации .................................................................. 482 23.4. Развертывание веб-приложения ........................................................................................ 483 23.5. Самостоятельные упражнения .......................................................................................... 483 Заключение ................................................................................................................... 485 ПРИЛОЖЕНИЯ........................................................................................................... 487 Приложение 1. Node.js: подготовка к работе ......................................................... 488 П1.1. Установка Node.js .............................................................................................................. 488 П1.2. Проверка работоспособности Node.js .............................................................................. 493 Приложение 2. Visual Studio Code: установка и использование ........................ 494 П2.1. Установка Visual Studio Code ........................................................................................... 494 П2.2. Запуск и завершение работы Visual Studio Code ............................................................ 500 П2.3. Главное окно Visual Studio Code ...................................................................................... 500 П2.4. Расширения Visual Studio Code и работа с ними ............................................................ 503 П2.5. Работа с отдельными файлами ......................................................................................... 509 П2.5.1. Создание файлов .................................................................................................... 509 П2.5.2. Открытие файлов ................................................................................................... 510 П2.5.2.1. Работа с вкладками главной области .................................................... 510 П2.5.2.2. Разделение главной области на секции ................................................ 511 Разделение на секции при открытии файлов .................................................. 511 Разделение на секции после открытия файлов ............................................... 514 П2.5.3. Сохранение файлов ................................................................................................ 514 П2.5.4. Отмена правок, сделанных в файле ...................................................................... 515 П2.5.5. Закрытие файлов .................................................................................................... 515 П2.6. Работа с папками................................................................................................................ 516 П2.6.1. Открытие папки ..................................................................................................... 516 П2.6.1.1. Область Открытые редакторы боковой панели ............................... 518 П2.6.2. Работа с содержимым открытой папки ................................................................ 519 П2.6.2.1. Создание файлов и папок ...................................................................... 519 П2.6.2.2. Открытие файлов .................................................................................... 520 П2.6.2.3. Закрытие файлов..................................................................................... 520 П2.6.2.4. Переименование файлов и папок .......................................................... 520 П2.6.2.5. Перемещение файлов и папок ............................................................... 521 П2.6.2.6. Удаление файлов и папок ...................................................................... 521
Оглавление 13 П2.7. Работа с программным кодом ........................................................................................... 521 П2.7.1. Автоматическая установка отступов ................................................................... 521 П2.7.2. Синтаксическая подсветка кода ........................................................................... 522 П2.7.3. Команды для работы с кодом ............................................................................... 522 П2.7.4. Списки быстрого выбора ...................................................................................... 522 П2.7.5. Подсказки по коду ................................................................................................. 523 П2.7.6. Выявление ошибок ................................................................................................ 524 П2.7.6.1. Ошибки времени компиляции ............................................................... 524 П2.7.6.2. Ошибки времени выполнения ............................................................... 525 П2.7.7. Просмотр структуры программного кода ............................................................ 526 П2.7.8. Поиск и замена ....................................................................................................... 528 П2.7.8.1. Поиск и замена в файле из активной папки ......................................... 528 Поиск .................................................................................................................. 528 Замена ................................................................................................................. 529 П2.7.8.2. Поиск и замена в файлах ....................................................................... 530 Поиск .................................................................................................................. 530 Замена ................................................................................................................. 532 П2.8. Терминал ............................................................................................................................ 533 П2.9. Отладка ............................................................................................................................... 534 П2.9.1. Запуск приложения для отладки ........................................................................... 535 П2.9.2. Точки останова ....................................................................................................... 535 П2.9.3. Пошаговое выполнение кода ................................................................................ 537 П2.9.4. Просмотр состояния приложения......................................................................... 539 П2.9.5. Просмотр вывода приложения ............................................................................. 541 П2.9.6. Создание конфигурации отладки ......................................................................... 541 П2.10. Вспомогательные инструменты ..................................................................................... 543 П2.10.1. Область Сценарии NPM боковой панели ........................................................... 543 П2.10.2. Палитра команд .................................................................................................... 544 П2.10.3. Создание нескольких главных окон ................................................................... 545 П2.10.4. Мини-карта ........................................................................................................... 546 П2.11. Настройка Visual Studio Code ......................................................................................... 547 П2.11.1. Задание параметров Visual Studio Code ............................................................. 547 П2.11.2. Задание цветовой схемы ...................................................................................... 550 П2.11.3. Задание тем значков ............................................................................................ 551 Приложение 3. MongoDB: установка ....................................................................... 552 Приложение 4. MongoDB Shell: установка и использование .............................. 558 П4.1. Установка MongoDB Shell ................................................................................................ 558 П4.2. Запуск и завершение MongoDB Shell............................................................................... 560 Приложение 5. MongoDB Compass: установка и использование ....................... 561 П5.1. Установка MongoDB Compass .......................................................................................... 561 П5.2. Запуск и завершение работы MongoDB Compass ........................................................... 562 П5.3. Главное окно MongoDB Compass ..................................................................................... 563 П5.4. Работа с соединениями ...................................................................................................... 564 П5.4.1. Создание соединений ............................................................................................ 564 П5.4.2. Перечни соединений .............................................................................................. 565 П5.4.3. Удаление соединений ............................................................................................ 566
14 Оглавление П5.5. Подключение к серверам MongoDB и отключение от них ............................................ 566 П5.5.1. Подключение к серверу ......................................................................................... 566 П5.5.1.1. Правка соединения ................................................................................. 567 П5.5.1.2. Подключение с входом .......................................................................... 567 П5.5.2. Просмотр списка баз данных и коллекций .......................................................... 567 П5.5.3. Отключение от сервера ......................................................................................... 569 П5.6. Работа с базами данных .................................................................................................... 569 П5.6.1. Создание базы данных........................................................................................... 569 П5.6.2. Удаление базы данных .......................................................................................... 570 П5.7. Работа с коллекциями ........................................................................................................ 570 П5.7.1. Создание коллекции .............................................................................................. 570 П5.7.2. Удаление коллекции .............................................................................................. 571 П5.8. Работа с индексами ............................................................................................................ 571 П5.8.1. Создание индекса ................................................................................................... 571 П5.8.2. Удаление индекса .................................................................................................. 573 П5.9. Работа с документами ....................................................................................................... 574 П5.9.1. Добавление документов в коллекцию .................................................................. 574 П5.9.2. Просмотр документов, имеющихся в коллекции ................................................ 575 П5.9.2.1. Фильтрация и сортировка документов ................................................. 577 П5.9.3. Правка и удаление документов ............................................................................ 578 П5.9.4. Прочие операции над документами ..................................................................... 579 П5.10. Экспорт и импорт документов........................................................................................ 580 П5.10.1. Экспорт документов ............................................................................................ 580 П5.10.2. Импорт документов ............................................................................................. 581 П5.11. Встроенная программа Mongosh .................................................................................... 582 Приложение 6. Postman: установка и использование .......................................... 583 П6.1. Установка Postman ............................................................................................................. 583 П6.2. Запуск и завершение работы Postman .............................................................................. 584 П6.3. Главное окно Postman ........................................................................................................ 585 П6.4. Отправка клиентских запросов ......................................................................................... 587 П6.4.1. Задание целевого интернет-адреса и HTTP-метода ............................................ 587 П6.4.2. Указание GET-параметров .................................................................................... 588 П6.4.3. Указание POST-параметров .................................................................................. 590 П6.4.4. Указание жетона пользователя для аутентификации ......................................... 590 П6.4.5. Указание заголовков запроса ................................................................................ 591 П6.4.6. Работа с cookie ....................................................................................................... 591 П6.4.7. Отправка созданного клиентского запроса ......................................................... 594 П6.5. Просмотр серверных ответов ........................................................................................... 594 П6.5.1. Просмотр тела ответа ............................................................................................ 595 П6.5.2. Просмотр cookie, полученных с ответом ............................................................. 596 П6.5.3. Просмотр заголовков ответа ................................................................................. 596 П6.6. Работа с вкладками главной области ............................................................................... 597 П6.7. Работа с историей Postman ................................................................................................ 599 Приложение 7. Описание файлового архива ......................................................... 601 Предметный указатель ............................................................................................... 602
Предисловие 27 мая 2009 года вышла первая версия Node.js — исполняющей среды для приложений, написанных на языке JavaScript. С этого момента JavaScript, ранее бывший всего лишь простеньким языком для создания веб-сценариев, превратился в развитый язык программирования общего назначения. На JavaScript стали писать инструментальные программы (применяемые для разработки других программ), консольные и даже оконные приложения. Но основную популярность Node.js получила в веб-разработке. Почему Node.js? У Node.js имеются два решающих преимущества перед конкурирующими платформами для разработки веб-приложений (к которым можно отнести, например, PHP, Python и Ruby). Знакомый язык программирования. Разработчику, до этого писавшему только веб-сценарии, для программирования веб-приложений не придется изучать новый язык. Экономия системных ресурсов — без потери производительности. Конкурирующие платформы для обработки нескольких одновременных запросов запускают несколько экземпляров веб-приложения. Выполнение каждого отдельного экземпляра приложения требует определенного объема системных ресурсов, которых, если экземпляров запущено слишком много, может и не хватить... Node.js для обработки множества запросов использует единственный экземпляр приложения, потребляющий относительно немного ресурсов. Производительность при этом не теряется, поскольку программный код приложения выполняется асинхронно. Асинхронные операции — конек Node.js. На уроке 1 книги они будут подробно рассмотрены. Веб-разработчики настолько полюбили Node.js, что написали для него ряд дополнительных библиотек и сопутствующих программ, помогающих в программировании. «Связка» из Node.js, одной ключевой программы и двух ключевых библиотек оказалась настолько востребованной, что получила собственную аббревиатуру — MERN.
16 Предисловие Что такое MERN? MERN — это «связка» из четырех ключевых технологий, применяемых при разработке веб-приложений на JavaScript, «фантастическая четверка» веб-программирования. Последняя буква: «N» — это, безусловно, Node.js. Вторая по счету буква: «E» — это веб-фреймворк Express. Веб-фреймворк — это программная библиотека, реализующая основную функциональность, своего рода каркас веб-приложения. Разработчик «навешивает» на этот каркас свои модули, добавляющие к нему нужные функции. Express — вероятно, первая разработка такого рода на Node.js (начальная версия вышла 16 ноября 2010 года). И до сих пор — популярнейший в мире вебфреймворк общего назначения. Как видим, не всякий первый блин выходит комом... Преимущества Express: • простота — все четко и понятно, без каких бы то ни было ненужных сущностей, которые лишь все усложняют; • компактность — ничего лишнего, только самое необходимое; • быстродействие — прямое следствие сказанного; • множество дополнительных библиотек, наращивающих функциональность фреймворка. Первая буква: «M» — это СУБД MongoDB. MongoDB не только мощна и быстра, но и как будто специально предназначена для того, чтобы работать совместно с Node.js. Судите сами: • отдельные сущности в базах MongoDB хранятся в виде документов, аналогичных простым объектам JavaScript; • отдельные значения в базах MongoDB хранятся в типах, аналогичных типам языка JavaScript. Все это устраняет необходимость в преобразовании данных при выборке, а значит, существенно ускоряет ее; • данные, записываемые в базу, и параметры выбираемых данных представляются в виде простых объектов JavaScript; • сами команды на запись и выборку данных подаются путем вызова методов у объекта, представляющего базу данных. Все это устраняет необходимость дополнительно изучать какие-либо промежуточные языки (наподобие SQL). Предпоследняя буква: «R» — это клиентский веб-фреймворк React. React применяется для разработки фронтендов современных двухзвенных вебприложений. Его преимущества те же, что у Express: простота, компактность, быстродействие и дополнительные библиотеки на разные случаи.
Предисловие 17 Эта книга посвящена разработке веб-приложений с применением Node.js, Express, MongoDB, React и еще ряда программных библиотек, расширяющих функциональность представленной здесь «фантастической четверки». Изучив MERN, вы, уважаемые читатели, станете весьма востребованными специалистами на рынке труда. Внимание! Автор предполагает, что читатели этой книги знакомы с языками HTML, CSS и JavaScript. В книге все это описываться не будет. Электронное приложение Сопровождающий книгу файловый архив содержит программный код сайта электронной доски объявлений и доступен по ссылке https://zip.bhv.ru/9785977518536.zip и на странице книги на сайте https://bhv.ru/ (см. приложение 7). Использованные программные продукты Автор применял в работе над книгой следующие версии ПО: Microsoft Windows 10, русская 64-разрядная редакция со всеми установленными обновлениями; Node.js — 19.7.0, 64-разрядная редакция; npm — 9.5.0; Visual Studio Code — 1.76.2; chalk — 5.2.0; dotenv — 16.0.3; nodemon — 2.0.22; Express — 4.18.2; EJS — 3.1.9; method-override — 3.0.0; express-validator — 7.0.0; cookie-parser — 1.4.6; express-session — 1.17.3; session-file-store — 1.5.0; express-flash-message — 2.1.0; multer — 1.4.4; MongoDB — 6.0.6, 64-разрядная редакция; MongoDB Shell — 1.9.1, 64-разрядная редакция; MongoDB Compass — 1.37.0, 64-разрядная редакция; клиентская библиотека MongoDB — 5.6.0;
18 Предисловие Mongoose — 7.2.4; Postman — 10.15.0, 64-разрядная редакция; jsonwebtoken — 9.0.1; cors — 2.8.5; React — 18.2.0; React Router — 6.14.2; express-cache-ctrl — 1.1.0; cache-manager — 5.2.3; cache-manager-fs-hash — 1.0.0; compression — 1.7.4; helmet — 7.0.0; tiny-csrf — 1.1.3; morgan — 1.10.0; rotating-file-stream — 3.1.1. Типографские соглашения Для написания различных языковых конструкций, применяемых в JavaScript, в книге использованы следующие типографские соглашения. В угловые скобки: <> — заключаются наименования различных значений, которые дополнительно выделяются курсивом. В реальный код, разумеется, должны быть подставлены конкретные значения. Пример: export default <сущность> Здесь вместо подстроки сущность должно быть подставлено реальное имя сущности. В квадратные скобки: [] — заключаются необязательные фрагменты кода. Пример: nodemon [<ключи>] Здесь ключи могут указываться, а могут и отсутствовать. Вертикальной чертой: | — разделяются различные варианты языковой конструкции, из которых следует указать лишь какой-то один. Пример: npm init -y|--yes Здесь надо поставить либо: -y, либо: --yes. Слишком длинные, не помещающиеся на одной строке языковые конструкции, автор разрывал на несколько строк и в местах разрывов ставил знак . Например: nodemon [<ключи самой nodemon>] [<путь к стартовому модулю> [<параметры приложения через пробел>]]
Предисловие 19 Приведенный код разбит на две строки, но должен быть набран в одну. Символ при этом набирать не следует. Троеточием: . . . — помечены фрагменты кода, пропущенные ради сокращения объема текста. Пример: { "name": "console-app", "type": "module", . . . } Здесь пропущено все содержимое приведенного JSON-документа, кроме двух первых атрибутов. Обычно такое можно встретить в исправленных впоследствии фрагментах кода — приведены лишь собственно исправленные выражения, а оставшиеся неизмененными пропущены. Также троеточие используется, чтобы показать, в какое место должен быть вставлен вновь написанный код: в начало исходного фрагмента, в его конец или в середину, между уже присутствующими в нем выражениями. Полужирным шрифтом выделен вновь добавленный и исправленный код. Пример: import chalk from 'chalk'; if (process.argv.length < 3) { . . . } Здесь добавлено выражение импорта объекта chalk из одноименной библиотеки. Зачеркнутым шрифтом выделяется код, подлежащий удалению. Пример: if (process.argv.length < 3) { console.error('Укажите величину в дюймах'); console.error(chalk.red('Укажите величину в дюймах')); process.exit(1); } Здесь следует удалить первую, зачеркнутую строку блока, присутствующего в условном выражении, и вместо нее вставить вторую, выделенную полужирным шрифтом. Еще раз внимание! Все приведенные здесь типографские соглашения имеют смысл лишь в форматах написания языковых конструкций JavaScript. В реальном программном коде используются только знак , троеточие, полужирный и зачеркнутый шрифт.
ы ЧАСТЬ I Начала программирования на Node.js Урок 1. Введение в Node.js Урок 2. Основные принципы веб-разработки на Node.js
Урок 1 Введение в Node.js Начнем изучение Node-программирования с самых основ. И в качестве примера напишем простое консольное приложение конвертора, преобразующего величины размеров из дюймов в сантиметры. Исходную величину оно будет получать из единственного параметра строки запуска. Консольное приложение — запускается из консоли, получает необходимые для работы параметры и ключи из строки запуска, выводит результаты также в консоли в виде обычного текста и сразу после этого завершает работу. Консоль1 — программа, исполняющая команды, которые пользователь вводит с клавиатуры, а также различные консольные приложения, которые тоже запускаются командами, вводимыми с клавиатуры. В составе Windows поставляются две программы, исполняющие функции консоли: командная строка и Microsoft PowerShell. Строка запуска — команда, которую следует набрать в консоли для запуска того или иного консольного приложения. Параметр строки запуска — указывается в составе строки запуска, после пути к файлу запускаемого приложения, через пробел. Может быть как обязательным, так и необязательным для указания. Приложение может принимать несколько параметров строки запуска, которые в этом случае разделяются пробелами. Командный ключ (или просто ключ) — разновидность параметра командной строки. Служит для задания какого-либо режима работы консольного приложения и представляет собой символическое обозначение, начинающееся с одного или двух дефисов. Как правило, необязателен для указания. Может передавать приложению какое-либо значение, которое записывается после ключа через пробел. Вывод — любые данные, выводимые приложением: результаты ее выполнения, сообщения об ошибках, справочная информация и др. Прежде чем начать учиться программированию под Node.js, следует установить саму эту исполняющую среду на свой компьютер. Как это сделать, описывается в приложении 1. 1 Также встречается название «терминал». Однако во избежание путаницы автор будет применять это название только применительно ко встроенной консоли Visual Studio Code (см. разд. П2.8).
24 Часть I. Начала программирования на Node.js Поскольку в дальнейшем мы будем набирать весьма объемный программный код и сохранять его в многочисленных файлах, нам понадобится специализированный текстовый редактор для программистов. Например, Visual Studio Code, установка и основы приемы использования которого описаны в приложении 2. Внимание! Эта книга не претендует на полное описание всех программных инструментов, предлагаемых Node.js. В ней будут представлены лишь ключевые инструменты: манифест проекта, экспорт и импорт. За полным описанием Node.js обращайтесь к другим книгам1 и официальному сайту. 1.1. Упражнение. Запуск Node.js и работа в интерактивном режиме Запустим исполняющую среду в интерактивном режиме и попробуем выполнить несколько выражений JavaScript. Чтобы исполняющая среда Node.js работала в интерактивном режиме, ее следует запустить из консоли как обычное консольное приложение. Интерактивный режим — режим работы консольного приложения, при котором пользователь отдает приложению команды, вводя их с клавиатуры, и тут же получает результат. 1. Запустим Node.js, выбрав пункт меню Пуск | Node.js. Исполняющая среда будет запущена в отдельном экземпляре командной строки Windows. Также можно сначала запустить командную строку, выбрав пункт меню Пуск | Служебные — Windows | Командная строка, и подать в ней команду: node Запущенная среда Node.js выведет приветствие, номер своей версии и предложение набрать команду .help для получения справки: Welcome to Node.js v19.7.0. Type ".help" for more information. Далее появится приглашение к вводу команды, имеющее вид знака «больше» и мигающего текстового курсора: > _ 2. Вычислим произведение чисел 2 и 2, набрав выражение 2 * 2 языка JavaScript после упомянутого приглашения2 : > 2 * 2 1 Например, к книге Николая Прохоренка и Владимира Дронова «JavaScript и Node.js для веб-разработчиков», https://tinyurl.com/4f693n2s. 2 Команды, набираемые пользователем, в тексте примеров выделены полужирным шрифтом и помечены знаком «больше» слева (то есть приглашением к вводу команды).
Урок 1. Введение в Node.js 25 3. Нажмем клавишу <Enter>, чтобы вычислить выражение. Получим ожидаемый результат1 : 4 4. Вычислим разность чисел 321 и 123, частное от деления 125 на 5 и квадратный корень от 12, не забывая после ввода каждого выражения нажимать <Enter>: > 321 - 123 198 > 125 / 5 25 > Math.sqrt(12) 3.4641016151377544 Пожалуй, достаточно на первый раз. 5. Завершим работу Node.js, набрав команду: > .exit Исполняющая среда завершит работу, и мы увидим приглашение к вводу команды, выведенное командной строкой. Если вы запускали Node.js из командной строки и она вам более не нужна, можете закрыть ее. Но мы не для того устанавливали Node.js, чтобы применять ее в качестве мощного, но не очень удобного в использовании калькулятора. Мы хотим писать программы! 1.2. Упражнение. Написание простейшего консольного Node-приложения Теория Программный код приложения (любого, не обязательно написанного для Node.js) всегда организуется в виде проекта. Проект — совокупность модулей с кодом приложения и иных файлов, которые входят в его состав (графических изображений, таблиц стилей и пр.), помещенная в отдельную папку. Модуль — общее название JavaScript-файлов с программным кодом и JSONфайлов с данными. Папка проекта — папка, в которой хранится проект. Содержит только файлы, непосредственно относящиеся к приложению. 1 Выводимые результаты приводятся шрифтом обычной насыщенности, без символа «больше», с небольшим отступом слева. Все использованные в этой книге типографские соглашения описаны в предисловии.
26 Часть I. Начала программирования на Node.js Если модулей в проекте слишком много, для удобства работы их распределяют по вложенным папкам, основываясь на каких-либо их общих чертах. Например, в одной папке могут быть размещены контроллеры, в другой — схемы, в третьей — шаблоны (что все это такое, мы узнаем позже). Единственным исключением выступает стартовый модуль, который обычно хранится непосредственно в папке проекта. Стартовый модуль — модуль, запускающий приложение. Практически всегда хранится непосредственно в папке проекта и имеет имя index.js или app.js. Практика Наше первое приложение будет получать с параметром строки запуска исходную величину в дюймах и выводить ее преобразованной в сантиметры. 1. Создадим где-либо на диске нашего компьютера папку console, которая станет папкой нашего первого проекта. 2. Откроем эту папку в редакторе Visual Studio Code. Первая версия нашего приложения будет состоять всего из одного модуля. 3. Создадим в этой папке модуль index.js, который будет содержать код приложения. Вновь созданный модуль автоматически откроется в редакторе. Все готово для ввода кода. Сначала следует реализовать проверку, была ли при запуске приложения указана преобразуемая величина, и, если она не была указана, вывести в терминал соответствующее предупреждение и завершить приложение с выдачей кода завершения 1. Код завершения — числовой код, который выдается консольным приложением при завершении работы. Код, равный 0, обозначает успешное выполнение приложения, код, равный или больший 1, — возникновение какой-либо ошибки. Коды завершения часто используются консольными приложениями, вызывающими другие консольные приложения, для определения, выполнилось ли вызванное приложение успешно. 4. Запишем в единственный модуль index.js следующий код: if (process.argv.length < 3) { console.error('Укажите величину в дюймах'); process.exit(1); } Переменная process создается самой исполняющей средой и хранит объект, представляющий саму эту исполняющую среду. Свойство argv этого объекта содержит массив, элементы которого хранят отдельные части строки запуска. Первый элемент этого массива всегда хранит путь к исполняемому файлу самой Node.js (это файл C:\Program Files (x86)\nodejs\node.exe или C:\Program Files\nodejs\ node.exe), а второй элемент — путь к файлу стартового модуля приложения
Урок 1. Введение в Node.js 27 (у нас — единственный модуль index.js). Третий и последующие элементы будут содержать параметры и ключи, записанные в строке запуска приложения. В нашем случае третий элемент этого массива будет содержать единственный параметр, переданный приложению, — исходную величину, подлежащую преобразованию. Следовательно, чтобы узнать, указал ли пользователь исходную величину, достаточно удостовериться, что массив из свойства argv содержит не менее двух элементов. Объект исполняющей среды из переменной process также поддерживает метод exit(), завершающий выполнение приложения с выдачей указанного кода: exit([<код завершения>=0]) Элементы массива из свойства argv представляют собой строки. Поэтому перед собственно вычислениями следует привести заданную пользователем исходную величину к числовому типу. 5. Добавим следующий код: const inches = parseFloat(process.argv[2]); Также следует учесть, что пользователь в качестве исходной величины по ошибке может задать что-либо, отличное от числа. В этом случае функция parseFloat(), встроенная в JavaScript, вернет значение NaN, эквивалентное логическому false. 6. Добавим код, выполняющий такую проверку: if (!inches) { console.error('Укажите дюймы в виде числа'); process.exit(1); } Теперь можно преобразовать полученную величину в сантиметры, умножив ее на 2,54, и вывести в консоль. 7. Добавим необходимый код1 : const centimetres = inches * 2.54; console.log(`${inches}\" >>> ${centimetres} см.`); Выполнить написанное нами приложение можно, запустив исполняющую среду Node.js, передав ей первым параметром путь к стартовому модулю этого приложения, а вторым — преобразуемую величину. 8. Сохраним модуль и проверим приложение в действии, набрав в терминале Visual Studio Code2 следующие команды: 1 Символ двойной кавычки: «"», присутствующий в коде и в составе вывода после исходной величины, — это общепринятое обозначение дюйма. 2 Здесь и далее под словом «терминал» подразумевается встроенная консоль Visual Studio Code (подробности — в разд. П2.8).
28 Часть I. Начала программирования на Node.js > node index.js 27 27" >>> 68.58 см. > node index.js 32 32" >>> 81.28 см. Внимание! Перед запуском приложения всегда следует сохранять все вновь созданные и исправленные модули. Автор больше не будет напоминать об этом! 9. Проверим обработку приложением нештатных ситуаций: > node index.js Укажите величину в дюймах > node index.js abc Укажите дюймы в виде числа Ура! Наше первое Node-приложение работает! Однако вывод у него какой-то скучный, одноцветный... Давайте раскрасим его в разные цвета. И используем для этого готовую дополнительную библиотеку. Однако предварительно нам придется сделать кое-какие действия... ПОЛЕЗНО ЗНАТЬ Перед исполнением кода любого модуля Node.js производит его преобразование (компиляцию) в команды, выполняемые непосредственно центральным процессором компьютера (машинный код), и сохраняет откомпилированный код в памяти. Это делается для повышения производительности. На этапе компиляции выявляются ошибки в самом программном коде (ошибки времени компиляции). Как только Node.js встретит в коде такую ошибку, она сразу прервет компиляцию и выведет в консоли соответствующее сообщение. Если весь код приложения хранится в одном модуле и не использует никаких дополнительных библиотек, этот модуль можно хранить в любой папке файловой системы, в том числе содержащей другие файлы, и запускать из этой папки, — на работоспособности приложения это никак не скажется. 1.3. Упражнение. Создание манифеста проекта Чтобы получить возможность устанавливать в проекте дополнительные библиотеки, нам придется создать манифест проекта. Теория Проект, созданный нами в упражнении 1.2, на самом деле не совсем полноценный. Полноценный проект Node-приложения должен содержать в своем составе манифест.
ЧАСТЬ II Веб-фреймворк Express Урок 3. Введение в Express Урок 4. Маршрутизация и контроллеры Урок 5. Шаблоны. Шаблонизатор EJS Урок 6. Получение данных, отправленных пользователем Урок 7. Посредники, статические файлы и обработка ошибок Урок 8. Валидация Урок 9. Cookie, серверные сессии и всплывающие сообщения Урок 10. Выгрузка файлов Урок 11. Разграничение доступа и промисификация
Урок 3 Введение в Express Разумеется, разрабатывать веб-приложения можно, применяя исключительно встроенные инструменты Node.js (что мы и делали на уроке 2). Однако тогда понадобится реализовывать самостоятельно маршрутизатор, обработку данных, полученных от пользователя в GET- и POST-параметрах, разбор заголовков полученного запроса и др. На это придется потратить много времени и написать много кода. Снизить трудоемкость программирования веб-приложения позволит применение какого-либо веб-фреймворка. Фреймворк1 — программная библиотека, реализующая основную функциональность, своего рода каркас разрабатываемого приложения. Веб-фреймворк — фреймворк для разработки веб-приложений. Express (он же Express.js) — наиболее популярный веб-фреймворк, созданный под Node.js. Он очень быстр, компактен и не навязывает разработчику никаких ограничений. Кроме того, это самый первый веб-фреймворк, предназначенный для работы с Node.js. Стартовому модулю веб-приложения, написанного с применением Express, сами разработчики фреймворка советуют давать имя app.js. Видимо, так они хотят подчеркнуть, что этот модуль запускает веб-приложение. 3.1. Упражнение. Простейшее веб-приложение на Express Чтобы познакомиться с этим замечательным фреймворком, напишем простейшее веб-приложение, при получении запроса с путем / (слеш) выводящее перечень запланированных дел. Базу данных дел будем загружать из файла data\todos.json и хранить в оперативной памяти — это упростит программирование. Для работы с ней напишем модель, код которой сохраним в модуле source\models\todos.js. Код пока что единственного контроллера сохраним в модуле source\controllers\ todos.js. 1 От англ. framework — каркас.
90 Часть II. Веб-фреймворк Express Номер TCP-порта, через который станет работать приложение, будем хранить в переменной окружения PORT. Реализуем в приложении поддержку файловых переменных окружения. Остальные детали уточним по ходу дела. 1. Создадим где-либо на диске нашего компьютера папку express-web, которая станет папкой проекта этого приложения. 2. Откроем эту папку в редакторе Visual Studio Code. 3. Создадим манифест проекта, пользуясь инструкциями из разд. 1.3. Укажем имя express-web-app, имя стартового модуля app.js, произвольные описание и набор ключевых слов. 4. Добавим в созданный манифест проекта указание на то, что будем использовать формат инструкций импорта ECMAScript (см. разд. 1.3). 5. Создадим в папке проекта вложенную папку data. 6. Найдем в папке lesson2\!sources сопровождающего книгу файлового архива (см. приложение 7) файл todos.json и скопируем его в только что созданную папку data. 7. Установим библиотеку dotenv (см. разд. 1.9). 8. Установим утилиту nodemon (инструкции — в разд. 2.4). 9. Добавим в манифест проекта указание для этой утилиты не отслеживать содержимое папки data (инструкции — в разд. 2.7). 10. Добавим в манифест проекта сценарий serve, запускающий nodemon (см. разд. 2.4). 11. Установим версию 4.18.2 веб-фреймворка Express, которую использовал автор, набрав в терминале команду: npm install express@^4.18.2 На этом подготовительные действия закончены. Можно начать непосредственно программирование приложения. 12. Создадим вложенную папку source — для хранения программного кода приложения. Чтобы открыть файл data\todos.json, в котором хранится база данных, нам понадобится вычислить полный путь к этому файлу. А чтобы вычислить его, потребуется получить полный путь к папке проекта. Этот путь нам будет нужен также на уроке 10, когда мы станем реализовывать удаление файлов с графическими иллюстрациями к делам. Чтобы не вычислять путь к папке проекта дважды в разных местах кода, вынесем определяющий его код в отдельный модуль source\utility.js. Путь сохраним в переменной currentDir, которую подвергнем множественному экспорту (благодаря чему впоследствии с легкостью сможем добавить в этот модуль код, объявляющий и экспортирующий другие сущности).
Урок 3. Введение в Express 91 13. Создадим в папке source модуль utility.js и запишем в него код из листинга 3.1. Листинг 3.1. Модуль служебного кода (source\utility.js) import { dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; const currentDir = dirname(dirname(fileURLToPath(import.meta.url))); export { currentDir }; Базу данных data\todos.json мы загрузим сразу при запуске приложения. Высокая производительность здесь не нужна, так что мы применим для этого синхронную функцию. К тому же ее использование несколько упростит программирование. В нашем приложении будут две модели: модель дел (которую напишем прямо сейчас) и модель пользователей (она будет написана на уроке 11). Обе модели станут работать с одной и той же базой данных. Следовательно, удобнее и логичнее вынести код, загружающий эту базу, в отдельный модуль, дав ему имя, например: source\models\__loaddatabase.js. Загруженная база данных будет сохранена в переменной database, подвергнутой экспорту по умолчанию. 14. Создадим в папке source вложенную папку models. 15. Создадим в папке source\models модуль __loaddatabase.js и занесем в него код из листинга 3.2. Листинг 3.2. Модуль, загружающий базу данных (source\models\__loaddatabase.js) import { join } from 'node:path'; import { readFileSync } from 'node:fs'; import { currentDir } from '../utility.js'; // ..................... 1 const dataFileName = join(currentDir, 'data', 'todos.json'); const dataFile = readFileSync(dataFileName, 'utf8'); // ............ 2 const database = JSON.parse(dataFile); // .......................... 3 export { database }; Полный путь к папке проекта, на основе которого мы вычислим путь к файлу базы данных, хранится в переменной currentDir, экспортированной из модуля source\utility.js. Не забываем импортировать эту переменную (поз. 1 в листинге 3.2). Как мы условились ранее, для загрузки файла используем синхронную функцию readFileSync() из встроенного модуля fs (поз. 2). Формат ее вызова такой же, как и у асинхронной функции readFile(), основанной на промисах (см. разд. 2.7).
92 Часть II. Веб-фреймворк Express Не забываем преобразовать загруженный из файла JSON-документ в объект JavaScript (поз. 3). Теперь напишем модель. Она предоставит функцию getList(), возвращающую перечень дел в виде массива. 16. Создадим в папке source\models модуль todos.js и запишем в него код из листинга 3.3. Листинг 3.3. Модуль модели запланированных дел (source\models\todos.js) import { database } from "./__loaddatabase.js"; const todos = database.todos; export function getList() { return todos; } Загруженную базу данных импортируем из модуля source\models\__loaddatabase.js. Сразу же присвоим массив дел, хранящийся в ее атрибуте todos, одноименной переменной — это немного упростит дальнейшее программирование. Займемся контроллером, выдающим страницу с перечнем дел. Он будет реализован в виде функции mainPage(). 17. Создадим в папке source вложенную папку controllers — для хранения кода контроллеров. 18. Создадим в папке source\controllers модуль todos.js и запишем в него код из листинга 3.4. Листинг 3.4. Модуль контроллеров, работающих с запланированными делами (source\controllers\todos.js) import { getList } from '../models/todos.js'; export function mainPage(req, res) { // ............................ 1 const list = getList(); // ..................................... 2 let s = '<!doctype html>' + '<html>' + ' <head>' + ' <meta charset="UTF-8">' + ' <title>Список запланированных дел</title>' + ' </head>' + ' <body>' + ' <h1>Запланированные дела</h1>';
Урок 4 Маршрутизация и контроллеры Маршрутизация — это определение, какой контроллер следует запустить, в зависимости от пути, полученного с клиентским запросом, и HTTP-метода, которым был выполнен запрос. Маршрутизация выполняется особой подсистемой приложения, называемой маршрутизатором. Контроллер — это подсистема приложения, формирующая заданный серверный ответ при получении клиентского запроса с определенным путем, выполненного определенным HTTP-методом. Маршрутизатор, встроенный во фреймворк Express, сам выполняет анализ маршрутов сообразно заданным нами правилам. Он также извлекает из полученных путей присутствующие в их составе значения (например, идентификаторы выводимых дел) и передает их контроллерам для обработки. 4.1. Маршрутизатор Express. Маршруты и URL-параметры 4.1.1. Встроенный маршрутизатор В простых проектах удобнее использовать встроенный маршрутизатор. Встроенный маршрутизатор — маршрутизатор, автоматически создаваемый при создании объекта приложения Express и являющийся его частью. Express также позволяет использовать произвольное количество выделенных маршрутизаторов, что пригодится при программировании сложных приложений. Выделенный маршрутизатор будет рассмотрен позже. 4.1.2. Маршруты Маршрут — это правило, связывающее заданные допустимый HTTP-метод и шаблонный путь с определенным контроллером. В приложении можно создать произвольное количество маршрутов. Допустимый HTTP-метод — HTTP-метод, записываемый в маршруте. Шаблонный путь — либо путь, либо шаблон пути, записываемый в маршруте. При получении клиентского запроса фреймворк извлекает из него HTTP-метод, которым был произведен этот запрос, и путь к запрашиваемому ресурсу и передает
Урок 4. Маршрутизация и контроллеры 105 их для анализа маршрутизатору. Тот перебирает список маршрутов, созданных разработчиком приложения, в том порядке, в котором они были созданы, и сравнивает HTTP-метод и путь, полученные с запросом, с допустимым HTTP-методом и шаблонным путем, записанными в каждом маршруте, пока не найдет первый совпавший маршрут. Совпавший маршрут — маршрут, в котором допустимый HTTP-метод и шаблонный путь совпали с HTTP-методом, которым был выполнен поступивший клиентский запрос, и путем, полученным в составе этого запроса. После чего маршрутизатор вызывает контроллер, записанный в совпавшем маршруте, а контроллер, в свою очередь, выдает серверный ответ. 4.1.2.1. Создание маршрутов Для создания маршрутов применяются методы, имена которых совпадают с именами различных HTTP-методов: get() — создает новый маршрут и заносит в него допустимый HTTP-метод GET. HTTP-метод GET предписывает приложению выдать ресурс; post() — создает новый маршрут с допустимым методом POST. HTTP-метод POST указывает приложению принять данные, отправленные пользователем в POST-параметрах, обработать их (например, сохранить на диске или в базе данных) и выдать ресурс с результатами их обработки (сообщение об успешном сохранении или возникшей ошибке); head() — создает новый маршрут с допустимым методом HEAD. HEAD — HTTP-метод, указывающий приложению выдать сведения о ресурсе. HTTP-методом HEAD клиент отсылает проверочный запрос, чтобы выяснить, актуальна ли еще кешированная копия ресурса (подробности — в разд. 20.1.1). Внимание! Если отсутствует маршрут с совпадающим шаблонным путем и допустимым HTTP-методом HEAD, будет использован маршрут с подходящим шаблонным путем и допустимым HTTP-методом GET. option() — создает маршрут с допустимым методом OPTION. OPTION — HTTP-метод, указывающий приложению выдать сведения о себе. HTTP-методом OPTION фронтенд отсылает проверочный запрос, чтобы узнать, поддерживает ли бэкенд межсайтовые запросы и, если поддерживает, какие ограничения накладывает на них (подробности — в разд. 17.5). Как правило, самостоятельно обрабатывать запросы, отправленные HTTP-методами HEAD и OPTION, приходится лишь в крайне редких случаях. Обычно разработчики используют дополнительные библиотеки, обрабатывающие такие запросы.
106 Часть II. Веб-фреймворк Express В случае использования встроенного маршрутизатора эти методы вызываются непосредственно у объекта приложения Express. Формат вызова всех этих методов одинаков: <метод>(<шаблонный путь>, <контроллеры>) Пример создания маршрута, связывающего допустимый HTTP-метод GET и шаблонный путь / (слеш, «корень» приложения) с контроллером mainPage(): const app = express(); app.get('/', mainPage); Пример создания маршрута, связывающего допустимый HTTP-метод POST и шаблонный путь /add с контроллером addTodoPage(): app.post('/add', addTodoPage); Для создания маршрутов также можно использовать два следующих, более специфических метода: route(<шаблонный путь>) — возвращает новый объект маршрутизатора, содержащий заданный шаблонный путь. Пользуясь этим объектом, можно создать произвольное количество маршрутов, имеющих одинаковый шаблонный путь, который берется из упомянутого ранее объекта маршрутизатора, и разные допустимые HTTP-методы. Маршруты создаются вызовами у полученного объекта тех же методов get(), post() и head(), только без указания в их вызовах шаблонного пути. Каждый из этих методов в качестве результата возвращает текущий объект маршрутизатора, поэтому их вызовы можно записывать цепочкой. Пример создания двух маршрутов с одинаковым шаблонным путем / и разными допустимыми HTTP-методами GET и POST: app.route('/') .get(mainPage) .post(addTodoPage); all() — создает маршрут и заносит в него в качестве допустимых все поддерживаемые HTTP-методы. Формат его вызова такой же, как и у методов get(), post() и head(). Пример: app.all('/', todoController); В коде контроллера, который связан с таким маршрутом, следует проверять, какой HTTP-метод был использован, и в зависимости от этого выполнять соответствующие действия и выдавать соответствующие ресурсы. Порядок записи выражений, создающих маршруты, может быть произвольным. 4.1.2.2. Написание шаблонных путей Шаблонный путь в вызове любого из методов, описанных в разд. 4.1.2.1, можно указать в виде:
Урок 4. Маршрутизация и контроллеры 107 строки — в начале пути должен стоять прямой слеш, а в конце он не ставится: app.get('/', mainPage); app.get('/others/about', aboutPage); Такой путь может содержать литералы следующего вида: • <символ>? — указанный символ может присутствовать, а может и не присутствовать: Шаблонный путь Примеры совпадающих путей '/addto?do' /addtdo и /addtodo • <символ>+ — указанный символ может присутстовать в произвольном количестве экземпляров: Шаблонный путь Примеры совпадающих путей '/addto+do' /addtodo, /addtoodo и /addtoooooodo Если требуется указать не один символ, а их последовательность, ее следует взять в круглые скобки: Шаблонный путь Примеры совпадающих путей '/add(todo)?' /add и /addtodo '/add(todo)+' /addtodo, /addtodotodo и /addtodotodotodo • * — обозначает любое количество любых символов, включая слеши: Шаблонный путь Примеры совпадающих путей '/add*todo' /addtodo, /addabctodo, /add123456789todo и /add/todo Шаблонный путь /* совпадает с любым из полученных путей; регулярного выражения JavaScript. В его начале также должен стоять прямой слеш, а в конце его быть не должно. Пример: Шаблонный путь Примеры совпадающих путей /\/add|create/ /add и /create массива, содержащего произвольное количество шаблонных путей, — такой маршрут станет считаться совпавшим при совпадении любого из этих шаблонных путей с полученным в запросе. Шаблонные пути задаются описанными ранее способами. Пример: Шаблонный путь Примеры совпадающих путей ['/add', 'create(todo)?', /\/new(todo)?/] /add, /create, /createtodo, /new и /newtodo
Урок 6 Получение данных, отправленных пользователем Практически все веб-приложения позволяют пользователям создавать новые интернет-ресурсы и делать их доступными для всех. Пользователю, желающему создать новый ресурс, достаточно ввести необходимые данные в веб-форму (например, заголовок и описание — для создания нового запланированного дела) и нажать кнопку отправки данных. Кроме того, приложения позволяют указывать разнообразные параметры, задающие формат и содержимое выдаваемых ресурсов. Эти параметры также задаются в веб-формах. В качестве примеров можно привести, например, искомое слово для поиска ресурсов и порядок их сортировки при выдаче в виде перечня. Данные, занесенные в веб-формы, отправляются веб-приложению в GET- или POST-параметрах. При этом в POST-параметрах обычно пересылаются данные, описывающие создаваемый ресурс и в основном обязательные к занесению (например, содержание запланированного дела), а в GET-параметрах — данные, задающие необязательные параметры выдаваемого ресурса (скажем, искомое слово для поиска). 6.1. Настройка приложения для получения данных 6.1.1. Настройка обработки GET-параметров Собственно обработка полученных GET-параметров задействована в Express изначально. Управлять ею можно, задавая различные значения параметра query parser приложения: 'extended' — будет использован обработчик GET-параметров по умолчанию («расширенный»). Он подходит практически для всех случаев; 'simple' — будет использован «простой» обработчик, применявшийся в старых версиях Express. В настоящее время не используется, однако может встретиться в старом коде;
Урок 6. Получение данных, отправленных пользователем 139 false — GET-параметры вообще не будут обрабатываться. Если приложение не должно получать никаких данных с GET-параметрами, их обработку можно отключить — это сэкономит немного системных ресурсов: app.set('query parser', false); Значение параметра по умолчанию: 'extended'. 6.1.2. Настройка обработки POST-параметров А вот обработка POST-параметров в Express изначально не выполняется. Чтобы ее задействовать, придется подключить к Express-приложению дополнительную программную подсистему. Эта подсистема реализована в виде функции urlencoded(), объявленной в библиотеке Express и подвергнутой множественному экспорту. Подключение подсистемы выполняется в два шага: 1. Вызывается функция urlencoded() — в следующем формате: urlencoded([<параметры подсистемы>]) Задаваемые параметры мы рассмотрим позже. Результат, возвращенный этой функцией, следует сохранить — он скоро понадобится. 2. Вызывается метод use() объекта приложения — и ему в качестве единственного параметра передается результат, возвращенный функцией urlencoded(). Пример: import express, { urlencoded } from 'express'; . . . const app = express(); app.use(urlencoded({ extended: true })); Параметры подсистемы, передаваемые функции urlencoded(), задаются в виде простого объекта, свойства которого соответствуют отдельным параметрам: extended — если true, для обработки POST-параметров будет использован новый, «расширенный», обработчик, если false или undefined — старый, «простой». Рекомендуется использовать новый обработчик, подходящий практически для всех случаев. Старый обработчик в настоящее время применяется только в старом коде. Значение по умолчанию: undefined. Внимание! Значение undefined параметра extended в текущей версии Express является устаревшим и не рекомендуется к применению. Предлагается явно дать этому параметру значение true или false.
140 Часть II. Веб-фреймворк Express parameterLimit — максимально допустимое количество POST-параметров в запросе, заданное в виде целого числа. Если запрос содержит больше POST-параметров, будет возбуждено исключение класса HttpError с кодом статуса 413 (количество передаваемых значений слишком велико). Значение по умолчанию: 1000. Класс исключения HttpError объявлен в библиотеке http-errors, которую мы рассмотрим на уроке 7; limit — максимально допустимый совокупный размер данных, передаваемых POST-параметрами. Может быть указан в виде: • целого числа — в байтах: app.use(urlencoded({ extended: true, limit: 1024 })); • строки, содержащей число и суффикс: b — в байтах: app.use(urlencoded({ extended: true, limit: '1024b' })); kb — в килобайтах: app.use(urlencoded({ extended: true, limit: '1kb' })); mb — в мегабайтах; gb — в гигабайтах. Если размер данных превышает заданный максимум, возбуждается исключение класса HttpError с кодом статуса 413. Значение по умолчанию: '100kb'. Ограничение допустимого количества POST-параметров и (или) максимального размера отправляемых данных позволит защитить приложение от ряда сетевых атак, когда злоумышленник отправляет данные слишком большого объема, чтобы перегрузить приложение и вызвать его крах. Метод use() также поддерживается объектом выделенного маршрутизатора: import { Router, urlencoded } from 'express'; . . . const router = Router(); router.use(urlencoded({ extended: true })); В этом случае подсистема, обрабатывающая POST-параметры, присоединяется к выделенному маршрутизатору. ПОЛЕЗНО ЗНАТЬ urlencoded() — это фабрика функций. Фабрика функций — функция, в качестве результата выдающая другую функцию, настроенную на выполнение определенной задачи в соответствии с параметрами, полученными фабрикой функций.
Урок 6. Получение данных, отправленных пользователем 141 Фабрика функций urlencoded() выдает посредник — подсистему приложения, обрабатывающую получаемые запросы и сгенерированные фреймворком ответы перед передачей их контроллерам. Подробнее посредники будут рассмотрены на уроке 7. 6.2. Получение отправленных данных Получить данные, отправленные пользователем, можно, обратившись к следующим свойствам объекта запроса: query — содержит данные, отправленные в GET-параметрах. Данные представляются в виде простого объекта, свойства которого соответствуют полученным GET-параметрам, имеют те же имена и хранят значения из этих GET-параметров. Пример: const search = req.query.search; Если в запросе отсутствуют GET-параметры или их обработка отключена (присваиванием параметру query parser приложения значения false), свойство хранит пустой объект; body — содержит данные, отправленные в POST-параметрах, представленные аналогичным объектом: const newTodo = { title: req.body.title, desc: req.body.desc }; Если в запросе отсутствуют POST-параметры, свойство хранит пустой объект. Если обработка POST-параметров не выполняется (не была задействована подсистема urlencoded()), свойство хранит значение undefined. 6.3. Упражнение. Поиск дел Дадим пользователю возможность выполнять поиск дел — получать перечень только тех дел, которые удовлетворяют заданным условиям1 . Таким условием будет наличие в заголовке или в описании дела заданного искомого слова. Для ввода искомого слова поместим в верхней части главной страницы, над заголовком, веб-форму. Введенное слово будем пересылать приложению в GETпараметре с именем search. Поиск реализуем без учета регистра символов. Начнем с шаблона главной страницы. Сделаем так, чтобы в поле ввода искомого слова подставлялось заданное ранее слово, извлеченное из GET-параметра search. 1 Вообще-то, процесс получения перечня лишь тех позиций, что удовлетворяют заданным условиям, называют поиском лишь в силу традиции. Корректнее было бы назвать его фильтрацией. Поиск же — это выделение каким-либо образом искомой позиции в уже выведенном перечне. Но не будем нарушать сложившуюся традицию…
ЧАСТЬ III СУБД MongoDB. Модели Mongoose Урок 12. СУБД MongoDB Урок 13. Mongoose: объектные модели и их написание Урок 14. Mongoose: запись данных Урок 15. Mongoose: выборка данных Урок 16. Mongoose: агрегатные вычисления и расширение моделей
Урок 12 СУБД MongoDB До сих пор наше веб-приложение списка дел использовало «самодельную» базу данных, представляющую собой обычный JSON-файл. Нам пришлось самостоятельно реализовывать загрузку базы при запуске приложения (причем она загружалась в память целиком), извлечение пользователя с заданным именем или дела с заданным идентификатором, поиск дел, содержащих введенное искомое слово, сортировку дел и сохранение базы на диске после каждого сделанного в ней изменения. В результате мы написали довольно большой и сложный код. Три недостатка подхода «сделай все сам» очевидны: чрезвычайно сложное программирование, повышенный расход оперативной памяти (поскольку база целиком хранится там) и падение производительности при внесении изменений в базу (поскольку после каждого изменения ее нужно преобразовать в формат JSON и сохранить в файле, а это процедура небыстрая). Еще есть четвертый недостаток — возможность повреждения базы при сохранении в случае останова или аварийного завершения приложения. Такой подход оправдан разве что при программировании приложений с небольшой нагрузкой и малым объемом хранимых внутренних данных. 12.1. Серверные СУБД и их преимущества перед «самоделками» Если же разрабатывается высоконагруженное приложение, обслуживающее большое количество пользователей и манипулирующее большими объемами данных, то базу, хранящую эти данные, следует «поручить заботам» отдельной программы — СУБД. И лучше, если это будет серверная СУБД. СУБД (система управления базами данных) — комплекс программ, служащих для создания баз данных, их ведения и совместного использования многими пользователями. Тогда, если пользователю понадобится дело с идентификатором 64662ddd80335484 e6602f07, он не станет самостоятельно просматривать хранящийся в базе массив дел в поисках нужного, а просто отдаст СУБД указание вида: «дай мне дело с идентификатором 64662ddd80335484e6602f07». Получив его, СУБД «за кулисами» примется считывать из файлов базу данных часть за частью (чтобы сэкономить память) и искать в очередной загруженной части затребованное дело (максимально быстро
266 Часть III. СУБД MongoDB. Модели Mongoose и оптимально), после чего отправит найденное дело пользователю или сообщит ему, что затребованного дела в базе нет. Аналогично если пользователю нужен зарегистрированный пользователь, он подаст СУБД команду вида: «а подать сюда Ляпкина-Тяпкина», после чего ему останется лишь дождаться результата. А для получения перечня дел, содержащих слово «Node.js», он прикажет: «выдай мне все дела, у которых заголовок или описание содержат слово “Node.js”». Преимущества полноценных СУБД перед «самоделками»: радикальное упрощение программирования — разработчику не придется самостоятельно писать код для занесения данных в базу и извлечения их оттуда, поскольку все это уже реализовано в СУБД; повышение производительности — СУБД написаны таким образом, чтобы выполнять запрашиваемые пользователем действия максимально быстро и эффективно. Серверная СУБД — СУБД, представляющая собой программу-сервер. Для работы с такой СУБД требуется какая-либо программа-клиент. Клиентом серверной СУБД может выступать как специализированная программа для работы с базами данных, так и веб-приложение, использующее данные, которые хранятся в базе. Инфраструктура серверных СУБД функционирует по тем же принципам, что и WWW. Клиентские программы отправляют нужной базе данных клиентский запрос на выполнение определенной операции: поиска документа, выборки документов, удовлетворяющих заданным условиям, добавления нового документа и др. А серверная СУБД после выполнения операции посылает клиенту серверный ответ, содержащий затребованные пользователем данные, оповещение об успешном или неуспешном добавлении документа и др. Серверные СУБД имеют дополнительные преимущества: еще более значительное повышение быстродействия — поскольку серверная СУБД зачастую работает на отдельном серверном хосте; повышенная безопасность данных — серверные СУБД практически всегда реализуют разграничение доступа. 12.2. Введение в MongoDB В новой редакции приложения для хранения внутренних данных мы используем базу, обслуживаемую серверной СУБД MongoDB. Это исключительно мощная СУБД, которая хранит данные в форматах, применяемых в JavaScript, вследствие чего прекрасно интегрируется с Node.js. А еще это негласный стандарт в Nodeпрограммировании: если вы видите веб-приложение, написанное под Node.js, то можете быть уверены, что внутренние данные этого приложения хранятся в базе данных MongoDB.
Урок 12. СУБД MongoDB 267 12.2.1. Базы данных, коллекции и документы На всякий случай вспомним определение базы данных, приведенное в разд. 2.7. База данных — хранилище какой-либо информации, взаимосвязанной и организованной определенным образом. Сервер MongoDB может обслуживать произвольное количество баз данных. Каждая база данных должна иметь уникальное имя. База данных MongoDB содержит произвольное количество коллекций, а каждая из коллекций — произвольное количество документов. Документ — базовая единица информации, которой манипулирует MongoDB. Представляет собой произвольный набор атрибутов. Атрибут — значение, хранящееся в документе. Должен иметь уникальное в пределах документа имя. Документ MongoDB можно рассматривать как полный аналог документа JSON (см. разд. 1.4.2) или подобие простого объекта JavaScript (только в нем в качестве атрибутов выступают свойства). Структура документов не фиксирована. Каждый документ может содержать собственный набор атрибутов. Количество атрибутов в документе не ограничено. Предельно допустимый объем документа — 16 Мбайт. Коллекция — массив документов, имеющих что-либо общее друг с другом. Должна иметь уникальное в пределах базы данных имя. Например, коллекция users может хранить документы, описывающие зарегистрированных пользователей приложения, а коллекция todos — запланированные дела. Коллекцию MongoDB можно рассматривать как обычный массив JavaScript. Количество коллекций в базе данных MongoDB не ограничено. Объектный идентификатор — уникальная неизменяемая пометка, записываемая в каждый создаваемый документ и однозначно его идентифицирующая. Представляет собой объект класса ObjectId и хранится в атрибуте _id документа. Пример базы данных MongoDB схематично показан на рис. 12.1 (объектные идентификаторы приведены в сокращенном написании и в виде строк — для компактности). ПОЛЕЗНО ЗНАТЬ Для генерирования объектного идентификатора применяется функция, аналогичная функции getObjectId(), которую мы написали в разд. 6.5. Объектный идентификатор представляет собой комбинацию временно´й отметки его создания, псевдослучайного числа и трехзначного порядкового номера, что гарантирует его уникальность.
268 Часть III. СУБД MongoDB. Модели Mongoose Рис. 12.1. Пример базы данных MongoDB Базы данных MongoDB хранятся не в JSON, как наша «самоделка», а в специальном формате, компактном и оптимизированным для максимально быстрого выполнения операций. MongoDB относится к документным СУБД, поскольку хранит информацию в виде наборов документов произвольной структуры. Существуют другие разновидности СУБД. Так, популярные в настоящее время реляционные СУБД хранят информацию в виде набора таблиц, имеющих фиксированную структуру. 12.2.2. Типы данных, поддерживаемые MongoDB Атрибуты документа MongoDB могут хранить данные следующих типов: строки; числа; логические величины; временны´ е отметки — объекты класса Data, поддерживаемого JavaScript; двоичные данные — массивы байтов любого из типов, поддерживаемых JavaScript (например, объекты класса Buffer); объектные идентификаторы — объекты класса ObjectId; null; поддокументы произвольной структуры;
Урок 12. СУБД MongoDB 269 массивы произвольных значений, принадлежащих любым из поддерживаемых типов. Следующие три типа применяются довольно редко, в особых случаях и при выдаче документов преобразуются в обычные числа JavaScript: 32-разрядные целые числа; 64-разрядные целые числа; вещественные числа с фиксированным количеством цифр после запятой. Также поддерживается еще несколько типов, являющихся крайне специфическими и редко применяемыми. 12.2.3. Встраивание и связывание документов Часто бывает необходимо сохранить в базе данных какой-либо документ и набор других документов, подчиненных ему. Структурой такого рода является, например, документ со сведениями о зарегистрированном пользователе и набор связанных с ним документов, описывающих запланированные дела, которые создал этот пользователь. Первичный документ (в упомянутой структуре) — документ, которому подчинены другие документы. Вторичный документ (в упомянутой структуре) — документ, подчиненный другому документу (являющемуся первичным). Существуют два подхода в реализации такого рода связей между документами. 12.2.3.1. Встраивание вторичных документов в первичный Встраивание — хранение совокупности вторичных документов непосредственно в первичном документе в виде массива, присвоенного какому-либо атрибуту. Вот представленный в виде JSON-кода пример коллекции, в которой реализовано встраивание (массивы дел хранятся в атрибутах todos каждого документа пользователя): [ { "_id": "2111", "name": "ivanov", "todos": [ { "_id": "1111", "title": "Изучить Node.js", "desc": "Это главное" }, { "_id": "1112",
270 Часть III. СУБД MongoDB. Модели Mongoose "title": "Изучить Express", "desc": "Он необходим" }, { "_id": "1113", "title": "Изучить редактор", "desc": "VSC" } ] }, { "_id": "2112", "name": "petrov", "todos": [ { "_id": "1114", "title": "Изучить React", "desc": "Клиентский веб-фреймворк" } ] }, ] Существенное преимущество у встраивания лишь одно: при извлечении первичного документа сразу же извлекается и массив подчиненных ему вторичных документов. Однако встраивание имеет и существенные недостатки: увеличение объема первичного документа при росте количества подчиненных ему вторичных документов. В какой-то момент объем первичного документа может превысить установленный предел в 16 Мбайт, и СУБД откажется сохранять его, выдав ошибку. Кроме того, первичный документ большого объема дольше передается по сети; добавление, правка или удаление хотя бы одного вторичного документа приводит к изменению первичного документа и соответственно к возникновению необходимости его сохранения в базе данных. А если первичный документ имеет большой объем, сохранение может отнять много времени и тем самым снизить производительность; проблемы с обработкой всей совокупности вторичных документов, хранящихся в базе. Например, если нам понадобится получить количество всех дел, созданных всеми пользователями, придется просмотреть все документы, хранящие сведения о пользователях, у каждого документа получить количество подчиненных ему документов-дел, после чего сложить все полученные значения количеств.