вдеис
Алекс Янг
Брэдли Мек
Майк Кантелон
А также
Тим Оксли
Марк Хартер
Т. Дж. Головайчук
Натан Райлих
I I H A N N IN G ПИТЕР
Node.js in Action
S e c o n d E d it io n
ALEXYOUNG
BRADLEY MECK
MIKE CANTELON
WITH
TIM OXLEY
MARC HARTER
T.J. HOLOWAYCHUK
NATHAN RAJLICH
■1
MANNI NG
S helter Islan d
Алекс Янг, Брэдли Мек, Майк Кантелон
А также Тим Оксли, М арк Хартер, Т. Дж. Головайчук, Натан Райлих
в действии
2-е издание
ББК 32.988-02-018
УДК 004.738.5
Права на издание получены по соглашению с Manning. Все права защищены. Никакая часть данной книги не
может быть воспроизведена в какой бы то ни было форме без письменного разрешения владельцев авторских
прав.
Информация, содержащаяся в данной книге, получена из источников, рассматриваемых издательством как на
дежные. Тем не менее, имея в виду возможные человеческие или технические ошибки, издательство не может
гарантировать абсолютную точность и полноту приводимых сведений и не несет ответственности за возможные
ошибки, связанные с использованием книги.
ISBN 978-1617292576 англ. © 2017 by Manning Publications Co. All rights reserved
978-5-496-03212-4 © Перевод на русский язык ООО Издательство «Питер», 2018
© Издание на русском языке, оформление ООО Издательство «Питер», 2018
© Серия «Для профессионалов», 2018
Краткое содержание
Приложения............................................................................................. 359
A. Установка Node ............................................................................................................... 360
Б. Автоматизированное извлечение веб-данны х........................................................365
B. Официально поддерживаемые промежуточные ком пон ен ты .......................... 378
Глоссарий....................................................................................................................... 418
Оглавление
Предисловие ....................................................................................................................15
Благодарности ................................................................................................................ 16
О книге.............................................................................................................................. 17
Структура ......................................................................................................................... 17
Правила оформления и загрузка примеров ко д а................................................... 18
Об авторах....................................................................................................................... 19
Алекс Янг ...................................................................................................................19
Брэдли Мек ...............................................................................................................19
Иллюстрация на обложке ........................................................................................... 20
От издательства ............................................................................................................. 20
Приложения............................................................................................. 359
Приложение А. Установка N o d e ..............................................................................360
А.1. Установка Node с использованием программы установки ........................360
А.1.1. Программа установки для m a c O S ........................................................ 360
А.1.2. Программа установки для W in d o w s.................................................... 362
A.2. Другие способы установки N o d e ...................................................................... 363
A.2.1. Установка Node из исходного кода .................................................... 363
A.2.2. Установка Node из менеджера п акетов................................................ 363
Приложение Б . Автоматизированное извлечение веб-данных ...................... 365
Б.1. Извлечение веб-данных........................................................................................365
Б.1.1. Применение извлечения веб-данных ................................................ 366
Б.1.2. Необходимые инструм енты ....................................................................367
Б.2. Простейшее извлечение веб-данных с использованием cheerio...............368
Б.3. Обработка динамического контента с jsd o m ..................................................371
Б.4. Обработка «сырых» д ан н ы х ............................................................................... 374
Б.5. Заключение ............................................................................................................ 377
Приложение В . Официально поддерживаемые промежуточные
компоненты 378
B.1. Разбор cookie, тел запросов и строк информационных зап росов............378
B.1.1. cookie-parser: разбор H T T P-cookie........................................................ 379
В.1.2. Разбор строк запросов ............................................................................383
В.1.3. body-parser: разбор тел запросов............................................................384
В.1.4. Сжатие ответов........................................................................................... 391
В.2. Реализация базовых функций веб-приложения ......................................... 393
В.2.1. morgan: ведение журнала запросов .................................................... 393
В.2.2. serve-favicon: значки адресной строки и закладки .........................397
В.2.3. m ethod-override — имитация методов H T T P .................................... 398
В.2.4. vhost: виртуальный хости н г....................................................................401
В.2.5. express-session: управление сеансами ................................................ 402
В.3. Безопасность веб-приложений ......................................................................... 407
В.3.1. basic-auth: базовая H T T P -аутентификация .................................... 408
В.3.2. csurf: защита от атак CSRF ....................................................................410
В.3.3. errorhandler: — обработка ошибок при разработке........................... 412
В.4. Предоставление статических файлов ..............................................................414
В.4.1. serve-static — автоматическое предоставление статических
файлов браузеру ................................................................................................... 414
В.4.2. serve-index: генерирование списков содержимого каталогов........416
Глоссарий 418
Предисловие
П ервое издание книги «Node.js в действии» было посвящ ено разработке веб
приложений, при этом особое внимание уделялось веб-фреймворкам C onnect
и Express. Второе издание «Node.js в действии» было переработано в соответствии
с изменившимися требованиями в области разработки Node. Вы узнаете о системах
построения интерфейса и популярных веб-фреймворках Node, а также научитесь
строить веб-приложения на базе Express с нуля. Также в книге рассказано о том,
как строить автоматизированные тесты и развертывать веб-приложения Node.
Технология Node все чаще используется в сочетании с инструментами командной
строки и настольными прилож ениями на базе Electron, поэтому в книгу были
включены главы, посвященные обеим областям.
Предполагается, что читатель знаком с основными концепциями программирования.
В первой главе приведен краткий обзор JavaScript и ES2015 для читателей, которые
только начинают вникать в тонкости современного JavaScript.
Структура
Книга состоит из трех частей.
В части I рассматриваются основы Node.js и фундаментальные методики, исполь
зуемые для разработки приложений на этой платформе. В главе 1 описываются
характеристики JavaScript и Node, с приведением примеров кода. Глава 2 проведет
вас поэтапно через фундаментальные концепции программирования Node.js. Глава 3
представляет собой полное руководство по построению веб-приложений с нуля.
Часть II — самая большая — посвящ ена разработке веб-приложений. В главе 4
рассеиваются некоторые заблуждения по поводу frond-end систем сборки; если
вы когда-либо использовали webpack или Gulp в проекте, но не понимали эти тех
нологии в полной мере, эта глава написана для вас. В главе 5 описаны некоторые
популярные фреймворки стороны сервера для Node, а в главе 6 технологии Connect
и Express рассматриваются более подробно.
Глава 7 посвящена языкам сценариев, которые могут значительно повысить эф
фективность вашей работы при написании кода на стороне сервера. Большинству
веб-приложений нужна база данных, поэтому в главе 8 рассматриваются многие
18 О книге
Алекс Янг
Алекс — веб-разработчик, ж ивущ ий в Лондоне; автор книги Node.js in Practice
(M anning, 2014). Алекс ведет популярный блог DailyJS по тематике JavaScript.
В настоящее время работает на Sky старшим разработчиком для NOW TV. Он есть
на G itH ub ( https://github.com/alexyoung) и в Twitter (@alex_young).
Брэдли Мек
Брэдли — участник TC39 и Node.js Foundation. В свободное время он разрабаты
вает инструментарий JavaScript, занимается садоводством и преподает. До работы
в G oD addy он долго использовал Node.js на благо других компаний, таких как
NodeSource и Nodejitsu. Всегда готовый обучать и объяснять, он старается под
держивать у людей мотивацию, потому что учиться для него так же сложно, как
и для всех остальных.
Иллюстрация на обложке
От издательства
Ваши замечания, предложения, вопросы отправляйте по адресу comp@piter.com
(издательство «Питер», компьютерная редакция).
Мы будем рады узнать ваше мнение!
На веб-сайте издательства www.piter.com вы найдете подробную информацию о на
ших книгах.
Знакомство с Node
Node.js — асинхронная уп равляем ая собы тиям и исп олни тельная платф орм а
JavaScript с мощной, но компактной стандартной библиотекой. Ее сопровождением
и поддержкой занимается Node.js Foundation — отраслевой консорциум с откры
той моделью управления. Существует две активно поддерживаемые версии Node:
текущ ая (C u rren t) и пользующаяся долгосрочной поддержкой (LTS, Long Term
Support). Если вы захотите больше узнать о том, как осуществляется управление
Node, на официальном веб-сайте имеется достаточно подробная документация
(https://nodejs.org).
С момента появления Node.js в 2009 году язык JavaScript прошел долгий путь от еле
сносного браузерного язы ка до одного из важнейших языков во всех областях раз
работки программного обеспечения. Отчасти это изменение связано с появлением
спецификации ECM AScript 2015, устранившей ряд важных недостатков в преды
дущих версиях языка. Node использует JavaS cript-ядро Google V8, основанное на
шестой версии стандарта ECM AScript (иногда она называется ES6 и обозначается
сокращением ES2015). Также на ситуацию повлияли такие инновационные техно
логии, как Node, React и Electron; они позволяют применять JavaScript буквально
повсеместно: от сервера до браузера и в платформенных мобильных приложениях.
Самые крупные компании постепенно принимают JavaScript, а компания Microsoft
даже внесла свой вклад в успех Node.
В этой главе мы расскаж ем о технологии Node, о ее неблокирую щ ей модели,
управляемой событиями, а также о некоторых факторах, благодаря которым язык
JavaScript стал отличным языком программирования общего назначения. Д ля на
чала давайте рассмотрим типичное веб-приложение Node.
Ответ Браузер
SQL INSERT
Регистрация
пользователя
Неблокирующий
сетевой
Квитанция
по электронной ввод/вывод
почте Загрузка шаблона
вызывается с диска
next
(error) Ответ
HTTP
Обработчик
ошибки Квитанция
Объект ошибки по электронной
JavaScript почте
libuv
Обращения к диску происходят примерно так же, но, как ни странно, полного совпа
дения нет. Когда приложение генерирует квитанцию, отправляемую по электронной
почте, и шаблон сообщения читается с диска, libuv использует пул потоков для
создания иллю зии использования неблокирующего вызова. Управление пулом
потоков — довольно тяжелое дело, но понять команду e m a il.s e n d ('te m p la te .e js ',
( e r r , html) => {}) определенно намного проще.
Истинное преимущество использования асинхронных A PI с неблокирующими
операциями ввода/вывода заключается в том, что Node может заниматься други
ми делами во время выполнения относительно медленных процессов. И хотя вы
полняться может только однопоточное и однопроцессное веб-приложение Node,
в любой момент времени оно может обрабатывать сразу несколько подключений
от тысяч потенциальных посетителей сайта. Чтобы понять, как это происходит,
необходимо познакомиться с циклом событий.
Выполнение таймеров
Активизация Отправка
незавершенных Функция: данных
Создание
обратных вызовов отправка/ JSON
пользователя
Опрос ввода/вывода заказ браузеру
База данных
Код получается более компактным и лучше читается. Но это еще не все: Node также
поддерживает субклассирование, su p er и статические методы. Д ля пользователей
с опытом работы на других языках поддержка синтаксиса классов делает техноло
гию Node более доступной по сравнению с временами, когда мы были ограничены
возможностями ES5.
Другая важная возможность Node 4 и выше — добавление конструкций const и le t.
В ES5 все переменные создавались с ключевым словом var. Проблема в том, что
var определяет переменные в области видимости функции или глобальной области
видимости, поэтому вы не могли определить переменную уровня блока в команде
i f , f o r или в другом блоке.
Когда вы колеблетесь между const и let, почти всегда следует сделать выбор
в пользу const. Так как в большей части вашего кода будут использоваться
экземпляры ваших собственных классов, объектные литералы или значе
ния, которые не должны изменяться, почти во всех случаях уместно const.
Даже экземпляры объектов с изменяемыми свойствами могут объявляться
с ключевым словом const — ведь const означает лишь то, что ссылка доступна
только для чтения, а не неизменность самого значения.
В Node такж е поддерж иваю тся обещ ания (prom ises) и генераторы. Обещания
поддерживаются многими библиотеками, что позволяет писать асинхронный код
в стиле динамических интерфейсов (fluent interface). Вероятно, вы уже знакомы
с динамическими интерфейсами: их видел каждый, кто когда-либо использовал
такие API, как jQuery, или хотя бы массивы JavaScript. Следующий короткий при
мер демонстрирует применение сцепленных вызовов для манипуляций с массивами
в JavaScript:
[1, 2, 3]
.map(n => n * 2)
.f ilte r ( n => n > 3);
Koa (h ttp ://ko a js.com /). При использовании обещаний или других генераторов
с Koa вы можете прибегнуть к y ie ld со значениями вместо вложения обратных
вызовов.
Еще одна полезная возможность ES2015 в Node — шаблонные строки. В ES5 стро
ковые литералы не поддерживали интерполяцию или деление на несколько строк.
Теперь при использовании символа обратного апострофа ( ' ) можно вставлять
значения и использовать разбиения по строкам. Д анная возможность особенно
полезна при вставке коротких фрагментов H TM L в веб-приложениях:
this.body = '
<div>
<h1>Hello from Node</h1>
<p>Welcome, ${user.name}!</p>
</div>
В Node обычно используются два аргумента, потому что первым аргументом функ
ции обратного вызова часто является объект ошибки. В этом случае аргументы
должны заключаться в круглые скобки:
Если тело функции должно содержать более одной строки, необходимо использовать
фигурные скобки. Полезность стрелочных функций не ограничивается упрощением
синтаксиса; она также имеет отношение к областям видимости JavaScript. В ES5
и ранее при определении функций внутри других функций ссылка t h i s становится
1.2. ES2015, Node и V8 29
1.2.1. Node и V8
работы Node. В разделе 1.1.1 был упомянут еще один из встроенных компонентов
Node —libuv. В этой части речь идет о вводе/выводе; V8 обеспечивает интерпретацию
и выполнение кода JavaScript. Чтобы использовать libuv с V8, следует применить
связующую прослойку C++. На рис. 1.3 изображены все программные компоненты,
из которых состоит Node.
Операционная система
ПРИМЕЧАНИЕ
Дополнительная информация об установке Node приведена в приложении А.
1.4.1. npm
Чтобы запустить программу командной строки npm, введите команду npm. П ро
грамма может использоваться для установки пакетов из центрального реестра npm,
но также и для поиска и организации совместного использования ваших проектов
с открытым или закрытым кодом. У каждого пакета npm в реестре существует сайт,
на котором хранится Readme-файл, информация об авторе и статистика о загрузках.
Впрочем, это описание нельзя назвать исчерпывающим. Программой npm занимает
ся npm, Inc. — компания, обеспечивающая работу сервиса npm и предоставляющая
услуги коммерческим организациям. Сюда входит хостинг приватных пакетов
npm; вы также можете вносить ежемесячную плату за хостинг исходного кода,
принадлежащего вашей компании, чтобы разработчики JavaS cript могли легко
устанавливать его с использованием npm.
При установке пакетов командой npm i n s t a l l вам придется решить, хотите ли вы до
бавить их в свой текущий проект или установить их глобально. Глобальная установка
пакетов обычно используется для служебных программ —чаще всего это программы,
запускаемые из командной строки. Хорошим примером служит пакет gulp-cli.
Чтобы использовать npm, создайте файл package.json в каталоге, содержащем проект
Node. Проще всего поручить создание файла package.json менеджеру npm.
Введите следующую команду в командной строке:
mkdir example-project
cd example-project
npm in it -y
Если теперь открыть файл package.json, вы увидите, что в свойстве dependencies до
бавился пакет express. Кроме того, в папке node_modules появился каталог express. Он
содержит только что установленную версию Express. Вы также можете установить
модули глобально с ключом - -g lo b al. Старайтесь по возможности использовать
локальные модули, но глобальные модули могут быть полезны для инструментов
командной строки, которые должны использоваться за пределами JavaS cript-кода
Node. В качестве примера средства командной строки, устанавливаемого из npm,
можно привести ESLint (http://eslint.org/).
Начиная работать с Node, вы часто будете пользоваться пакетами из npm. Node
поставляется с множеством полезных встроенных библиотек, которые обычно на
зываются базовыми модулями. Рассмотрим их более подробно.
34 Глава 1. Знакомство с Node.js
Файловая система
В поставку Node включается библиотека файловой системы (fs, path), клиенты
и серверы T C P (n et), поддержка H T T P (h ttp и h ttp s) и разреш ения доменных
имен (dns). Также имеется полезная библиотека, которая используется в основном
для написания тестов (assert), и библиотека операционной системы для запроса
информации о платформе (os).
Node также содержит ряд уникальны х библиотек. М одуль events — небольшая
библиотека для работы с событиями — используется в качестве основы для многих
API Node. Например, модуль stream использует модуль events для предоставления
абстрактных интерфейсов для работы с потоками данных. Поскольку все потоки
данных в Node используют одни и те же API, вы можете легко составлять программ
ные компоненты; если у вас имеется объект чтения файлового потока, вы можете
направить его через преобразование zlib, которое выполняет сжатие данных, а затем
через объект записи в файловый поток для записи данных в файл.
В следующем листинге продемонстрировано использование модуля Node fs для
создания потоков чтения и записи, которые могут направляться через другой поток
(gzip) для преобразования данных — в данном случае их сжатия.
Сетевые операции
Существует распространенное мнение, что создание простого сервера H T T P было
настоящим примером программы «Hello World» для Node. Чтобы построить сервер
на базе Node, необходимо загрузить модуль http и передать ему функцию. Функция
1.4. Встроенные средства Node 35
получает два аргумента: входной запрос и выходной ответ. В листинге 1.2 приведен
пример, который вы можете выполнить в терминале.
Листинг 1.2. Программа «Hello World» с использованием модуля http для Node
const http = re q u ire ('h ttp ');
const port = 8080;
1.4.3. Отладчик
В поставку Node вклю чается отладчик с поддержкой пошагового выполнения
и R EPL (R ead-E val-P rint Loop). Работа отладчика основана на взаимодействии
с вашей программой по сетевому протоколу. Чтобы запустить программу в отлад
чике, укажите аргумент debug в командной строке.
Предположим, вы отлаживаете код из листинга 1.2:
node debug h ello .js
ИНТЕРАКТИВНАЯ ОТЛАДКА
1.5.1. Веб-приложения
Технология Node предназначена для работы с JavaScript на стороне сервера, по
этому ее выбор в качестве платформы для построения веб-приложений выглядит
логично. Выполнение JavaScript на стороне клиента и сервера открывает возмож
ности для повторного использования кода в разных средах. Веб-приложения Node
обычно пишутся с применением таких фреймворков, как Express (http://expressjs.
com/). В главе 6 рассматриваются основные серверные фреймворки, доступные для
Node. Глава 7 посвящена Express и Connect, а темой главы 8 станет шаблонизация
веб-приложений.
Чтобы создать простейшее веб-приложение Express, создайте новый каталог и уста
новите модуль Express:
mkdir hello_express
cd hello_express
npm in it -y
npm i express --save
программы. Программы Node могут запускаться при помощи cron или работать
в фоновом режиме как демоны.
Если все эти термины вам незнакомы, не беспокойтесь: глава 11 познакомит вас
с написанием программ командной строки, и вы узнаете, как программы этого
типа помогают Node проявить свои сильные стороны. Например, в программах
командной строки широко используются потоки данных (stream s) как универсаль
ный API, а потоки также относятся к числу самых полезных возможностей Node.
1.6. Заключение
О Node — неблокирующая, управляемая событиями платформа для построения
приложений JavaScript.
О В качестве исполнительной среды JavaScript используется ядро V8.
О Библиотека libuv обеспечивает быстрый кросс-платформенный неблокирующий
ввод/вывод.
О Node содержит небольшую стандартную библиотеку — базовые модули, которые
добавляют в JavaScript поддержку сетевого и дискового ввода/вывода.
О В поставку Node входит отладчик и менеджер зависимостей (npm).
О Node используется для построения веб-приложений, средств командной строки
и даже настольных приложений.
Основы программирования
Node
Служебные функции
Команды 1 Команды
index.js lib/commands.js
__________________________ L.
/ /
Весь код в одном файле Взаимосвязанная логика
группируется в разных файлах
J
Логика модуля заполняет
module.exports или exports
Рис. 2.2. Заполнение свойства module.exports или объекта exports позволяет модулю
выбрать, какая информация должна быть доступна приложению
Ф лаг -y означает подтверждение (yes). Таким образом, npm создаст файл package.
json со значениями по умолчанию. Если вы хотите полностью управлять процессом
создания проекта, опустите флаг -y; npm задаст ряд вопросов о лицензии проекта,
имени автора и т. д. После того как все будет сделано, просмотрите содержимое
файла package.json. Вы можете отредактировать его вручную, но помните: файл
должен содержать корректную разметку JSON.
Пустой проект успешно создан, теперь можно переходить к созданию модуля.
Рис. 2.3. Модули Node могут создаваться либо в виде файлов (пример 1),
либо в виде каталогов (пример 2)
Обратите внимание: задаются только два свойства объекта exports. Таким обра
зом, для приложения, включающего модуль, будут доступны только две функции:
canadianToUS и USToCanadian. Переменная can ad ian D o llar закрыта от внешнего
доступа: она влияет на логику работы canadianToUS и USToCanadian, но приложение
не сможет обратиться к ней напрямую.
Ч тобы использовать новы й модуль в программе, восп ользуй тесь ф ункцией
Node re q u ire и передайте путь к нужному модулю в аргументе. Node выполняет
синхронны й поиск м одуля и загруж ает его содержимое. С начала N ode ищет
файлы среди базовых модулей, затем в текущем каталоге, и наконец, в каталоге
node modules.
Рис. 2.4. Если аргумент require начинается с ./, Node проверяет тот каталог,
в котором находится выполняемый файл
После того как Node найдет и обработает ваш модуль, функция req u ire возвращает
содержимое объекта e x p o rts, определенного в модуле. После этого вы сможете
использовать две функции, возвращаемые модулем, для пересчета сумм в другую
валюту.
Если вы захотите определить структуру модулей, разместите модули в подкаталогах.
Если, например, вы хотите хранить модуль currency в папке lib/, приведите строку
re q u ire к следующему виду:
const currency = re q u ire ('./lib /c u rre n c y ');
Возвращение функции из re q u ire (вместо объекта) сделает ваш код более элегант
ным — если это единственное, что требуется от модуля.
Казалось бы, чтобы создать модуль, возвращающий одну переменную или ф унк
цию, нужно присвоить exports то, что вы хотите вернуть. Однако такое решение
не сработает, потому что Node не ожидает, что exports будет присваиваться любой
другой объект, функция или переменная. Код модуля в следующем листинге пы
тается присвоить exports функцию (листинг 2.3).
Чтобы приведенный код модуля работал так, как ожидается, нужно заменить exports
на module .exports. Механизм module .exports позволяет экспортировать одну перемен
ную, функцию или объект. Если вы создаете модуль, который заполняет как exports,
так и m odule.exports, то возвращается m odule.exports, а exports игнорируется.
48 Глава 2. Основы программирования Node
{
"main": "currency.js"
}
app.js models/post.js
• D localhost:8000 х _ Alex
f- -> С О lo c a lh o st:8 0 0 0 :
Latest Posts
• Kazakhstan is a huge country... what goes on there?
• This weather is making me craaazy
• My neighbor sort o f howls at night
});
}
function getTem plate(titles, res) { getTemplate читает файл
f s . 2ead F ile('./tem plate.htm l', (e rr, data) => { шаблона и передает
i f (err) { управление formatHtml.
hadE22 o r(e 22 , res);
} else {
formatHtm l(titles, data.toS tring(), res); formatHtml читает заголовки
} и шаблон, после чего строит ответ
}); для клиента.
}
function formatHtm l(titles, tmpl, res) {
const html = tm pl.replace('% ', title s .jo in ( '< /li> < li> ') ) ;
2es.w2ІteHead(200, {'Content-Type': 'tex t/h tm l'});
res.end(html);
}
function hadE rror^rr, res) { < - Если возникает ошибка,
C0ns0le.e2202(e22); hadError выводит ее на
res.end('Server E rror'); консоль и возвращает клиенту
} сообщение «Server Error».
console.error(err);
res.end('Server E rror');
}
Итак, теперь вы знаете, как использовать обратные вызовы для обработки одно
разовых событий для таких задач, как определение ответов при чтении файлов
и запросов веб-серверов, и мы можем перейти к организации событий с использо
ванием генераторов событий.
П П 2. Shell О
После того как эхо-сервер будет запущен, вы можете подключиться к нему следу
ющей командой:
teln et 127.0.0.1 8888
Каждый раз, когда подключенный сеанс teln et отправляет данные серверу, эти
данные будут продублированы в сеансе telnet.
Однако функция обратного вызова jo in никогда не будет вызвана, потому что ника
кие события еще не генерируются. Добавьте в листинг строку, которая инициирует
событие функцией emit:
channel.em it('join');
ИМЕНА СОБЫТИЙ
События — ключи, с которыми может быть связано любое строковое значение: data,
join и вообще произвольное длинное имя. Только одно событие с именем error играет
особую роль; вскоре мы рассмотрим его.
2.8. Обработка повторяющихся событий с генераторами событий 59
После того как чат-сервер заработает, откройте новое окно командной строки
и введите следующий код для входа в чат:
teln et 127.0.0.1 8888
Если вы открыли несколько окон командной строки, вы увидите, что все данные,
вводимые в одном окне, воспроизводятся в других.
У этого чат-сервера есть серьезная проблема: при закрытии подключений и вы
ходе пользователя из чат-комнаты остается слушатель, который будет пытаться
60 Глава 2. Основы программирования Node
Теперь при вводе любым участником в чате команды shutdown все участники будут
отключены.
2.8. Обработка повторяющихся событий с генераторами событий 61
ОБРАБОТКА ОШИБОК
После того как класс Watcher будет определен, его можно использовать для создания
объекта W atcher следую щ ей командой:
w a tc h e r.s ta rt();
этих задач важен, вы м ож ете столкнуться с новой проблемой: как управлять тем,
когда им енно будет вы полняться каж дая задача в серии асинхронны х задач?
П реж де чем зан яться управлением потоком вы полнен ия (эта тем а будет рассм а
триваться в разделе 2.10), стоить рассм отреть некоторы е проблемы, с которы м и вы
с больш ой вероятностью столкнетесь при нап исании асинхронного кода.
Ц и к л собы тий N ode, наприм ер, о тслеж ивает асинхрон ную логику, вы полнен ие
которой еще не заверш илось. П ока остается незаверш енная асинхронная логика, вы
ход из процесса N ode не происходит. П оведение постоянно вы полняемого процесса
N ode хорош о подходит д ля такого прилож ения, как веб-сервер, но неж елательно
д л я процессов, которы е долж ны завер ш и ться по истечении некоторого периода
вр ем ени (н ап р и м ер , програм м ком ан дной строки ). Ц и к л собы тий отслеж ивает
все подклю чения к базе данных, пока они не будут закры ты , не п озволяя N ode з а
верш ить работу прилож ения.
П ерем енны е при лож ения такж е могут неож иданно изм еняться при недостаточном
соблю дении осторож ности. В ли сти н ге 2.14 приведен прим ер того, как порядок
вы п о л н ен и я асинхронного кода м ож ет вы звать путаницу. Е сли бы прим ер кода
вы п о л н ял ся синхронно, м ож но предполож ить, что было бы выведено сообщ ение
«T he color is blue». Н о поскольку прим ер вы полняется асинхронно, значение пере
м енной c o lo r и зм ен яется до вы п о л н ен и я c o n s o le .lo g , и вы вод и тся сообщ ение
«T he color is green».
(color => {
asyncFunction(() => {
console.log('T he color i s ' , color);
});
} )(c o lo r);
ЗАМЫКАНИЯ
За дополнительной информацией о замыканиях обращайтесь к документации JavaScript
от Mozilla: https://developer.mozilla.org/en-US/docs/JavaScript/Guide/Closures.
Зад ачи , которы е д олж ны в ы п о л н яться одна после другой, назы ваю тся последо
вательными. П ростой при м ер — задачи со зд ан и я каталога и сохранени я ф ай л а
в каталоге. Ф ай л невозм ож но сохранить до того, как каталог будет создан.
66 Глава 2. Основы программирования Node
Задачи, которые не обязаны вы полняться одна за другой, назы ваю тся параллельны
ми. О тносительны й порядок запуска и остановки этих задач мож ет быть неважен,
но все эти задачи долж ны бы ть заверш ен ы до вы п ол н ен и я дальн ейш ей логики.
П рим ер — загрузка нескольких ф айлов, которы е позднее будут сж аты в zip-архив.
Ф ай л ы могут загруж аться одновременно, но все загрузки долж ны быть заверш ены
до создания архива.
setTimeout(() => {
c o n so le .lo g ('I execute f i r s t . ') ;
setTimeout(() => {
c o n so le .lo g ('I execute n e x t.') ;
setTim eout(() => {
c o n so le .lo g ('I execute l a s t . ') ;
}, 100);
}, 500);
}, 1000);
npm in s t a l l async
68 Глава 2. Основы программирования Node
mkdir listing_217
cd listing_217
npm i n i t -y
npm in s t a l l --save request@2.60.0
npm in s t a l l --save htmlparser@1.7.7
М одуль re q u e s t реализует упрощ енного клиента H TTP, которы й мож ет исп ользо
ваться для вы борки данны х RSS. М одуль h tm lp arse r обладает функциональностью ,
которая позволяет преобразовать низкоуровневые данные RSS в структуры данных
JavaS crip t.
Прежде чем тестировать прилож ение, создайте ф айл rss_feeds.txt в одном каталоге со
сценарием прилож ения. Если у вас нет под рукой ни одного канала RSS, опробуйте
кан ал блога N ode по адресу http://blog.nodejs.org/feed/. П ом ести те U R L -адреса
2.13. Реализация параллельного потока выполнения 71
канала RSS в текстовы й ф айл, по одному в каж дой строке ф айла. П осле того как
ф айл будет создан, откройте окно командной строки и введите следующие команды,
чтобы перейти в каталог при лож ения и вы полнить сценарий:
cd listing_217
node index.js
Результат вы глядит прим ерно так (хотя, скорее всего, он будет намного длиннее):
would: 2
wrench: 3
w riteable: 1
you: 24
О ткройте окно ком андной строки и введите следую щ ие команды, чтобы создать
два каталога: один для примера, а другой, внутри него, — д ля текстовы х ф айлов,
которы е нуж но проанализировать:
mkdir listing_218
cd listing_218
mkdir te x t
72 Глава 2. Основы программирования Node
f unct i on checkIfComp let e () { Когда все задачи будут завершены, вывести список
comp let edTa sks++; всех слов, использованных в файлах, и количество
i f (completedTasks === ta sk s.le n g th ) { вхождений каждого слова.
for ( le t index in wordCounts) { < -----
console.log('${index}: ${wordCounts[index]}');
}
}
}
function addWordCount(word) {
wordCounts[word] = (wordCounts[word]) ? wordCounts[word] + 1 : 1;
}
function countWordslnText(text) {
const words = te x t
.to S trin g ()
.toLowerCase()
.sp lit(/\W + /)
.s o r t( ) ;
words
.filte r(w o rd => word)
.forEach(word => addWordCount(word)); < -------- Подсчитывает вхождения слов втексте.
}
fs .r e a d d ir ( f ile s D ir, ( e rr, f il e s ) => { Получает список файлов в каталоге.
i f (e rr) throw e rr;
f ile s .fo rE a c h (file => {
const task = ( f il e => { -<
Определяет задачудля обработки каждого
return () => { файла. Каждая задача включает вызов функции,
fs .r e a d F ile ( f ile , (e rr, te x t) => { которая асинхронно читает содержимое файла,
i f (e rr) throw e rr; после чего подсчитывает в нем вхождения слов.
countWordslnText(text);
checkIfComplete();
});
}; Добавляет каждуюзадачу в массив функций,
} )('$ { file s D ir} /$ { file } ');
task s.p u sh (task ); вызываемыхдля параллельного выполнения.
}
tasks.forE ach(task => ta s k ( ) ) ; Запускает параллельное выполнение задач.
});
2.14. Средства, разработанные в сообществе 73
cd word_count
node word_count.js
дополнения, как Async, Step и Seq. И хотя каж дое из них заслуж ивает вним ания,
мы снова используем A sync в другом примере.
2.15. Заключение
К од N ode м ож ет о ф о р м л я т ьс я в м одули, п р ед н азн ач ен н ы е д л я повторного и с
пользования.
Э та глава полностью посвящ ена веб-прилож ениям Node. П рочитав ее, вы поймете,
как вы глядят веб-п рилож ени я N ode и как приступить к их построению . Вы будете
делать все, что делает соврем енны й веб-разработчик при создании прилож ений.
М ы создадим веб-прилож ение по образцу таких поп улярн ы х сайтов отлож енного
чтения, как In stap ap er (www.instapaper.com) и P o ck et (getpocket.com). В процессе
работы нам пр ед сто и т со зд ать н о вы й п роект N ode, у п р а в л я ть зави си м о стям и ,
создать R E S T -совм естимы й A PI, сохранить инф орм ацию в базе данных, а такж е
создать ин терф ейс на основе ш аблонов. Н а первы й взгляд довольно много, но все
концепции этой главы будут более подробно исследованы в последую щ их главах.
Н а странице при лож ения слева с целевого сайта удалены все элем енты навигации,
оставлен только основной контент и заголовок. Ч то еще важнее, статья сохран я
ется в базе данных; это позволит вам прочитать ее позднее, когда оригинал может
оказаться недоступным.
П реж де чем строить веб-приложение, следует сначала создать новый проект. В сле
дую щ ем проекте показано, как создать проект N ode с нуля.
О package.json — ф айл со списком зависим остей и ком андой запуска прилож ения;
О public/ — папка статических активов (C SS, кли ен тский код Jav aS crip t);
mkdir la te r
cd la te r
npm i n i t -fy
78 Глава 3. Что представляет собой веб-приложение Node?
Добавление зависимости
Д л я д о б ав л ен и я за в и си м о сти в п роект и сп о л ьзу й те npm . С л едую щ ая ком анда
устанавливает Express:
Е сли теперь просм отреть ф айл package.json, вы увидите, что в него был добавлен
модуль Express. С оответствую щ ий ф рагм ент вы глядит так:
"dependencies": {
"express": "Л4.14.0"
}
Простой сервер
E xpress ориен ти руется на построение м одели при лож ен и я в контексте запросов
и ответов H T T P и строится на базе встроенного м одуля N ode h ttp . Ч тобы создать
простейшее приложение, следует создать экземпляр прилож ения вызовом e x p re s s (),
добавить обработчик марш рута, а затем связать прилож ение с портом TCP. П олны й
код примера:
Все не так сложно, как каж ется! С охраните код в ф айле с именем index.js и зап у
стите его ком андой node in d e x .js . Затем откройте стран ицу http://localhost:3000
для просмотра результатов. Ч тобы не приходилось запоминать, как именно должно
запускаться каж дое прилож ение, м ногие разработчики использую т сценарии npm
д ля упрощ ения процесса.
3.1. Структура веб-приложения Node 79
Сценарии npm
Ч тобы сохранить ком анду запуска сервера (node in d e x .j s ) как сценарий npm, от
кройте ф айл package.json и добавьте в раздел s c r i p t s новое свойство с именем s t a r t :
Теперь прилож ение можно запустить командой npm s t a r t . Если вы получите ошибку
из-за того, что порт 3000 уж е используется на ваш ей маш ине, используйте другой
порт командой PORT=3001 npm s t a r t . Сценарии npm использую тся для самых разных
целей: построения пакетов на стороне клиента, зап уска тестов и генерировани я
докум ентации. В них м ож но разм естить все, что угодно; по сути это м еханизм з а
пуска м ини-сценариев.
Вместо того чтобы создавать отдельную кон ф и гурац ию сервера H TTP, вы можете
хранить ее в том ж е месте (то есть в том же каталоге). Это обстоятельство упрощ ает
разверты ван ие при лож ений N ode и управление ими.
Д ругая особенность, упрощ аю щ ая разверты ван ие при лож ений Node, — npm. П о
скольку зависимости устанавливаю тся на уровне проекта, у вас не будет конфликтов
меж ду проектам и в одной системе.
веб-ф рейм ворк, которы й вам захочется опробовать, создайте новы й каталог, вы
полните ком анду npm i n i t и установите м одуль из npm.
П реж де чем переходить к таким вопросам , как базы дан ны х и веб-интерф ейсы ,
остановим ся на создании R E S T -совм естимы х ресурсов в Express. Вы м ож ете вос
пользоваться cU R L д ля вы дачи запросов к прилож ению -примеру, чтобы получить
некоторое представление о происходящ ем , а потом перейти к более слож ны м опе
рац и ям (таким , как сохранение данны х), чтобы прилож ение было больш е похож е
на полноценное веб-прилож ение.
a p p .g e t ( '/ a r t ic l e s ', (req, re s, next) => { -<■ (1) Получает все статьи.
re s .s e n d (a rtic le s );
});
a p p .p o s t ( '/ a r t ic l e s ', (req, re s, next) => { <- (2) Создает статью.
res.send('O K ');
});
a p p .g e t ( '/ a r t i c l e s / : i d ', (req, re s, next) => { <- (3) Получает одну статью.
const id = req.param s.id;
c o n so le .lo g ('F e tc h in g :', id );
re s .s e n d (a rtic le s [id ]);
});
a p p .d e l e te ( '/ a r t ic l e s /: i d ', (req, re s, next) => { (4) Удаляет статью.
С охраните этот код в ф айле index.js; если все сделано правильно, он долж ен зап у
скаться ком андой node in d e x .js .
mkdir listing3_1
cd listing3_1
npm i n i t -fy
run npm in s t a l l --save express@4.12.4
Э тот прим ер такж е м ож ет вы дать ответ с одной статьей по тому ж е принципу (3).
Вы даж е можете удалить статью (4 ), использовав клю чевое слово Jav a S c rip t d e le te
с числовы м идентиф икатором, заданны м в URL. Ч тобы получить значения из URL,
вклю чите их в строку м арш рута ( / a r t i c l e s / : i d ) и прочитайте нуж ное значение
re q .p a ra m s .id .
Л и сти н г 3.1 не позволяет создавать статьи (2 ), потому что д ля этого нуж ен парсер
тела запроса; эта тема рассм атривается в следую щ ем разделе. А пока посмотрим,
как использовать этот прим ер с cU R L (http://curl.haxx.se).
Н о почему мы сказали, что вам не удастся создать статью ? Главная причина закл ю
чается в том, что реал и зац и я запроса P O S T требует разбора тела запроса. Ранее
в поставку Express входил встроенны й парсер тела запроса, но возмож ны х способов
реализации было столько, что разработчики реш или ввести отдельную зависимость.
П арсер тела запросов знает, как приним ать тела запросов P O S T в кодировке MIME
(M u ltip u rp o se In te rn e t M ail E xtensions) и преобразовывать их в данные, которые вы
можете использовать в своем коде. О бы чно вы получаете данные JS O N , с которыми
удобно работать. К аж ды й раз, когда вы отправляете данны е ф орм ы на сайте, где-то
в программном обеспечении на стороне сервера задействуется парсер тела запросов.
Ч тобы добавить оф ици альн о поддерж иваем ы й парсер тела запросов, вы полните
следую щ ую ком анду npm:
В этом листинге добавляю тся два полезны х аспекта: разбор тела запроса в формате
JS O N ( 1 ) и тела запроса в кодировке ф орм ы (2 ). Также добавляется базовая р еа
л и зац и я создания статей: если вы создадите запрос P O S T с полем t i t l e , в массив
статей будет добавлена новая статья! Соответствующ ая команда cU R L вы глядит так:
П реж де чем д о бавл ять поддерж ку базы данны х, п род олж им работать с Express
и разработаем код обработки м арш рутов с ш ага 5. О бработчики марш рутов H T T P
в E x p re ss-части п р и л о ж е н и я вы даю т п р о сты е вы зо вы к м о д ел я м баз данны х.
П ример:
84 Глава 3. Что представляет собой веб-приложение Node?
С ущ ествует великое м нож ество баз данных; как ж е определить, какая вам нуж на?
Н иж е мы изл о ж и л и причины , по которы м д ля этого прим ера бы ла вы брана база
данны х SQ Lite.
Все эти методы м ож но реализовать с помощ ью м одуля sqlite3. Э тот модуль п озво
ляет вы бирать несколько строк результатов вы зовом d b .a l l и ли одну строку — вы
зовом d b .g e t. Н о сначала нуж но создать подклю чение к базе данных.
2 Bookshelf.js http://bookshelfjs.org.
3.3. Добавление базы данных 85
Л и сти н г 3.3 дем онстрирует вы полнение этих операций с SQ L ite в Node. Этот код
следует сохранить в ф айле db.js в одной папке с кодом из листинга 3.1.
Когда база данны х и таблиц ы будут подготовлены , при лож ение готово к выдаче
запросов. Д л я получения всех статей используется метод sqlite3 a l l (3 ). Д л я п о
лучен и я конкретной статьи используется синтаксис запросов с вопросительны м
знаком (4); модуль sqlite3 подставляет в запрос идентификатор. Наконец, вы можете
вставлять и удалять данны е методом run (5 ).
86 Глава 3. Что представляет собой веб-приложение Node?
Ч тобы этот прим ер работал, необходимо установить м одуль sqlite3 командой npm
i n s t a l l - -sav e s q l i te 3 . Н а м ом ент нап исания книги последней бы ла версия 3.1.8.
module.exports = app;
Л и сти н г 3.4 предполагает, что вы сохранили листинг 3.3 в ф айле db.js в том ж е к а
талоге. N ode загруж ает этот модуль (1 ) и затем использует его для вы борки каждой
статьи (2 ), поиска конкретной статьи (3 ) и удален ия (4).
3.3. Добавление базы данных 87
П оиск в npm по слову «read ab ility » дает достаточно м ного модулей. П опробуем
воспользоваться модулем n o d e-read ab ility (сущ ествует в версии 1.0.1 на момент
н ап и сан и я кн и ги ). У становите м одуль ком андой npm i n s t a l l n o d e - r e a d a b i l i t y
--s a v e . М одуль предоставляет асинхронную ф ункц ию д ля загрузки U R L -адреса
и преобразования разм етки H T M L в упрощ енное представление.
С ледую щ ий ф рагм ент показы вает, как используется модуль node-readability; если
вы захотите опробовать его, добавьте следую щ ий ф рагм ент в ф айл index.js в д о
полнение к листингу 3.5:
Чтобы использовать модуль в прилож ении, откройте ф айл index.js и добавьте новый
обработчик м арш рута a p p .p o s t д ля загрузки и сохранения статей. О б ъеди н яя все
это с тем, что вы узнали о H T T P P O S T в Express и парсере тела запроса, мы п о л у
чаем прим ер из листинга 3.5.
88 Глава 3. Что представляет собой веб-приложение Node?
re a d (u rl, (e rr, re s u lt) => { -<-------- (2) Использует режим удобочитаемости от выборки URL.
i f (e rr || !re su lt) re s.sta tu s(5 0 0 ).se n d ('E rro r downloading a r t i c l e ') ;
A rtic le .c re a te (
{ t i t l e : r e s u l t . t i t l e , content: re su lt.c o n te n t },
(e rr, a r tic le ) => {
i f (e rr) return n e x t(e rr);
res.send('O K '); -<-------- (3) После сохранения статьи возвращает код 200.
}
);
});
});
curl --d ata "url= http://m anning.com /cantelon2/" h ttp ://lo c a lh o s t:3 0 0 0 /a rtic le s
О т с о х р а н е н и я с т а т е й и их п р о г р а м м н о й в ы б о р к и м ы п ер ей д е м к д о б а в л е
нию веб-и нтерф ейса, чтобы п о л ьзо вател и такж е м огли прочитать сохраненны е
статьи.
res.form at({
html: () => {
r e s .r e n d e r ( 'a r ti c le s .e js ', { a r tic le s : a r tic le s });
},
json: () => {
re s .s e n d (a rtic le s );
}
});
npm in s t a l l e js --save
Д алее необходимо создать ш аблон articles.ejs в папке views. В листинге 3.6 приведен
полны й ш аблон, которы й вы м ож ете использовать.
90 Глава 3. Что представляет собой веб-приложение Node?
Статические файлы
Д л я о тп р ав к и б р ау зер у кл и ен тск о го кода Ja v a S c rip t, гр а ф и к и и C SS в E xpress
им еется в с т р о ен н а я п р о с л о й к а e x p r e s s . s t a t i c . Ч тоб ы и с п о л ьзо в ат ь ее, п е р е
д ай те катал о г со стати ч ески м и ф ай л ам и , и эти ф ай л ы стан ут д оступ н ы м и для
браузера.
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: tru e }));
app.use(
'/c s s /b o o ts tra p .c s s ',
e x p re ss.sta tic ('n o d e _ m o d u le s/b o o tstra p /d ist/c ss/b o o tstra p .css')
);
<html>
<head>
< title > la te r ;< /title >
<link rel="stylesheet" href=”/css/bootstrap.css” >
</head>
<body>
<div class="container">
Это только разм етка CSS; в поставку B o o tstrap такж е входят другие ф айлы , вкл ю
ч ая значки, ш риф ты и плагины jQ uery. Вы мож ете добавить в свой проект другие
ф айлы B o o tstrap или ж е упаковать их в один ф айл д ля удобства загрузки.
3.5. Заключение
О В еб-прилож ение N ode м ож но бы стро построить с нуля при помощ и команды
npm i n i t и Express.
О E x p re ss п о з в о л я е т с о з д а в а т ь в е б -п р и л о ж е н и я с и с п о л ь з о в а н и е м R E S T -
совм естимы х API.
В соврем енной веб-разработке N ode все чащ е исп ользуется д ля запуска и н стр у
м ентов и сервисов, от которы х зав и ся т ф рон тэн д -разработчи ки . В озмож но, вам
как N o d e -програм м исту при дется отвечать за н астройку и сопровож дени е этих
инструм ентов. А как полностековом у р азработчику вам стоит исп ользовать эти
ин струм енты д ля создан ия более бы стры х и надеж ны х веб-п рилож ени й. В этой
главе вы научитесь использовать сценарии npm, G ulp и w ebpack д ля построения
проектов, удобны х в сопровож дении.
В сл еду ю щ ем р а зд ел е п р и в о д и тс я к р а т к о е в вед ен и е во ф р о н т эн д -р а зр а б о т к у
с и сп о льзо ван и ем N ode. П осле этого будут рассм отрен ы при м еры соврем енны х
тех н о л о ги й — нап рим ер, R eact, которы е вы см ож ете и сп о л ьзо в ать в своих п р о
ектах.
4.1. Фронтэнд-разработка
с использованием Node
В последнее врем я как разработчики фронтэнда, так и разработчики кода на сто
роне сервера стали прим енять npm для передачи Jav aS crip t. Это означает, что npm
используется как для м одулей ф ронтэнда (таких, как R eact), так и для серверного
кода (наприм ер, Express). О днако некоторы е м одули трудно четко отнести к той
или иной стороне: lodash — прим ер библиотеки общего назначения, которая может
использоваться N ode и браузерами. П ри тщ ательной упаковке lodash один модуль
4.2. Использование npm для запуска сценариев 95
N ode такж е часто управляет систем ам и запуска тестов. Вы мож ете запускать тесты
д ля U I -кода в процессе N ode или ж е использовать сценарий N ode д ля управления
тестами, вы полняем ы м в браузере.
{
"s c rip ts " : {
" s ta r t" : "node se rv e r.js"
},
{
"s c rip ts " : {
" te s t" : "./node_modules/.bin/mocha t e s t / * .js "
},
О братите внимание: в преды дущ ем прим ере M ocha передаю тся аргументы. Также
при запуске сценариев npm м ож но передать аргум енты через два дефиса:
npm t e s t -- t e s t / * . j s
В табл. 4.1 приведена сводка некоторы х ком анд npm.
П оддерживаю тся многие команды, вклю чая команды очистки пакетов перед публи
кацией и ком анды м играции м еж ду версиям и пакетов. Впрочем, д ля больш инства
задач веб-разработки вам хватит ком анд s t a r t и t e s t .
mkdir es2015-example
cd es2015-example
npm i n i t -y
npm in s t a l l --save-dev b a b e l-c li babel-preset-es2015
echo '{ "p resets": ["es2015"] }' > .babelrc
Н иж е приведен прим ер ф айла в синтаксисе ES2015, которы й вы мож ете исп оль
зовать; сохраните его в ф айле browser.js:
class Example {
render() {
return '<h1>Example</h1>';
}
}
const example = new Example();
console.log(exam ple.render());
Ч тобы протестировать этот пример, введите команду npm run babel. Если все было
настроено прави льн о, д о лж н а п о я в и ть ся п ап ка п остроен и я с ф айлом browser.js.
О ткройте browser.js и убедитесь в том, что это действительно ф айл ES5 (поищ ите
конструкцию вида v a r _ c r e a te C la s s в начале ф айла.
Если ваш проект при построении ничего более не делает, ему можно присвоить имя
b u ild вместо b ab el в ф айле package.json. Н о м ож но пойти еще дальш е и добавить
UglifyJS:
Теперь вы см ож ете вы полни ть ком анду npm run u g lif y . Всю ф ун кц и он альн ость
можно связать воедино, объединив оба сценария. Добавьте еще одно свойство s c r ip t
с именем b u ild д ля вы зова обеих задач:
О ба сц ен ар и я зап ускаю тся ком ан дой npm run b u ild . У частн ики ваш ей ком анды
м огут объединить несколько средств у п ак овки кл и ен тской части вы зовом этой
простой команды. Такое реш ение работает, потому что Babel и UglifyJS могут вы
по л н яться как сценарии ком андной строки, они получаю т аргументы командной
строки и легко добавляю тся как однострочны е команды в ф айл package.json. Также
Babel позволяет определить сложное поведение в ф айле .b a b e lrc , что было сделано
ранее в этой главе.
О вклю чение парам етров кон ф и гурац ии в ф айл package.json. Babel такж е поддер
ж ивает эту возможность.
• build/admin.js • build/public.js
Конка • lib/shared.js Конка • lib/shared.js
тенация => build/admin.js тенация => build/public.js
• build/admin.js • build/admin.js
Minify => assets/admin.min.js Minify => assets/public.min.js
• Retina • Retina
• CSS sprite sheets • CSS sprite sheets
Обработка Обработка
• Branding shared between • Branding shared between
графики графики
admin and public admin and public
G ulp позволяет обеспечить вы сокую степень повторного исп ользован ия кода с по
мощ ью двух приемов: пр и м ен ен и я плагинов и определен ия ваш их собственны х
задач построения. Как видно из иллю страции, процесс построения представляет
собой поток, поэтом у задачи и плагины м ож но объединять в цепочку. Н апример,
R eact-часть приведенного прим ера можно обработать с использованием G ulp Babel
(www.npmjs.com/package/gulp-babel/) и встроенны х средств gulp.src:
g u lp .s rc ('p u b lic /in d e x .js x ')
.pipe(babel({
p resets: ['e s2 0 1 5 ', 'r e a c t ']
}))
.pipe(m inify())
.p ip e (g u lp .d e s t( 'b u ild /p u b lic .js ') );
G ulp м ож но запустить просты м вводом ком анды gulp. Учтите, что, если пакет gulp
был ранее установлен глобально, следует вы полнить команду npm rm - -g lo b a l gulp.
В ы полните следую щ ий фрагмент, чтобы установить gulp-cli глобально и создать
новы й проект N ode с зависим остью от Gulp:
npm i --g lo b al g u lp -c li
mkdir gulp-example
cd gulp-example
npm i n i t -y
npm i -save-dev gulp
Н е забудьте и сп о льзо вать npm с клю чом - -sa v e -d e v , когда вы хотите добавить
плагины G ulp в проект. Если вы эксперим ентируете с новы ми плагинам и и позднее
реш ите удалить их, используйте ком анду npm u n i n s t a l l - -sav e-d ev , чтобы удалить
их из ./node_modules и обновить ф айл package.json проекта.
p resets: ['e s2 0 1 5 ', 'r e a c t'] < ---- Настраивает gulp-babel для использования ES2015 и React (jsx).
}))
.p ip e ( c o n c a t( 'a ll.J s ') ) < -------- Объединяет все файлы с исходным кодом вall.js.
.p ip e (so u rc e m a p s.w rite('.')) < -------- Записывает файлы с картами отдельно.
.p ip e ( g u lp .d e s t( 'd is t') ) ; < -------- Перенаправляет все файлы в dist/folder.
});
В листинге 4.1 исп ользую тся плагины G ulp д ля получения, обработки и записи
ф айлов. С н ач ал а все входны е ф ай л ы нах о д ятся по ш аблону, после чего плагин
gulp-sourcem aps используется д ля сбора м етрик карты исходного кода для отладки
на стороне клиента. О братите вним ание на то, что д ля работы с картам и исходно
го кода нуж ны две фазы: в одной вы указы ваете, что хотите использовать карты,
а в другой записы ваете ф айлы карт. Также gulp-babel настраивается д ля обработки
ф айлов с использованием ES2015 и React.
В этом прим ере все ф айлы преобразую тся с использованием одного плагина. Так
уж выш ло, что B abel и транспили рует код R eact JS X , и преобразует ES2015 и ES5.
Когда это будет сделано, вы полняется конкатенация ф айлов с использованием пла
гина gulp-concat. П осле заверш ен ия тран сп и л яц и и происходит безопасная запись
карт исходного кода, и итоговая сборка пом ещ ается в папку dist.
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementByld('example')
);
Gulp позволяет легко описать стадии построения на JavaScript, а при помощ и метода
g u lp .ta s k ( ) вы смож ете добавить в этот ф айл собственны е задачи. Зад ачи обычно
строятся по одной схеме:
В преды дущ ем прим ере sourcem aps я в л яется особым случаем, потому что требует
двух конвейеров: д ля ко н ф и гурац ии и д ля вы вода ф айлов. Это логично, потому
102 Глава 4. Системы построения фронтэнда
Э тот ф рагмент определяет задачу с именем watch, а затем использует вызов w atch()
д ля отслеж ивани я изм енений в ф айлах R eact JSX . П ри каж дом изм енении ф айла
вы полняется задача построения по умолчанию . С небольш им и изм ен ен иям и этот
рецепт может использоваться для построения ф айлов SASS (S yntactically Awesome
Style Sheets), оптим изации граф ики и вообще практически всего, что только может
понадобиться в проектах кли ен тской части.
g u lp f ile .js
gulp/
gulp/in d ex .js
gulp/tasks/developm ent-build.js
g u lp /task s/p ro d u ctio n -b u ild .js
Usage
gulp [TASK] [OPTIONS...]
Available tasks
help Display this help text,
version prints the version. Aliases: v, V
О дно из преимущ еств w ebpack заклю чается в том, что эта система позволяет быстро
н астр о и ть си стем у п остроен и я, поддерж иваю щ ую и н кр ем ен тн ы й реж им . Если
настроить ее для автоматического построения при изм енении ф айлов, ей не об яза
тельно будет перестраивать весь проект при изм енении одного файла. В результате
процесс построения упрощ ается и становится более понятны м.
В этом разделе показано, как использовать w ebpack для небольш их проектов React.
Н о сначала определим ся с терм инологией, п ри нятой в webpack.
Е сли вам нуж но преобразовать код R eact, C offeeScript, SASS и ли любого другого
тран сп и л и р у ем о го язы ка, вам нуж ен загрузчик. Е сли ж е вам нуж но обработать
Jav aS crip t или вы полнить какие-то операции с группами файлов, вам нуж ен плагин.
mkdir webpack-example
npm i n i t -y
npm in s t a l l --save react react-dom
npm in s t a l l --save-dev webpack babel-loader babel-core
npm in s t a l l --save-dev babel-preset-es2015 b ab el-p reset-react
К онф игураци онны й ф айл содерж ит все необходимое для успеш ного построения
при лож ений R eact с ES2015. П роцесс настройки достаточно прост: определите зн а
чение e n try с главны м файлом, загруж аю щ им прилож ение. Затем укаж ите каталог,
в которы й долж ен запи сы ваться вывод; если каталог еще не существует, он будет
создан. Затем определите загрузчи к и свяж ите его с поиском ф айлов по ш аблону
при помощ и свойства t e s t . Н аконец, задайте все необходимые парам етры загр у з
чика. В данном прим ере эти парам етры загруж аю т B abel-плагины ES2015 и React.
Вклю чите код R eact JS X в ф айл app/index.jsx; используйте ф рагмент из раздела 4.3.2.
Теперь ком анда ./n o d e _ m o d u le s/.b in /w e b p a c k отком пилирует ES5-версию ф айла
с зависи м остям и React.
106 Глава 4. Системы построения фронтэнда
output: {
path: p ath .reso lv e(_dirname, 'd i s t ') ,
filename: 'b u n d le .js ',
publicPath: '/ a s s e t s / '
},
<s c r ipt s rc ="/a sse ts/b u n d le . j s "></ s c r i pt > < ---- 1 общедоступный путь пакета,
</body> построенного с webpack.
</html>
П арам етр - -h o t заставл яет сервер д ля разр аботки исп ользовать реж им горячей
перезагрузки модулей. Е сли отредактировать R ea ct-ф ай л прим ера в app/index.jsx,
браузер долж ен обновиться. М еханизм обновления задается параметром - - in lin e .
В реж име i n l i n e сервер для разработки внедряет код для управления обновлением
пакета. Также сущ ествует версия ifram e, которая заклю чает всю страницу в ifram e.
Зап уск сервера для разработки w ebpack инициирует построение и запускает сервер
с про сл у ш и ван и ем порта 3001. Ч тобы про тести ровать результат, введи те адрес
http://localhost:3001 в браузере.
ГОРЯЧАЯ ПЕРЕЗАГРУЗКА
И з-за R eact и других ф рейм ворков, вклю чая AngularJS, существуют проекты
горячей п ерезагрузки м одулей, предназначенны е д л я кон кретны х ф р ей м
ворков. Н екоторы е из них задействую т ф рейм ворки потоков данны х (такие,
как R edux и R elay); это означает, что код м ож ет обновляться с сохранением
текущ его состояния. Это идеальны й способ вы полнен ия перезагрузки кода,
потом у что вам не придется заново проделы вать все необходимое для вос
создания состоян ия пользовательского интерф ейса, над которы м вы рабо
таете. О днако п ри веден н ы й прим ер в м еньш ей степени п ри вязан к R eact
и хорош о подходит д ля начала работы с серверам и д ля разработки webpack.
О бязательно поэксперим ентируйте и найдите тот вариант, которы й лучш е
подходит д ля ваш его проекта.
WEBPACK и COMMONJS
Ч тобы исп ользовать син такси с м одулей C om m onJS с w ebpack, вам не придется
ничего настраивать. Д опустим, им еется ф айл, в котором используется re q u ire :
module.exports = function() {
return 'h e llo ';
};
module.exports = {
entry: './a p p /in d e x .js ',
output: { path: _dirname, filename: 'd is t/b u n d le .js ' },
};
Э тот прим ер показы вает, насколько сильно различаю тся G ulp и webpack. w ebpack
полностью сп ец и али зи р у ется на построении пакетов и в кон тексте этой задачи
может генерировать пакеты с оболочками совместимости Com m onJS. О ткры в файл
dist/bundle.js, вы увидите в начале ф айла оболочку совместимости webpackBootstrap,
а затем все ф айлы из исходного дерева кода, вклю ченны е в зам ы кан ия для м одели
ровани я системы модулей. С ледую щ ий ф рагм ент я в л я ет ся частью пакета:
h e llo ();
/***/ },
/* 1 */
/***/ function(module, exports) {
module.exports = function() {
return 'h e llo ';
};
К ом м ентарии в коде показы ваю т, где о пред еляю тся м одули, а ф айлы получаю т
доступ к объектам module и e x p o rts как аргументам своих зам ы кан ий д ля м одел и
ровани я A P I м одуля Com m onJS.
4.5. Заключение 109
Это означает, что w ebpack предоставляет в ваш е распоряж ение м одули C om m onJS
и доступ к м одулям из npm без какой-либо д ополнительной конфигурации!
4.5. Заключение
О Если вам понадобится автоматизировать простые задачи или запускать сценарии,
сценарии npm идеально подходят д ля этой цели.
О Е сли вам нуж но построить пакет на стороне клиента, реш ение с w ebpack может
быть м енее трудоем ким , чем настройка соответствую щ его сценари я с Gulp.
Зад ача вы бора правильного ф рейм ворка сложна, потому что их слож но сравнивать
в систем е едины х кри тери ев. У м ногих разработч и ков нет врем ени и зучать все
ф реймворки, поэтому мы склонны приним ать субъективные реш ения относительно
тех ф рейм ворков, с которы м и у нас им еется опыт работы. И ногда разны е ф р ей м
ворки использую тся совместно. Н апример, Express мож ет использоваться с более
кр у п н ы м и п р и л о ж е н и я м и , а м и к р о сер ви сы , обесп ечи ваю щ и е раб о ту б ольш их
прилож ений, м огут быть написаны на hapi.
5.1. Персонажи
М ы не с о б и р а е м с я р а с х в а л и в а т ь н а все л ад ы од и н ф р е й м в о р к , к о т о р ы й м о
ж ет и сп о льзо ваться в каж дом проекте. Гораздо лучш е д ействовать многогранно
5.1. Персонажи 111
и исп ользовать набор инструм ентов, подходящ их д ля каж дой задачи. П рактика
прим енения персонаж ей для проведения анализа в ходе проектирования получила
ш ирокое распространение еще и потому, что она помогает проектировщ икам со
переж ивать своим пользователям .
В этой главе персонаж и помогут вам взглянуть на фрейм ворки со стороны и понять,
как разны е классы проектов подходят д ля разны х реш ений. П ерсонаж и опред еля
ю тся в контексте проф ессиональной ситуации и средств разработки. С корее всего,
вы найдете что-то общее хотя бы с одним из трех персонаж ей, придум анны х нами
д ля этой главы.
О Инструменты — Sublim e Text, Dash, xScope, Pixelm ator, Sketch, G itH ub.
В ходе своего рабочего дня Н адин обычно распределяет время между двумя своими
крупны м и кли ен там и и работой над своим проектом с откры ты м кодом. В своей
112 Глава 5. Фреймворки на стороне сервера
кон трактн ой работе она при м ен яет разработку через тестирование, тогда как ее
проекты с откры ты м кодом ориентированы на разработку ф ункциональности.
М ногие разработчики N ode поним аю т под термином «ф рейм ворк» второй пункт:
библиотеку H T T P -сервера. В следую щ ем разделе вы познаком итесь с K oa — б и
б лиотекой сервера, которая использует генераторы (н овы й синтаксис ES2015) как
уни кальны й подход к построению пром еж уточного П О (ф у н кц и й промеж уточной
обработки) H TTP.
5.3. Koa
Koa (http://koajs.com/) базируется на Express, но использует синтаксис генераторов
ES2015 д ля определения пром еж уточного П О (ф у н к ц и й пром еж уточной обработ
ки). Это означает, что промежуточное П О можно писать практически в синхронном
стиле. О тчасти это реш ает проблем у пром еж уточного ПО, сильно зависящ его от
обратны х вызовов. С K oa вы мож ете воспользоваться клю чевы м словом y ie ld для
выхода и последую щ его возврата к пром еж уточном у ПО.
app.use(function*(next) {
const s ta r t = new Date; (2) Уступает управление следующему
yield next; <— компоненту промежуточного ПО.
(1) Использует синтаксис
const ms = new Date - s ta r t; генераторов для функций
console.log('% s %s - %s', this.m ethod, t h i s .u r l , ms); промежуточного ПО.
});
app.use(function*() {
this.body = 'Hello World';
}); (3) Управление передается
в позицию исходного вызова yield.
ap p.listen(3000);
Е сли преж де вы исп о льзо вал и пром еж уточное П О E xpress и син такси с генерато
ров, изучить K oa будет неслож но. Е сли х отя бы что-то из этого окаж ется д л я вас
новы м , вам будет достаточно трудно пон ять л огику K oa — и л и по край н ей м ере
увидеть, чем хорош этот стиль. Н а рис. 5.1 более подробно показано, как y ie ld
передает вы полнен ие м еж ду ко м п онентам и пром еж уточного ПО.
К аж дая ф аза на рис. 5.1 соответствует ном ерам в листинге 5.1. С начала в первом
ком поненте пром еж уточного П О устан авли вается тайм ер (1 ), а затем управление
уступается второму компоненту промежуточного ПО, которы й строит тело (2 ). П о
сле того как ответ будет отправлен, управление возвращ ается первому компоненту
промеж уточного П О , где вы чи сляется врем я вы полнен ия (3 ). О но вы водится на
терминал вызовом c o n s o le . log, и обработка запроса на этом заверш ается. О братите
вним ание: стадия (4 ) в листинге 5.1 не видна; она обрабаты вается K oa и H T T P -
сервером Node.
5.3. Koa 115
5.3.1. Настройка
Н астройка проекта с Koa требует установки модуля и определения промежуточного
ПО. Если вам нуж на более ш ирокая ф ункциональность (например, A PI м арш рути
зации, упрощ аю щ ий определение различны х типов запросов H T T P и реакцию на
них), необходимо установить пром еж уточное П О м арш рутизации. Таким образом,
при типичном рабочем процессе используемое ваш им проектом промежуточное ПО
долж но плани роваться заранее, поэтом у вы долж ны заранее собрать инф орм ацию
о популярны х модулях.
РАЗМЫШЛЕНИЯ ПЕРСОНАЖЕЙ
router
.p o s t( '/p a g e s ', function*(next) {
/ / Создание страницы
})
. g e t( '/p a g e s /:id ', function*(next) {
/ / Визуализация страницы
})
.p u t('p a g e s-u p d a te ', '/p a g e s /:id ', function*(next) {
/ / Обновление страницы
});
И м ена марш рутов задаю тся дополнительны м аргументом. Это очень удобно, п о
том у что вы мож ете генерировать U R L -адреса, которы е поддерж иваю тся не всеми
веб-ф рейм воркам и Node. Пример:
РАЗМЫШЛЕНИЯ ПЕРСОНАЖЕЙ
Ф ил: «Эта библиотека марш рутизации напоминает мне многое из того, что мне
понравилось в R uby on Rails. П ожалуй, к Koa все ж е стоит присмотреться!»
Н адин: «Я виж у возм ож ности д ля разб и ен и я м оих сущ ествую щ их проектов
на м одули и последую щ его распространения кода в сообществе».
5.4. Kraken
K raken базируется на Express, но добавляет новую ф ункциональность через специ
альные модули, разработанные PayPal. О дин из особенно полезных модулей — Lusca
(https://github.com/krakenjs/lusca) — предоставляет прослойку безопасности п р и
лож ения. И хотя Lusca м ож ет использоваться и без K raken, одним из преимущ еств
K raken я в л яется предопределенная структура прилож ения.
5.4.1. Настройка
Е сли у вас уж е им еется проект Express, K raken м ож но д обавить как ком понент
промеж уточного ПО:
hh / '_ _ \
l(@)(@)l Release the Kraken!
) __ (
/ , ') ) ( ( '. \
(( (( )) ))
' \ ') ( ' / '
Генератор создает новы й каталог, так что вам не придется делать это самостоятельно.
П осле того как генератор заверш ит работу, вы смож ете запустить сервер, откры ть
адрес http://localhost:8000 и убедиться в этом.
co n tro lle rs
|-u ser
|- c r e a te .j s
|- lis t.js
Так как K raken предоставляет как библиотеку ш аблонизации (D u st), так и средства
ин терн ац ионализац ии (M ak ara), эти две области идеально интегрированы . Ч тобы
написать ш аблоны D u st с поддерж кой интернационализации, необходимо задать
соответствую щ ий ключ:
РАЗМЫШЛЕНИЯ ПЕРСОНАЖЕЙ
5.5. hapi
hapi (http://hapijs.com/) — серверный ф реймворк для создания A PI веб-приложений.
Он имеет собственны й A PI плагинов и не вклю чает никакой поддерж ки на стороне
клиента или уровня модели базы данных; поддерживает A PI марш рутизации и имеет
собственную обертку д ля сервера H T T P. В hapi вы проекти руете A PI, о р и ен ти
руясь на сервер как на основную абстракцию . Б л агод аря встроенной серверной
ф ункц иональн ости подклю чений и ведения ж урн ала hapi хорош о п роявляет себя
в области м асш табирования и у п равлени я с точки зрен и я D evO ps. В табл. 5.3 п р и
ведена сводка основной ф ункц иональн ости hapi.
5.5.1. Настройка
С начала создайте новы й проект N ode и установите hapi:
mkdir listing5_2
cd listing5_2
npm i n i t -y
npm in s t a l l --save hapi
Затем создайте новы й ф айл с именем server.js. Д обавьте код из листинга 5.2.
server.connection({
host: 'lo c a lh o s t',
port: 8000
});
server.route({
method: 'GET',
p a th :'/h e llo ',
handler: (request, reply) => {
return re p ly ('h e llo w orld');
}
});
s e r v e r .s ta r t( ( e r r ) => {
i f (e rr) {
throw e rr;
}
co nsole.log('S erver running a t : ' , s e rv e r.in fo .u ri);
});
Добавьте этот код в предыдущ ий листинг для определения марш рута и обработчика,
которы й отвечает текстом «H ello W orld». П рим ер м ож но запустить командой npm
s t a r t . Введите адрес http://localhost:8000/hello, чтобы увидеть ответ.
Ч тобы вы йти за пределы этого простого примера и получить доступ к расш иренной
ф ункциональности (наприм ер, предоставлению статических ф айлов), вам понадо
бятся плагины.
5.5.3. Плагины
hapi использует собственную архитектуру плагинов, и больш инству проектов для
предоставления такой ф ункциональности, как аутен ти ф и каци я и проверка ввода,
требуется несколько плагинов. П ростой плагин, необходим ы й больш инству п ро
ектов, — in e rt (https://github.com/hapijs/inert) — добавляет поддерж ку статических
ф айлов и обработчики каталогов.
Убедитесь в том, что вы создали проект из листинга 5.2. Затем установите inert:
npm in s t a l l --save in e rt
server.route({
method: 'GET',
path: '/{param *}',
handler: {
d irecto ry : {
path: ' . ' ,
redirectToSlash: tru e ,
index: tru e
}
}
});
М арш руты hapi м огут получать не только ф ункции, но и объекты конф игурации
д л я плагинов. В этом ли сти н ге объект d i r e c t o r y вклю чает настрой ки in e rt для
предоставления ф айлов из текущ его пути и вы вода индекса ф айлов в этом ката
логе. В этом отнош ении hapi отличается от промеж уточного П О Express; пример
показы вает, как плагины могут расш ирять поведение сервера в п ри лож ениях hapi.
server.route({
method: 'DELETE',
path: '/ite m s /{ id } ',
handler: (req, reply) => {
/ / Удалить "item" на основании req.param s.id
re p ly (tru e );
}
});
РАЗМЫШЛЕНИЯ ПЕРСОНАЖЕЙ
5.6. Sails.js
Ф рейм ворки, которы е бы ли рассм отрены до настоящ его момента, представляли
собой м ин им альны е серверны е библиотеки. Sails (http://sailsjs.org/) — ф рейм ворк
M VC (M o d el-V iew -C o n tro ller), при нци пиально отли чаю щ и йся от серверной б и
блиотеки. Sails вклю чает в себя библиотеку объектно-реляционного отображ ения
5.6. Sails.js 125
Документация http://sailsjs.org/documentation/concepts/
РАЗМЫШЛЕНИЯ ПЕРСОНАЖЕЙ
Ф и л: «Д а ведь это им енно то, что мне нуж но, — в чем подвох?!»
Э лис: « С н ач ал а я подум ала, что м не это не подойдет, потом у что мы уже
п о тр ати л и вр ем я р азр аб о тк и на п р и ло ж ен и е R eact. Н о если это реш ение
ориентировано на сервер, возможно, оно сработает д ля наш его продукта».
5.6.1. Настройка
В поставку Sails вклю чен генератор проектов, поэтому лучш е вы полнить глобаль
ную установку, чтобы упростить создание новы х проектов. Установите Sails из npm
и введите ком анду s a i l s new, чтобы создать проект:
npm i n s ta ll -g s a ils
s a ils new example-project
m odule.exports.routes = {
'g e t /exam ple': { view: 'example' },
'p o st /ite m s ': 'Item C o n tro ller.create
};
П ервы й м арш рут ожидает получить ф айл с именем views/example.ejs. Второй м арш
рут ож идает получить ф айл с именем api/controllers/ItemController и методом c re a te .
Ч тобы сгенерировать этот контроллер с методом c re a te , вы полните команду s a i l s
g e n e ra te c o n t r o l l e r item c r e a te . А налогичны е команды могут использоваться для
бы строго создания R E S T -совм естимы х A PI.
Sails объединяет модели баз данны х и контроллеры в A PI; чтобы быстро создать
заглуш ки д ля R E S T -совм естимы х A PI, введите ком анду s a i l s g e n e ra te a p i имя-
ресурса. Ч тобы использовать базу данных, сначала необходимо установить адаптер
базы данны х. Д л я д о бавл ен и я M y S Q L необходим о н ай ти и м я пакета W aterlin e
M yS Q L (https://github.com/balderdashy/waterline), а затем добавить его в проект:
Затем откройте ф айл config/connections.js и введите подробную инф орм ацию о под
клю чении д ля ваш его сервера M ySQ L. Ф ай л ы м оделей Sails позволяю т вам опре
делить подклю чение к базе данных, так что вы мож ете использовать разны е модели
с разны м и базам и данны х. Н априм ер, это позволяет реализовать такие ситуации,
как хранение базы данны х пользовательских сеансов в R edis с хранением других,
более д олгосрочны х ресурсов в р ел яц и о н н ой базе дан ны х (напри м ер, M ySQ L ).
РАЗМЫШЛЕНИЯ ПЕРСОНАЖЕЙ
Sails также обладает рядом недостатков, общих для всех ф реймворков MVC на сторо
не сервера: A P I м арш рутизации означает, что прилож ение долж но проектироваться
с учетом средств м арш рутизации Sails, и вам может быть трудно адаптировать свою
схему д ля того подхода, которы й используется в W aterline.
5.7. DerbyJS
D erb y JS — п о л н о стеко вы й ф рейм ворк, которы й поддерж ивает син хронизац ию
данны х и серверную визуализацию представлений. В этом он зависит от M ongoD B
и Redis. Уровень син хронизац ии данны х предоставляется ShareJS и поддерж ивает
автом атическое разреш ение кон ф ли ктов. В табл. 5.5 приведена сводка основной
ф ункц иональн ости D erbyJS.
128 Глава 5. Фреймворки на стороне сервера
Документация http://derbyjs.com/docs/derby-0.6
Лицензия MIT
5.7.1. Настройка
Е сли у вас установлен M ongoD B или Redis, вам придется установить оба пакета
д ля запуска прим еров D erbyJS. Д окум ентация D erbyJS объясянет, как это делается
в M ac OS, L inux и W indow s (http://derbyjs.com/started#environment).
Ч тобы бы стро создать новы й проект D erbyJS, устан овите derb y и derb y -starter.
П акет d e rb y -sta rte r используется д ля базовой ин иц и али зац и и при лож ения Derby:
mkdir example-derby-app
cd example-derby-app
npm i n i t -f
npm in s ta l l --save derby d e rb y -sta rte r derby-debug
П р и л о ж ен и я D erb y разб и ваю тся на несколько м алы х при лож ений ; создайте н о
вы й каталог пр и ло ж ен и й с тр ем я ф айлам и: index.js, server, js и index.html. В л и ст и н
ге 5.5 при ведено простое п р и ло ж ен и е Derby, которое вы п ол н яет ви зуали зац и ю
ш аблона.
С ерверны й ф айл долж ен загрузить только модуль derby-starter, как показано в сле
дую щ ем фрагменте. С охраните его в ф айле app/server.js:
<Body:>
H oller: <input value="{{hello.message}}">
<h2>{{hello.message}}</h2>
index:
h2 Hello
p Hello world
Если вы вы полнили ком анду npm s t a r t , проект долж ен постоянно обновляться, по
этом у при откры тии адреса http://localhost:3000/hello теперь долж но отображ аться
новое представление.
РАЗМЫШЛЕНИЯ ПЕРСОНАЖЕЙ
5.8. Flatiron.js
F latiro n — веб-ф рейм ворк, вклю чаю щ ий в себя ф ункциональность м арш рутизации
U R L, у п р ав л ен и я данны м и, пром еж уточного П О , плагинов и веден и я ж урнала.
В отличие от б ольш ин ства веб-ф рейм ворков, м одули F latiro n проекти ровали сь
в расчете на м аксим альную логическую изоляцию , поэтому вам не при дется и с
пользовать их все. Вы даж е мож ете использовать их в собственны х проектах: если,
скаж ем , вам п о н р ав и тся м о д у л ь в ед ен и я ж у р н ала, вы м ож ете п од кл ю чи ть его
к проекту Express. В отличие от м ногих ф рейм ворков Node, уровни м арш рутизации
U R L и пром еж уточного П О в F latiro n не написаны с использованием Express или
C onnect, хотя пром еж уточное П О обладает обратной совместимостью с Connect.
В табл. 5.6 приведена сводка основной ф ункц иональн ости Flatiron.
Документация https://github.com/flatiron
Лицензия MIT
5.8.1. Настройка
Установка F latiro n требует глобальной установки средств ком андной строки для
создания новы х проектов Flatiron:
npm in s t a l l -g f la tir o n
f la tir o n create exam ple-flatiron-app
П осле вы полнен ия этих ком анд вы найдете новы й каталог с ф айлом package.json
и необходимыми зависимостями. Выполните команду npm i n s t a l l , чтобы установить
зависим ости, а затем запустите прилож ение ком андой npm s t a r t .
ap p .c o n fig .file ({ f i l e : p a th .jo in (_dirname, 'c o n fig ', 'c o n fig .js o n ') });
}
});
const server = h ttp .c re a te S e rv e r((re q , res) =>
ro u te r.d isp a tc h (req , res);
});
В отличие от больш ин ства веб-ф рейм ворков N ode, F latiro n содерж ит менедж ер
плагинов. П лагины , поддерж иваем ы е сообщ еством, упрощ аю т расш и рение п р о
ектов Flatiron.
РАЗМЫШЛЕНИЯ ПЕРСОНАЖЕЙ
К ч ислу сильны х конкурентов при надлеж ит LoopB ack — последний ф рейм ворк,
описанны й в этой главе.
5.9. LoopBack
Ф р ей м во р к LoopB ack был создан S trongL oop — компанией, предоставляю щ ей н е
сколько ком м ерческих сервисов, поддерж иваю щ их разработку веб-п рилож ени й
Node. Ф о рм ально это ф рейм ворк A PI, но благодаря некоторы м своим возм ож н о
стям он хорош о подходит д ля баз данны х и при лож ений MVC. О н даж е вклю чает
в себя веб-интерфейс для анализа и управления R E ST API. Если вы ищете решение,
которое помож ет создавать веб-A PI д ля м обильны х и настольны х клиентов, ф у н к
циональность LoopBack подойдет идеально. В табл. 5.7 приведена сводка основной
ф ункц иональн ости LoopBack.
5.9. LoopBack 135
L o o p B a c k р а с п р о с т р а н я е т с я с о т к р ы т ы м ко д ом , и с м о м е н т а п р и о б р е т е н и я
StrongLoop компанией IBM фреймворк получил серьезную коммерческую поддерж
ку. В результате он стал уникальны м предложением в сообществе Node. В частности,
в него вклю чены генераторы Yeoman для быстрой настройки оснастки прилож ений.
В следую щ ем разделе показано, как создать новое прилож ение LoopBack.
5.9.1. Настройка
Д л я создания нового проекта L oopB ack необходимо воспользоваться програм мой
ком андной строки StrongL oop (www.npmjs.com/package/strongloop). П осле глобаль
ной установки пакета strongloop ин струм ентарий ком андной строки запускается
ком ан дой s l c . П акет вклю чает в себя средства у п р ав л ен и я процессам и, но нас
интересует преж де всего генератор проектов LoopBack:
npm in s t a l l -g strongloop
slc loopback
П рограм м а ком андной строки S trongL oop пом огает вам пройти все дей стви я по
созданию нового проекта. Введите им я проекта, а затем вы берите заготовку п р и
лож ения a p i-s e rv e r. Когда генератор закончит устанавливать зависимости проекта,
он вы ведет полезны е реком ендации по работе с новым проектом (рис. 5.2).
Чтобы запустить проект, введите команду node ., а чтобы создать модель — команду
slc :lo o p b a c k :m o d e l. Вы будете регулярно использовать ком анду s lc при создании
нового проекта LoopBack.
К о гд а п р о е к т за р а б о т а е т , вы см о ж е т е о б р а т и т ь с я к A P I E x p lo re r по ад р есу
http://0.0.0.0:3000/explorer/. Щ елкните на ссы лке User, чтобы раскры ть узел. Вы
увидите больш ой список доступн ы х м етодов A PI, вкл ю чая стан дартны е R E ST -
совм естимы е марш руты — такие, как PUT /U se rs и DELETE /U s e rs /{ id } . A P I Explorer
показан на рис. 5.3.
136 Глава 5. Фреймворки на стороне сервера
• • 3. alex@AI«xs-ѴлсВоок-Рго; -/Docum*nts/Codt (a h )
sic loopback
I I .-----------------------------.
I— <o)— I I Let's create a LoopBack |
-------- ' I application! |
( _U _ )
|e ' Y
7 What kind of application do you have in mind? api-server (A LoopBack API server with local Use
r auth)
Generating .yo-rc.json
I'm all done. Running for you to install the required dependencies. If this fails, t
ry running the command yourself.
create .editorconfig
create .jshintignore
create .jshintrc
create server/boot/root.js
create server/middleware.json
create server/middleware.production.json
Теперь при посещ ении http://localhost:3000/hello выдается ответ H ello , world. Впро
чем, добавление м арш рутов таким способом нетипично д л я проектов LoopBack.
В озм ож но, оно необходим о д л я некоторы х необы чны х кон ечн ы х точек A PI, но,
как правило, м арш руты добавляю тся автом атически при генерировании моделей.
РАЗМЫШЛЕНИЯ ПЕРСОНАЖЕЙ
Н а этом описание ф рейм ворков в этой главе подходит к концу. П реж де чем п ере
ходить к следую щ ей главе, давайте сравним ф рейм ворки, чтобы помочь в вы боре
наиболее подходящ его варианта д ля вашего следую щ его проекта.
5.10. Сравнение
Если вы следовали за разм ы ш лени ям и персонаж ей в этой главе, возмож но, вы уже
реш или, какой ф рейм ворк вам стоит использовать. А если еще не реш или — в остав
ш ейся части этой главы сравниваю тся сильны е стороны всех ф рейм ворков. Если
ж е и сравнение не проясни т ситуацию , рис. 5.4 пом ож ет вам вы брать правильны й
ф рейм ворк, ответив на несколько вопросов.
Р ис. 5.4. Выбор фреймворка Node
140 Глава 5. Фреймворки на стороне сервера
Если вы используете больш ой ф рейм ворк MVC (наприм ер, Sails.js или LoopBack),
A P I плагинов упрощ ает создание нового проекта. LoopBack частично обходит н е
обходимость в A PI плагинов, предоставляя в распоряж ение программиста мощ ные
средства управлени я проектами. О бративш ись к учетной запи си StrongL oop в npm
5.13. Заключение 141
5.13. Заключение
О K oa — облегченны й м и н и м ал ьн ы й ф рейм ворк, исп ользую щ ий син такси с ге
нераторов для пром еж уточного П О . О н хорош о подходит д ля хостинга од н о
страничны х веб-прилож ений, зависящ их от внеш них веб-API.
О hapi ориен ти руется на серверы H T T P и м арш руты . hapi хорош о подходит для
облегченны х внутренних подсистем, состоящ их из м нож ества м елких сервисов.
О F latiro n — набор несвязны х модулей, которы е могут использоваться либо как
веб-ф рейм ворк MVC, либо как более облегченная библиотека Express. F latiron
обладает совм естимостью с пром еж уточны м П О Connect.
О K raken строится на базе Express с добавлением средств безопасности; м ож ет
и сп о льзо ваться д л я р еал и зац и и MVC, содерж ит п оддерж ку O R M и систем у
ш аблонизации.
О Sails.js — ф рейм ворк MVC, построенны й под влияни ем R ails/D jango; содерж ит
O R M и систему ш аблонов.
О D erb y JS — и зо м о р ф н ы й ф р ей м в о р к, хорош о п од х о д ящ и й д л я п р и л о ж ен и й
реального времени.
О LoopBack снимает необходимость в написании стереотипного кода для быстрого
генерирования R E ST A PI с полноценной поддерж кой баз данных и A PI Explorer.
Connect и Express
И з главы 3 вы узнали, как построить простое при лож ение Express. В этой главе
Express и C o n n ect рассм атриваю тся более подробно. Э ти два поп улярн ы х модуля
N ode использую тся м ногим и веб-разработчикам и. Э та глава показывает, как стро
ить в еб -п р и л о ж ен и я и R E S T A P I с при м ен ением наи более часто п ри м ен яем ы х
паттернов.
CONNECT И EXPRESS
Д ля начала мы покажем, как создать базовое прилож ение Connect. Далее в этой главе
вы увидите, как построить более слож ное прилож ение Express с использованием
поп улярн ы х приемов Express.
6.1. Connect
В этом разделе вы познакомитесь с Connect. Вы увидите, как промежуточные компо
ненты могут использоваться для построения просты х веб-прилож ений и насколько
важ ную роль играет порядок пром еж уточны х компонентов. П озднее это помож ет
вам при построении при лож ений Express с более вы сокой степенью модульности.
6.1. Connect 143
$ npm in s t a ll connect@3.4.0
Д о того как в аш и п р о м еж у то ч н ы е к о м п о н ен ты н ач н ут в ы п о л н я т ь ся , C o n n e c t
исп о льзу ет диспетчера, ко то р ы й получает запросы и передает их первом у п р о
м еж у то ч н о м у ком поненту, д о б ав л ен н о м у в п ри л о ж ен и и . Н а рис. 6.1 показан о
ти п и чн о е п р и ло ж ен и е C o n n ect, состоящ ее из д и сп етчера и набора п р о м еж у то ч
ны х ком п онентов, вклю чаю щ его в себя ком п онент веден и я ж урн ала, парсер тела
запроса, сервер стати ч ески х ф ай л о в и специ альное пром еж уточное програм м ное
обеспечение.
К ак видите, архитектура A P I пром еж уточного П О означает, что более слож ное по
ведение мож ет строиться из м еньш их структурны х блоков. В следую щ ем разделе
вы увидите, как это делается посредством объединения компонентов.
144 Глава 6. Connect и Express
П ром еж уточны е ком поненты имею т две сигнатуры: с next и без. Это объясняется
тем, что ком понент заверш ает ответ H T T P и возвращ ать управление диспетчеру
ему не нужно.
6.1. Connect 145
Итак, у вас заработало простое прилож ение «Hello World»; теперь можно посмотреть,
почему важен порядок вы зовов .u s e ( ) пром еж уточны х ком понентов и как страте
гически использовать этот порядок для внесения изм енений в работу прилож ения.
В этом прим ере пром еж уточны й ком понент h e llo вы зы вается первы м и реагирует
на запрос H T T P, как и ож идалось. О днако ком понент lo g g e r вы зван никогда не
будет, потом у что h e llo никогда не вы зы вает n e x t( ), поэтому управлени е никогда
не возвращ ается диспетчеру для вы зова следую щ его промеж уточного компонента.
М ораль: если ком понент не вы зы вает n e x t( ) , то остальны е ком поненты в цепочке
вы зы ваться не будут.
146 Глава 6. Connect и Express
Р и су н о к 6.2 показы вает, как в этом при м ере обходится ком понент lo g g e r и как
исправить эту проблему.
О б ы чн о п ри п о стр о ен и и п р о м еж у то ч н ы х ко м п о н ен то в и с п о л ь зу ет с я простое
соглаш ение, которое о ткры вает перед разработч и ком возм ож н ости д л я к о н ф и
гурации: и сп о льзо ван и е ф ункц ии, ко торая возвращ ает другую ф ун кц и ю (за м ы
кан и е). Б а зо в а я структура настраиваем ы х пром еж уточны х ком понентов такого
рода вы гл яд и т так:
6.1. Connect 147
function setup(options) {
/ / Логика инициализации
В этом разделе данны й прием будет применен для построения трех настраиваемых,
пригодны х д ля повторного исп ользован ия пром еж уточны х компонентов:
Н ачнем с д оработки ком п онента lo g g e r, чтобы расш и рить возм ож н ости его н а
стройки . П ро м еж у то чн ы й ком п онент lo g g e r , созд ан н ы й ранее в этой главе, не
поддерж ивал настройку. О н был ж естко запрограм м ирован д ля вы вода значений
req.m ethod и r e q . u r l при вызове. Н о что, если в какой-то м ом ент вы захотите и з
м енить состав вы водим ой инф орм ации?
вернет управление, потом у что он о пределяется в том ж е зам ы кан и и Jav aS crip t.
Затем lo g g e r зам еняет лексем ы в ф орм атной строке соответствую щ им и свойства
ми запроса из объекта req, вы водит инф орм ацию в s td o u t и вы зы вает n e x t() , как
показано в листинге 6.3.
Так как ком понент lo g g e r создавался как настраиваем ы й, вы мож ете м ногократно
вы зы вать .u s e ( ) для lo g g e r в одном при лож ении с разны м и кон ф и гурац иям и или
ж е использовать этот код в других прилож ениях, которы е вы разработаете в бу
дущем. П ростая кон цепц ия настраиваем ы х пром еж уточны х ком понентов хорош о
известна в сообщ естве C o n n ect и при м ен яется ко всем базовы м промеж уточны м
ком понентам C o n n ect д ля последовательности.
Ч т о б ы и с п о л ь з о в а т ь к о м п о н ен т l o g g e r из л и с т и н г а 6.3, п ер ед ай те ем у с т р о
к у с в к л ю ч е н и е м н е к о то р ы х св о й ств о б ъ е к та r e q u e s t . Н а п р и м ер , с в ы зо во м
.u s e ( s e tu p ( ':m e th o d : u r l ') ) д ля каж дого запроса будет вы водиться метод H T T P
(G ET, P O S T и т. д.) и U R L -адрес.
П реж де чем переходить к Express, посмотрим, как в C o n n ect поддерж ивается об
работка ошибок.
Во всех при лож ениях возникаю т ош ибки (как на системном уровне, так и на п оль
зовательском ), и вы сильно упростите себе жизнь, если будете готовы к ош ибочным
ситуациям — даж е к непредвиденны м . C o n n ect реализует разновидность пром е
ж уточны х ком понентов с обработкой ош ибок, которая следует тем ж е правилам,
что и обычная, но наряду с объектами запроса и ответа получает объект ош ибки.
6.1. Connect 149
А теперь посмотрим, как C o n n ect обрабаты вает ош ибки без какой-либо настройки.
По умолчанию C onnect отвечает кодом статуса 500, телом ответа с текстом «Internal
Server E rro r» и дополнительной и н ф орм аци ей о сам ой ош ибке. Это неплохо, но
в лю бом реальном при лож ении вы, скорее всего, предпочтете реализовать более
специализированную обработку ош ибок — например, отправлять их демону веде
н и я ж урнала.
connect()
.u s e (ro u te r ( r e q u ir e ( './r o u te s /u s e r') ) )
.u s e ( r o u te r(re q u ire ('./ro u te s /b lo g '))) / / Пропускается
.u s e (ro u te r(re q u ire ('./ro u te s /a d m in '))) / / Пропускается
.use(errorH andler);
6.2. Express 151
Диспетчер передает
запрос по стеку
промежуточных
компонентов,
как обычно
і ОйІ В компоненте
маршрутизации
произошла ошибка!
О Компонент hello
пропускается,
потому что он не был
определен как
промежуточный
компонент
для обработки ошибок
ѳ Компонент
errorHandler
получает объект
ошибки, созданный
компонентом logger,
и может ответить
на запрос
в контексте ошибки
Рис. 6.3. Жизненный цикл запроса HTTP, порождающего ошибку сервера Connect
6.2. Express
Express — поп улярн ы й веб-ф рейм ворк, которы й некогда строился на базе C onnect,
но и сейчас остается совм ести м ы м с п ром еж уточ н ы м и ком п он ен там и C onnect.
Х отя Express вклю чает в себя базовую ф ункциональность (такую , как предостав
ление статических ф айлов, м ар ш р у ти зац и я U R L и кон ф и гурац и я при лож ения),
этот ф рейм ворк все равно остается м иним альны м . О н предоставляет достаточную
структуру д ля создания блоков, подходящ их д ля повторного использования, без
изли ш н их ограничений д ля ваш ей практики разработки.
152 Глава 6. Connect и Express
В примере этой главы используются ш аблоны Em bedded JavaS cript (E JS), которые по
своей структуре напоминают разметку H TM L. Язы к EJS близок к PHP, JS P (для Java)
и ERB (д ля Ruby): серверны й код Jav aS crip t встраивается в документ H T M L и вы
полняется перед отправкой клиенту. EJS более подробно рассматривается в главе 7.
ф О alex@locohost: ~/Documents/Code/nodeinaction/ch07-connect-and-express/li.
— app.js
— bin
I--- WWW
— package.json
— public
images
Ё javascripts
stylesheets
1— style, css
— routes
I— index, js
'— users, js
— views
error.jade
Ё index.jade
layout.jade
7 directories, 9 files
Установка Express
В ы полните глобальную установку e x p r e s s - g e n e ra to r с npm:
$ npm in s t a ll -g express-generator
Д л я просм отра всех доступны х парам етров используется ф лаг - -h e lp (рис. 6.5).
alex@locohost: ~/Documents/Code/nodeinaction/ch07-connect-and-express/li...
> express --help
Options:
Н екоторы е из этих парам етров генерирую т небольш ие части п ри лож ения за вас.
Н априм ер, вы мож ете приказать сгенерировать ф и ктивн ы й ш аблонны й ф айл для
вы бранного я д р а ш аблонизации. А налогичны м образом, если задать п реп роцес
сор CSS при пом ощ и парам етра - -c s s , д ля него будет сгенерирован ф и кти вн ы й
ш аблонны й файл.
П осле того как исполняем ы е ф айлы будут установлены , посмотрим, как сгенери
ровать прилож ение, над которы м мы будем работать в этой главе.
Генерирование приложения
Анализ приложения
1 {
2 "name": "listing6_6",
3 "version": "0.0.0",
4 "privet*": true,
5 "scripts": {
6 "start": "node ./bin/www"
>,
8 "dependencies": {
9 "body-parser": "~1.13.2",
10 "cookie-parser": "**1.3.5",
11 "debug": "-2.2.0",
12 "express": "*4.13.1",
13 "jade": "*1.11.0",
14 "morgan": "*1.6.1",
15 "serve-favicon": "*2.3.0"
is >
17 }
app.use(function(req, re s, next) {
le t e rr = new E rror('N ot Found');
e r r .s ta tu s = 404;
n e x t(e rr);
}); Выводит страницы ошибок
в формате HTMLв режиме
i f (a p p .g e t('e n v ') === 'development') { разработки.
app .u se(fu n ctio n (err, req, re s, next) {
r e s .s ta tu s ( e r r .s ta tu s || 500);
r e s .r e n d e r ( 'e r r o r ', {
message: err.m essage,
e rro r: e rr
});
});
}
ap p .u se(fu n ctio n (err, req, re s, next) {
r e s .s ta tu s ( e r r .s ta tu s || 500);
r e s .r e n d e r ( 'e r r o r ', {
message: err.message,
e rro r: {}
});
});
module.exports = app;
156 Глава 6. Connect и Express
E x p r e s s - M o z illa F ir e fo x
W Express 1+
E xpress
Welcome to Express
ПЕРЕМЕННЫЕ ОКРУЖЕНИЯ
О a p p .s e t( ) ;
О a p p .g e t( ) ;
О a p p .e n a b le ();
О a p p .d is a b le ( ) ;
О a p p .e n a b le d ();
О a p p .d is a b le d ( ) .
В этом разделе будет показано, как использовать систему кон ф и гурац ии для н а
стройки поведения Express и как пользоваться этой системой для ваш их собственных
целей в ходе разработки.
Р азберем ся, что ж е поним ается под конфигурацией на базе окружения. Х отя п ере
м енная окруж ения NODE_ENV появилась в Express, многие другие ф рейм ворки N ode
п р и н ял и ее как м еханизм оповещ ения при лож ений N ode о том, в каких условиях
они работаю т (по ум олчанию реж им разработки).
М ето д a p p . c o n f i g u r e ( ) п о л у ч а ет н е о б я за т е л ь н ы е стр о к и , п р е д с т а в л я ю щ и е
перем енн ы е о круж ения, и ф ункцию . Е сли п ерем енн ая окруж ен и я соответствует
переданной строке, нем едленно ак ти ви зи р у ется ф у н кц и я обратного вы зова; если
зад ан а то лько ф у н кц и я, она вы зы вается д ля всех перем енны х окруж ения. И м ена
перем енны х окруж ения вы бираю тся соверш енно произвольно. Н априм ер, можно
определить перем енн ы е developm ent, s ta g e , t e s t и p ro d u c tio n (и л и сокращ енно
prod):
<hl>Tobi</hl>
<p>Tobi is a 2 year old ferret.</p>
__DIRNAME
e js . В противном случае Express требует указать расш ирение д ля того, чтобы опре
делить используем ое ядр о ш аблонов.
П редполож им, вы обнаруж или, что каналы RSS прощ е програм м ирую тся с другим
ядром ш аблонов, или вы переходите с одного яд р а на другое. П о ум олчанию в п р и
лож ени и используется Pug, а д ля м арш рута /f e e d — EJS, на что в следую щ ем коде
указы вает расш ирение .e js :
СИНХРОНИЗАЦИЯ PACKAGE.JSON
Помните, что каждое дополнительное ядро шаблонов, которое вы хотите использовать
в приложении, должно быть добавлено в объект зависимостей файла package.json.
Такие пакеты должны устанавливаться командой npm i n s t a l l --s a v e имя_пакета
и удаляться из node_modules и package.json командой npm u n i n s t a l l - -sav e имя_па-
кета. Такой подход упрощает эксперименты с разными ядрами шаблонов, когда вы
еще только подбираете ядро для своего проекта.
Кэширование представлений
П арам етр view cache вклю чается по ум олчанию в среде эксплуатац ии прилож ения
и предотвращ ает вы полнение дискового вво д а/вы во д а при последую щ их вы зовах
re n d e r ( ) . С одерж им ое ш аблона сохраняется в памяти, что приводит к зн ач и тел ь
ному повы ш ению бы стродействия. П обочны й эф ф ект заклю чается в том, что вы не
смож ете редактировать ф айлы ш аблонов без перезапуска сервера; именно по этой
причине данная настройка отклю чена в процессе разработки. Вероятно, в условиях
реальной эксплуатац ии ее следует держ ать вклю ченной.
К ак показано на рис. 6.9, при отклю ченном кэш ировании представлений ш аблон
читается с диска при каж дом запросе. И м енно этот реж им позволяет вносить и з
м енени я в ш аблон без перезапуска при лож ения. П ри вклю ченном кэш ировании
обращ ение к диску происходит только один раз на шаблон.
Поиск представлений
П роцесс поиска представлений проходит по тому же принципу, что и д ля ф ункции
N ode r e q u i r e ( ) . П ри вы зове r e s .r e n d e r ( ) или a p p .re n d e r( ) Express сначала п ро
веряет, сущ ествует ли ф айл по абсолю тном у пути. З атем Express проводит поиск
относительно каталога views. Н аконец, Express проверяет ф айл index. Н а рис. 6.10
этот процесс изображ ен в виде блок-схемы.
П о мере эволю ции при лож ения вам понадобятся новые представления, а иногда
несколько представлений для одного ресурса. И спользование поиска представлений
6.2. Express 163
ком п о н ен там и перед итоговы м м етодом о бработки м арш рутов, где происходит
ви зу ал и зац и я представлений.
Рис. 6.11. Значения, напрямую передаваемые функции render, имеют более высокий
приоритет при визуализации шаблона
<html>
<head>
<title><%= s e t t i n g s .t i t l e %></title>
</head>
<body>
6.2. Express 165
<h1><%= s e t t i n g s .t i t l e %></h1>
<p>Welcome to <%= s e t t i n g s .t i t l e %>.</p>
</body>
П осле создания базовой модели следует добавить ф ункцию getRange (л и сти н г 6.8).
Э та ф у н кц и я предназначена д ля вы борки записей.
6.2. Express 167
Затем добавьте следую щ ий м арш рут в ф айл routes/entries.js. Л оги ка м арш рута з а
полняет ш аблон, содерж ащ ий форму:
Затем ш аблон EJS в листинге 6.9 создает ш аблон для формы и сохраняет его в views/
post.ejs.
</p>
<p>
<textarea name='entry[body]' placeholder='Body'></textarea>
</p> Текст тела
записи.
<p>
<input type='subm it' value='P ost' />
</p>
</form>
</body>
</html>
П риведите ее к следую щ ему виду, чтобы использовать расш иренны й разбор тела
сообщ ения:
Создайте ф айл routes/entries.js. Добавьте код из листинга 6.11 для вклю чения модели
запи си и экспортирования ф ун кц и и д ля вы вода списка записей.
Перед запуском прилож ения вы полните команду touch views/menu. e js для создания
временного ф айла, которы й будет содерж ать меню на более поздней стадии. Когда
п редставления и марш руты будут готовы, необходимо сообщ ить прилож ению , где
искать марш руты.
170 Глава 6. Connect и Express
Затем такж е в ф айле app.js приведите строку с текстом a p p . g e t ( '/ ' к следую щ ему
виду, чтобы для лю бы х запросов к пути / возвращ ался список записей:
a p p .g e t ( '/ ', e n t r i e s .l i s t ) ;
Теперь при запуске п р и лож ения на главной странице вы водится список записей.
Разобравш ись с созданием и вы водом списка записей, перейдем к использованию
пром еж уточны х ком понентов, п р и вязан н ы х к конкретны м м арш рутам , д ля п ро
верки данны х форм.
М арш руты E xpress м огут получать пром еж уточны е ком поненты , при м ен яем ы е
только при сопоставлении этого марш рута, перед заверш аю щ им обратным вызовом
маршрута. С ами обратные вы зовы марш рутов во всех промежуточных компонентах
работаю т одинаково — даж е в тех, которы е мы собираемся создать для проверки
данных!
6.2. Express 171
a p p .p o s t( '/p o s t',
requireE ntryT itle,
requireEntryTitleLengthAbove(4),
entries.subm it
);
О братите внимание: это определение м арш рута, которое обычно получает в аргу
ментах только путь и логику м арш рутизации, имеет два дополнительны х аргумента
с пром еж уточны м и ком понентам и проверки данных.
a p p .p o s t( '/p o s t',
v a lid a te .r e q u ir e d ( 'e n tr y [ title ] ') ,
v a lid a te .le n g th A b o v e ('e n try [title ]', 4),
en tries.su b m it);
Зам ени те строку a p p . p o s t ( '/ p o s t ', e n t r i e s .s u b m it) ; в разделе ro u tin g ф айла app.
jsэтим ф рагм ентом . С тоит зам етить, что сообщ ество Express разработало много
аналогичны х библиотек для всеобщ его использования; тем не менее очень п ол ез
но поним ать, как работаю т пром еж уточны е ком поненты проверки данны х и как
реализовать их самостоятельно.
Е сли вы теперь опробуете прилож ение, то увидите, что проверка данны х успеш но
действует. A P I п роверки дан ны х м ож но сделать ещ е более д инам ичны м , но мы
оставим вам эту задачу д ля сам остоятельной работы.
В листинге 6.15 приведена м одель пользователя. В этом коде вклю чаю тся зав и си
м ости redis и b crypt, после чего вы зов r e d i s . c r e a t e C l i e n t ( ) откры вает п одклю
чение Redis. Ф у н к ц и я User получает объект и объединяет свойства этого объекта
со своими. Н апример, new U ser({ name: 't o b i ' }) создает объект и задает свойству
name этого объекта значение Tobi.
Защита паролей
К хеш у при м ен яется затравка (salt). П рим енение затравки на уровне п ользовате
лей помогает защ ититься от атак на базе радуж ны х таблиц: затравка играет роль
закры того клю ча для м еханизм а хеш ирования. Вы мож ете воспользоваться bcrypt
д ля генерирования 12-сим вольной затравки д ля хеш а методом g e n S a lt() .
Вот и всё!
В ывод долж ен сообщ ать о том, что пользователь был успеш но создан: например,
u s e r id 1. П осле тестирован ия м одели пользователя удалите код листинга 6.18 из
ф ай л а models/user.js.
}
s ta tic g e t(id , cb) {
d b .h g e ta ll('u s e r:$ { id } ', (e rr, user) => { < ----- Получает данные в виде простого объекта.
i f (e rr) return cb (err);
c b (n u ll, new U ser(user)); < -------- Преобразует простой объект в новый объект User.
});
}
}
Э та ф орм а отображ ается при посещ ении м арш рута / r e g i s t e r в браузере. П озднее
мы создадим аналогичную ф орм у для вы полнен ия входа.
Register
Fill in the form below to sign up!
[ Usemam*
I Password
( SignUp I
В листинге 6.22 показано, как следует изм енить ф айл app.js. С истем а м одулей Node
используется для им п ортирования модуля, определяю щ его поведение м арш рута
из каталога марш рутов, а методы H T T P и пути U R L связы ваю тся с ф ун кц и ям и
м арш рутизац ии. Так ф ор м и р у ется некое подобие контроллера. К ак вы увидите,
м арш руты регистрации сущ ествую т как д ля м етода GET, так и для PO ST.
Ч тобы определить логику марш рута, создайте в каталоге routes пустой ф айл с им е
нем register.js. Н ачн ите определение поведения м арш рута регистрации с эк спорти
ровани я следую щ ей ф ун кц и и из routes/register.js — м арш рута, которы й вы полняет
визуализац ию ш аблона регистрации:
О братите вним ание на директиву in c lu d e messages, которая вклю чает в себя другой
ш аблон: m essages.ejs. Этот ш аблон, которы й будет определен на следую щ ем шаге,
используется д ля взаим одействия с пользователем.
6.2. Express 181
Чтобы создать шаблон сообщений, создайте в каталоге views файл с именем messages.
ejs и пом естите в него код из приведенного ниж е фрагмента. Л оги ка ш аблона п ро
веряет, задано ли значение переменной l o c a l s . messages. Если оно задано, то шаблон
перебирает ее содерж им ое и вы водит объекты сообщ ений. У каж дого объекта со
общ ения им еется свойство ty p e (позволяю щ ее при необходимости использовать
сообщ ения для оповещ ений, не явл яю щ и х ся ош ибкам и) и свойство s t r i n g (текст
сообщ ения). Л о ги ка при лож ения ставит ош ибку в очередь д ля вывода, добавляя
ее в массив r e s .lo c a ls .m e s s a g e s . П осле того как сообщ ения будут вы ведены, в ы
зы вается метод removeMessages д ля очистки очереди сообщений:
Н а рис. 6.13 изображ ена ф орм а регистрации при вы воде сообщ ения об ошибке.
Register
Fill In the form below to sign up!
I Password I
I a»nup I
app.use(session({
se c re t: 's e c r e t',
resave: fa ls e , saveU ninitialized: true
}));
6.2. Express 183
Ч тобы еще больш е упростить добавление сообщ ений, используйте код из следую
щего ф рагмента. Ф у н к ц и я r e s . e r r o r позволяет легко добавить в очередь сообщ е
ний сообщ ение типа e r r o r . И спользуйте ф ункцию res.m essage, которая ранее была
определена в модуле:
Остается лиш ь интегрировать этот новы й механизм ф ункцией re q u ire () в ф айл app.
js. Этот компонент долж ен м онтироваться после ком понента сеанса, поскольку для
него долж но быть определено свойство re q .s e s s io n . О братите внимание: поскольку
этот пром еж уточны й ком понент спроектирован так, что он не получает параметры
и не возвращ ает вторую ф ункцию , м ож но использовать вы зов a p p .u se(m essag e s)
вместо a p p .u s e (m e s s a g e s ()). Д л я надеж ности в пром еж уточны х ком понентах от
сторонних разработчиков лучш е использовать app.use(m essages()) вне зависимости
от того, получает ком понент парам етры и ли нет:
184 Глава 6. Connect и Express
app.use(express.m ethodOverride());
app.use(express.cookieP arser());
app.use(session({
se c re t: 's e c r e t',
resave: fa ls e ,
saveU ninitialized: true
}));
app.use(messages);
Регистрация пользователя
М ы долж ны создать м арш рутную ф ункцию д ля обработки H T T P -запросов P O S T
к /register. Э та ф у н кц и я будет назы ваться subm it.
После отправки данны х форм ы промеж уточный компонент b o d y P a rse r() заполняет
отправленны м и данны м и свойство req.body. В ф орм е регистрации используется
объектны й синтаксис user[nam e], которы й после разбора преобразуется в свойство
re q .b o d y .u se r.n a m e . А налогичны м образом д ля поля ввода пароля используется
свойство re q .b o d y .u s e r .p a s s .
Осталось добавить небольшой фрагмент кода к марш руту отправки, который должен
обеспечивать проверку данных (например, проверку того, что им я пользователя еще
не занято), и сохранить нового пользователя, как продемонстрировано в листинге 6.24.
Теперь вы смож ете запустить прилож ение, перейти по м арш руту /register и зареги
стрировать пользователя. Следую щ ее, что потребуется, — м еханизм возвращ ения
зарегистрированны х пользователей д ля аутен ти ф и каци и через ф орм у /lo g in .
Login
Fill in the form below to sign in!
( Username
I Password
Д л я начала изм еним ф айл app.js с вклю чением м арш рутов входа и назначением
м арш рутны х путей:
</p>
</form>
</body>
</html>
П осле того как вы добавили м арш рут и ш аблон д ля вы вода ф орм ы входа на экран,
в при лож ение нуж но добавить логику обработки попы ток входа.
Д л я о б р аб о т к и п о п ы то к в х о д а в с и с т е м у вам н у ж н о д о б а в и т ь в п р и л о ж ен и е
марш рутную логику, которая проверяет им я пользователя и пароль, отправленные
из ф орм ы . Е сли и м я п о л ьзо вател я и пароль верны , и д ен ти ф и к атор п о л ьзо вате
л я п р и сваи вается сеансовой перем енной, после чего пользователь п ерен ап р авля
ется на дом аш ню ю страницу. Д обавьте эту л оги ку (л и ст и н г 6.26) в ф ай л routes/
login.js.
R О M ozilla Fircfox
h t l p : / /1 2 7 .0 .0 .1 :3 0 0 0 / | + [___________________________________________________________________________________________
Рис. 6.15. Меню входа и регистрации обеспечивает доступ к созданным нами формам
О О О M ozilla Fircfox
I <♦ h ttp ://127.0.0.1:3000/
HL+l
(•* )$ 127.0.0.1 3000 ' С Coogle < 0 [ A | | C - |
К аж ды й создан ны й вам и E JS -ш аблон, п ред ставляю щ и й стран ицу при лож ения,
содерж ит код <% in c lu d e menu %>, располож енны й после тега <body>. Э тот код под
клю чает ш аблон ./views/menu.ejs (л и сти н г 6.27).
6.2. Express 189
В озни кает резонн ы й вопрос — откуда берется л о кал ьн ая перем енн ая u se r, если
мы ее еще не создали? В следую щ ем разделе будет написан код, реализую щ ий для
каж дого запроса загрузку данны х пользователя, вош едш его в систему, и о ткры ва
ю щ ий ш аблонам доступ к этим данным.
О дна из типичны х задач веб-прилож ений — загрузка из базы данны х ин ф орм ации
о пользователе, которая обычно предоставляется в виде Jav aS crip t-объекта. Наличие
подобны х данны х облегчает взаим одействие с пользователем. В при лож ении этой
главы для каж дого запроса будут загруж аться пользовательские данны е с исп оль
зованием промеж уточного компонента.
П оскольку N ode я в л яется однопоточной платф орм ой, локальное хранилищ е п ро
граммных потоков здесь отсутствует. В случае с H T T P -сервером переменные запроса
и ответа яв л яю тся единственны м и доступны м и контекстны м и объектами. В ы со
коуровневы е ф рейм ворки м огут пользоваться средствам и N ode и предоставлять
190 Глава 6. Connect и Express
const. User
.. = require(,, ../m. odels/user
. , . ,,); Получает идентификатор пользователя,
выполнившего вход, из сеансовыхданных.
module.exports = (req, re s, next) => {
const uid = req .se ssio n .u id ; ■< Получаетданные пользователя,
i f (!uid) return next(); выполнившего вход, от Redis.
U ser.get(uid, (e rr, user) => {
i f (e rr) return n e x t(e rr);
req.user = re s .lo c a ls .u s e r = user;
next(); Предоставляет доступ кданным
}); пользователя объекту запроса.
};
В листинге 6.29 показано, как вклю чить этот ком понент в ф айл app.js.
Листинг 6.30. Разметка CSS, добавляемая в файл style.css для оформления меню
#menu {
position: absolute;
top: 15px;
rig h t: 20px;
fo n t-s iz e : 12px;
color: #888;
}
#menu .nam e:after {
content: ' - ';
}
#menu a {
tex t-d eco ratio n : none;
m argin-left: 5px;
color: black;
}
В следующем разделе вы узнаете, как создать открытый R EST A PI для прилож ения.
О про екти р о ван и е A P I, с пом ощ ью которого пол ьзовател и см огут отображ ать,
вы водить список, удалять и публиковать записи;
Ч тобы продем онстрировать, как а у тен ти ф и кац и я ин тегри руется в прилож ение,
мы воспользуем ся пакетом basic-auth.
Проектирование API
П реж де чем браться за реализацию , ж елательно спланировать необходимые м арш
руты. В наш ем при лож ении R E S T -совм естимы й A PI будет снабж аться преф иксом
/ a p i , но это всего лиш ь реш ение из области проектирования, которое вы мож ете
изм енить — например, использовать поддомен вида http://api.myapplication.com.
С ледую щ ий фрагмент демонстрирует, почему ф ункции обратного вы зова стоит вы
делить в отдельны е м одули N ode (вм есто определения их во встроенном ф ормате
с вы зовам и a p p . метод()). П ростой список м арш рутов дает четкое представление
о том, что вы и ваш а группа р еал и зо вал и и где находятся р еал и зац и и обратны х
вызовов:
Реализация маршрутизации
Запустите прилож ение и протестируйте его в программ е ком андной строки cU RL.
Тестирование R E S T -аутен ти ф и каци и при лож ения продем онстрировано в следую
щем ф рагменте. Учетные данны е передаю тся в U R L tobi:ferret, по которы м cU R L
строит поле заголовка A uthorization:
class User {
// ...
toJSON() {
return {
id: th is .i d ,
name: this.name
};
}
Если метод .toJSON сущ ествует в объекте, он будет использоваться вы зовам и JSON.
s t r i n g i f y для получения ф орм ата JS O N . Е сли бы приведенны й выш е запрос cU R L
был вы дан повторно, на этот раз бы ли бы получены только свойства id и name:
{
"id": "1",
"name": "tobi"
}
Добавление записей
Процессы добавления записи с форм ы H T M L и через A PI практически идентичны,
поэтом у в данном случае стоит снова воспользоваться уж е реализованной логикой
м арш рута e n t r ie s .s u b m it ( ) .
Впрочем, при добавлении записей в логике м арш рута храни тся им я пользователя,
а запись добавляется к прочей информ ации. По этой причине необходимо м оди ф и
цировать ком понент загрузки данны х пользователя д ля заполнен ия инф орм ации,
загруженной промежуточным компонентом b a sic-au th . Промежуточный компонент
b a sic -a u th возвращ ает эту информацию , которую можно присвоить req.rem oteU ser.
С оответствую щ ая проверка в ком поненте загрузки данны х пользователя в ы п ол
няется достаточно прям олинейно; изм ените определение m o d u le .e x p o rts в ф айле
middleware/user.js, чтобы ком понент загрузки пользователей работал с API:
Впрочем, необходим о внести еще одно изм ен ен ие — ответ, совм ести м ы й с A PI,
вм есто п ер ен ап р авлен и я на домаш ню ю стран ицу при лож ен и я. Д л я добавления
этой ф ункц иональн ости приведите вы зов e n tr y .s a v e в ф айле routes/entries.js к сле
дую щ ему виду:
Так как м арш рутная логика будет работать с записям и, в начало ф айла routes/api.js
следует вклю чить м одель Entry:
const Entry = re q u ire ('../m o d e ls /e n try ');
П осле чего добавьте м арш рутную логику из следую щ его ф рагм ента в ф айл routes/
api.js. Р азл и ч и я меж ду этой логикой и аналогичной логикой из routes/entries.js от
раж аю т тот факт, что вместо визуализац ии ш аблона строится разм етка JSO N :
[
{
"username": "rick",
" t i t l e " : "Cats c a n 't read minds",
"body": "I think you're wrong about the cat th in g ."
},
{
"username": "mike",
" t i t l e " : "I think my cat can read my mind",
"body": "I think cat can hear my thoughts."
},
Зн ач ен и е qvalue (q= 0.5 в данном при м ере) указывает, что, хотя ф орм ат te x t/h tm l
указан на втором месте, он на 50% предпочтительнее ф орм ата t e x t / p l a i n . Express
разбирает эту информацию и предоставляет нормализованны й массив req .accepted:
);
6.2. Express 199
});
re s .e n d ('< /e n trie s > ');
}
})
});
};
res.form at({
j s °n: () => {
re s.se n d (e n trie s);
},
xml: () => {
re s .w rite ('< e n trie s > \n ');
en tries.fo rE ach ((en try ) => {
re s.w rite (
<entry>
< title > $ { e n try .title } < /title >
<body>${entry.body}</body>
<username>${entry.username}</username>
</entry>
);
});
re s .e n d ('< /e n trie s > ');
}
})
xml: () => {
re s .re n d e r('e n trie s /x m l', { e n trie s : e n trie s });
}
})
Теперь все готово для тестирования X M L -версии API. Введите следующую команду
в ком андной строке, чтобы просм отреть вы вод в ф орм ате XML:
6.3. Заключение
О C o n n ect — ф рейм ворк H TTP, которы й позволяет строить цепочки пром еж уточ
ны х ком понентов до и после обработки запросов.
Н а рис. 7.1 показано, как логика ш аблонизации вписы вается в общую архитектуру
M V C -прилож ения. Ф ай л ы ш аблонов обычно содерж ат заполни тели для значений
Рис. 7.1. Последовательность операций при работе МѴС-приложения и его взаимодействие с уровнем шаблона
7.1. Поддержка чистоты кода путем шаблонизации 203
Запи си блога читаю тся из текстового файла, отформатированного так, как фрагмент
из ф айла entries.txt (листинг 7.1). С им волы — показываю т, где заканчивается одна
запись и начинается другая.
});
en trie s.p u sh (e n try );
});
return e n trie s ;
}
const e n trie s = g e tE n trie s();
co n so le .lo g (e n trie s);
} )/
output += '</body></html>';
return output;
}
npm in s t a l l ejs
С ледую щ ий ф рагм ент загруж ает ш аблон из ф айла, а затем определяет новую вер
сию ф ун кц и и blogPage, которая теперь использует ш аблонизатор EJS (о нем мы
поговорим в разделе 7.2):
206 Глава 7. Шаблонизация веб-приложений
Х отя ш аб лони заторы м огут сделать ш аблоны более «чисты м и», возм ож но, вам
не захочется трати ть врем я на изучени е альтерн ати вны х способов зап и си H T M L
и CSS. В конце концов, о кон чательное реш ен ие зави си т от ваш их л и ч н ы х п р ед
почтений.
В оставш ейся части главы мы поговорим о том, как встроить ш аблонизацию в ваши
N o d e-прилож ения, на прим ере трех популярны х ш аблонизаторов:
7.2. Шаблонизация с EJS 207
О ш аблонизатор Pug.
в больш инстве случаев в качестве второго парам етра мож но передавать сам к о н
текст, как в следую щ ем вы зове render:
Е сли вы передаете кон текст E JS -ш аблону неп осредствен но во втором аргум енте
вы зова render, убедитесь, что при и м енован ии кон текстн ы х зн ач ен и й не и с п о л ь
зу ю тся следую щ и е клю чевы е слова: cache, c l i e n t , c lo s e , com pileD ebug, debug,
file n a m e , open и л и sco p e. О н и за р е зе р в и р о в ан ы с целью и зм е н ен и я н астроек
ш аблонизатора.
Экранирование символов
В проц ессе в и зу а л и зац и и E JS экранирует (escap es) все сп ец и альн ы е сим волы
в кон текстн ы х значениях, зам ен яя их кодам и H T M L -сущ ностей. Это позволяет
предотвратить атаки м еж сайтового вы п олн ен и я сценариев (C ro ss-S ite Scripting,
X SS), в ходе которы х зл о у м ы ш л ен н и к и пы таю тся в качестве дан ны х отправить
вредоносны й код J a v a S c rip t в надежде, что этот код будет вы полнен при выводе
данны х на экран в браузере другого пользователя. С ледую щ ий ф рагм ент д ем он
стрирует, как работает м еханизм экранировани я сим волов в EJS:
И так, вы ознако м и л и сь с основам и EJS; давай те посм отрим , как EJS позволяет
упростить управление представлением данных.
Ч тобы проверить, как работает кэш ирование, изм ените вызов E JS-ф ункц ии render
из преды дущ его примера:
О братите вним ание на то, что в параметре filenam e не обязательно указы вать файл.
В данном случае мож ет при м ен яться уникальное значение, определяю щ ее, какой
ш аблон вы визуализируете.
cd /your/w orking/directory
curl -O h ttp s ://ra w .g ith u b u s e rc o n te n t.c o m /tj/e js /m a ste r/lib /e js.js
П осле загр у зки ф ай л а ejs.js EJS м ож но исп ользовать в кли ен тском коде. В л и с
тинге 7.7 представлено простое клиентское прилож ение с ш аблоном EJS. Сохранив
код в ф айле с именем index.html, вы смож ете откры ть его в браузере и просмотреть
результаты.
Д алее представлен простейш ий прим ер исп ользован ия H ogan в при лож ении N ode
д л я ви зу ал и зац и и простого ш аблона на основе контекста. П ри его вы полнен ии
вы водится текст «H ello tem plate!».
Теперь, когда вы знаете, как с помощ ью H ogan обрабаты вать ш аблоны M ustache,
перейдем к рассм отрению тегов, предлагаем ы х стандартом M ustache.
var context = {
students: [
{ name: 'Jane Narwhal', age: 21 },
{ name: 'Rick LaRue', age: 26 }
]
};
Д опустим , вы реш или создать ш аблон, которы й вы водит на экран данны е каждого
студента в отдельном H T M L -абзаце:
{{#students}}
<p>Name: {{name}}, Age: {{age}} years old</p>
{{/students}}
вы зова ф ун кц и й , а не перебором элем ентов м ассива. Такие секц ии назы ваю тся
лямбда-секциями (sectio n lam bda).
В л и сти н ге 7.8 при веден при м ер того, как с пом ощ ью л я м б д а-сек ц и и д обавить
поддерж ку парсера M arkdow n в м еханизм визуализац ии ш аблона. В этом примере
используется модуль github-flavored-m arkdow n, которы й нуж но установить, введя
в ком андной строке команду:
npm in s t a l l github-flavored-markdown --dev
С помощ ью лям бда-секций вы смож ете легко реализовать в своих ш аблонах такие
вещи, как кэш ирование и преобразования.
Н априм ер, в листинге 7.9 с помощ ью ком понента код ш аблона, прим еняем ы й для
вы вода на экран данны х о студентах, вы деляется из главного шаблона.
Ч тобы вы лучш е поняли, как это работает, посмотрим, как представляется следу
ю щ ий ф рагм ент H TM L:
<html>
<head>
<title>W elcome</title>
</head>
<body>
<div id="main" class="content">
<strong>"Hello world!"</strong>
</div>
</body>
</html>
html
head
t i t l e Welcome
body
div.content#main
strong "Hello world!"
В Pug, как и в EJS, допускается внедрение кода Ja v a S c rip t как на стороне сервера,
так и на стороне клиента. P u g предлагает такж е дополнительны е механизмы , т а
кие как наследование ш аблонов и прим еси (m ixins). Б л агодаря прим есям можно
определять просты е м ногократно и сп ользуем ы е м ин и-ш аблоны , п ред н азн ач ен
ны е д ля представления в разм етке H T M L наиболее востребованны х визуальны х
элементов, таких как списки и поля. П рим еси (m ixins) напоминаю т прим еняем ы е
в H ogan.js ком п оненты , у п о м и н ав ш и еся в преды дущ ем разделе. Н асл ед ован и е
ш аблонов упрощ ает организацию ш аблонов Pug, необходимы х д ля визуализац ии
одной страницы H T M L в нескольких ф айлах. Д алее мы подробно рассм отрим эти
механизмы . Ч тобы установить P u g в папку N ode-прилож ения, в ком андной строке
введите следую щ ую команду:
О основны е принципы Pug: как задаю тся им ена классов, атрибуты и блочное рас
ш ирение;
div.content.sidebar#featured_content
.content.sidebar#featured_content
М ожно такж е указы вать атрибуты, которые не требую т значений. С ледую щ ий п ри
мер кода P u g дем онстрирует определение ф орм ы H T M L , которая вклю чает в себя
элемент s e l e c t вместе с предварительно вы бранны м параметром:
select
option(value='C heese') Cheese
option(value='T ofu', selected) Tofu
tex tarea
| This is some d efau lt te x t
| th a t the user should be
| provided with.
Е сли тег H T M L (такой, как s t y l e и ли s c r i p t ) приним ает только текст (то есть
вл о ж е н и е эл ем ен то в H T M L не д о п у с к а е т с я), си м вол ы | м ож н о оп устить, как
в следую щ ем примере:
sty le
h1 {
fo n t-siz e : 6em;
color: #9DFF0C;
}
Н аличие двух разны х способов вы раж ени я длинного и короткого контента тегов
упрощ ает создание элегантны х ш аблонов Pug. К роме того, в P ug поддерж ивается
альтернативны й м еханизм опи сан ия влож енности, которы й назы вается блочным
расширением (block expansion).
ul
li
a (h re f= 'h ttp ://n o d e js .o r g /') Node.js homepage
li
a (h re f= 'h ttp ://n p m js .o rg /') NPM homepage
li
a (h re f= 'h ttp ://n o d e b its .o r g /') Nodebits blog
7.4. Шаблоны Pug 219
ul
l i : a (h re f= 'h ttp ://n o d e js .o r g /') Node.js homepage
l i : a (h re f= 'h ttp ://n p m js .o rg /') NPM homepage
l i : a (h re f= 'h ttp ://n o d e b its .o r g /') Nodebits blog
Теперь, когда мы узнали, как в P u g представляется разм етка, давайте выясним, как
интегрировать ш аблон P u g в веб-прилож ение.
В этом прим ере кода кон струкция #{message} в ш аблоне определяет заполнитель,
которы й зам ен яется контекстны м значением.
ци клы f o r и объ явл ен и я var. Н о преж де чем углубиться в детали, давайте рассм о
трим прим ер ш аблона, визуализирую щ его список контактов. Э тот прим ер хорош о
иллю стрирует то, как логика P u g мож ет при м ен яться в прилож ении:
h3.contacts-header My Contacts
i f contacts.len g th
each contact in contacts
- var fullName = contact.firstN am e + ' ' + contact.lastName
.contact-box
p fullName
i f co n tact.isE d itab le
p: a (h re f= '/e d it/+ c o n ta c t.id ) Edit Record
p
case c o n ta c t.sta tu s
when 'A ctive'
strong User is activ e in the system
when 'In a c tiv e '
em User is inactive
when 'Pending'
| User has a pending in v ita tio n
else
p You curren tly do not have any contacts
Префикс Вывод
= Выходные данные экранируются (для не заслуживающих доверия
или непредсказуемых значений, для защиты от XSS-атак)
!= Выходные данные не экранируются (для заслуживающих доверия
или предсказуемых значений)
- count = 0
count = 0
К ак и упом януты е ранее ин струкции с преф иксом -, ин струкции без преф иксов не
порож даю т никаких данных.
- messages.forEach(message => {
p= message
- })
Вот как вы глядит эквивалент преды дущ его примера, в котором используется и н
струкци я each:
- n = Math.round(Math.random() * 1) + 1
- i f (n == 1) {
sc rip t
alert('Y o u w in !');
- }
Условные вы раж ени я P u g такж е м огут запи сы ваться в более чистой альтернатив
ной форме:
- n = Math.round(Math.random() * 1) + 1
i f n == 1
sc rip t
alert('Y o u w in !');
Если в условиях использую тся отрицан ия (наприм ер, i f (n != 1)), мож но восполь
зоваться клю чевы м словом u n le ss:
- n = Math.round(Math.random() * 1) + 1
unless n == 1
sc rip t
alert('Y o u w in !');
Инструкция case
В P u g такж е поддерж ивается условная кон струкция case, напом инаю щ ая sw itch:
она позволяет вы брать в ш аблоне нуж ны й вариант вы водим ы х данны х д ля одного
из нескольких возм ож ны х сценариев.
case re su lts.le n g th
when 0
p No re s u lts found.
when 1
p= re su lts[0 ].c o n te n t
default
each re s u lt in re s u lts
p= r e s u l t . t i t l e
7.4. Шаблоны Pug 223
Н ас л ед о в а н и е ш аб л о н о в — один из м ех ан и зм о в с т р у к т у р и р о в а н и я . В р ам к ах
этой кон ц еп ц и и ш аблоны рассм атр и ваю тся по анал оги и с классам и в парадигм е
объектно-ориентирован ного програм м ировани я. О дин ш аблон м ож ет расш ирять
д р у го й ш аблон, к о то р ы й в свою о черед ь м о ж ет р а с ш и р я ть ещ е один ш аблон.
К оличество у ровн ей н асл едо ван и я огр ан и ч и вается лиш ь соображ ен и ям и ц е л е
сообразности.
В качестве простого прим ера давайте вы ясним , как путем наследования ш аблонов
создать базовую H T M L -обертку д ля контента страницы. В рабочем каталоге соз
дайте папку template, в которой будет находиться P u g -ф ай л примера. Д л я ш аблона
страницы создается ф айл layout.pug со следую щ ей разметкой:
html
head
block t i t l e
body
block content
Затем в папке ш аблона, находящ ейся в рабочем каталоге, создайте ф айл page.pug.
Э тот ф айл ш аблона заполняет блоки t i t l e и co n ten t:
extends layout
block t i t l e
t i t l e Messages
block content
each message in messages
p= message
Н аконец, добавьте програм м ную логику из листинга 7.10 (это м оди ф и цирован ная
версия преды дущ его прим ера данного раздела), которая вы водит результаты п р и
м енени я ш аблона и показы вает наследование в действии.
А теперь давайте рассм отрим другое прим енение м еханизм а наследован ия ш абло
нов: вставку блоков в начало и в конец шаблона.
html
head
- const baseUrl = "h ttp ://a ja x .g o o g le a p is.c o m /a ja x /lib s/jq u ery u i/1 .8 /"
block t i t l e
block sty le
7.4. Шаблоны Pug 225
block s c rip ts
body
block content
Включение шаблонов
Ещ е одним инструм ентом организации ш аблонов яв л яетс я ком анда P u g in clu d e .
Э та ком анда встраивает в ш аблон контент другого ш аблона. Если в ш аблон layout.
pug из преды дущ его прим ера добавить строку in c lu d e f o o te r , получится следую
щ ий шаблон:
html
head
block t i t l e
block sty le
block s c rip ts
s c rip t(src = '//a ja x .g o o g le a p is .c o m /a ja x /lib s/jq u e ry /1 .8 /jq u e ry .js')
body
block content
include foo ter
Рис. 7.3. Механизм include в Pug предоставляет простой способ включения контента
одного шаблона в другой шаблон во время визуализации
П редполож им , например, что наш е при лож ение обрабаты вает следую щ ую стр у к
туру данных:
const students = [
{ name: 'Rick LaRue', age: 23 },
{ name: 'Sarah Cathands', age: 25 },
{ name: 'Bob Dobbs', age: 37 }
];
7.5. Заключение
О Ш аблони заторы помогаю т упорядочить прикладную логику и средства в и зу
ализации.
О В N ode поддерж иваю тся некоторы е популярны е ш аблонизаторы , вклю чая EJS,
H ogan.js и Pug.
Р еляц и он н ы е базы данных, базирую щ иеся на м атем атических кон цепц иях р е л я
ц и онной алгебры и теори и м нож еств, известны с 1970-х годов. Схема (schem a)
определяет ф орм ат различны х типов данны х и отнош ения, сущ ествую щ ие меж ду
этим и типам и. Н априм ер, при построении социальной сети мож но создать типы
данны х User и P o st и определить отнош ения «один ко многим» меж ду User и Post.
Далее на язы ке SQ L (S tru ctu red Q uery Language) формулирую тся запросы к данным
типа «П олучить все сообщ ения, принадлеж ащ ие пользователю с идентиф икатором
123», или на SQL: SELECT * FROM p o st WHERE user_id=123.
8.2. PostgreSQL
M yS Q L и P o stg reS Q L (P o stg re s) остаю тся самы ми поп улярн ы м и реляционн ы м и
б азам и дан н ы х д л я п р и л о ж ен и й N ode. Р а зл и ч и я м еж ду р ел я ц и о н н ы м и базам и
данны х в основном эстетические, поэтом у этот раздел в равной степени относится
8.2. PostgreSQL 229
brew update
brew in s t a ll postgres
Если в ваш ей системе поддерж ка Postgres уже установлена, вы мож ете столкнуться
с проблем ам и при попы тке обновления. В ы полните ин струкции д ля своей п л ат
ф ормы, чтобы произвести м играцию сущ ествую щ их баз данных, либо полностью
сотрите каталог базы данных:
Э ти ком анды запускаю т дем она Postgres. Д ем он долж ен запускаться каж ды й раз,
когда вы перезагруж аете компьютер. Возможно, вам строит настроить автоматиче
скую загрузку демона Postgres при запуске; во м ногих сетевых руководствах можно
найти описание этого процесса д ля ваш ей операционной системы.
createdb a r tic le s
230 Глава 8. Хранение данных в приложениях
Е сли операция заверш ается успеш но, ком анда ничего не выводит. Если база д ан
ны х с указанны м именем уж е сущ ествует, ком анда ничего не делает и сообщ ает об
ошибке.
Ч тобы удали ть все данны е из сущ ествую щ ей базы данны х, вы полни те команду
dropdb с терминала, передав ей им я базы данны х в аргументе:
dropdb a r tic le s
npm in s ta l l pg --save
К огда сервер P ostgres заработает, база данны х будет создана, а пакет pg устан ов
лен, вы смож ете переходить к использованию базы данны х из Node. П реж де чем
вводить какие-либо ком анды к серверу, необходимо создать подклю чение к нему,
как показано в листинге 8.1.
8.3. Knex
М ногие разработчики предпочитаю т работать с ком андам и SQ L в своих п ри лож е
н и ях не напрямую , а через абстрактную надстройку. Это ж елание вполне понятно:
кон катенаци я строк в ком анды SQ L м ож ет быть громоздким процессом, которы й
услож няет понимание и сопровож дение запросов. Сказанное особенно справедливо
по отнош ению к я зы к у Ja v a S c rip t, в котором не было син такси са представления
м ногострочны х строк до появл ен и я в ES2015 ш аблонны х литералов (см. h ttp s://
developer.mozilla.org/en/docs/Web/favaScript/Reference/Template_literals). Н а рис. 8.1
показана статистика K nex с количеством загрузок, доказы ваю щ их популярность.
16 dependencies version 0 11 7
278 dependents ^ updated 19 days ago
О PostgreS Q L ;
О M SSQ L;
О M ySQ L;
О M ariaD B ;
О SQ Lite3;
О Oracle.
В табл. 8.1 сравниваю тся способы генерирования ком анды INSERT в зависим ости
от вы бранной базы данных.
Таблица 8.1. Сравнение команд SQL, сгенерированных Knex, для разных баз данных
K nex поддерж ивает обещания (prom ises) и обратны е вы зовы в стиле Node.
234 Глава 8. Хранение данных в приложениях
d b ( 'a r t i c l e s ')
. s e l e c t ( 't i t l e ')
.where({ t i t l e : 'Today's News' })
.th e n (a rtic le s => {
c o n so le .lo g (a rtic le s);
});
П о ум олчанию запросы K nex возвращ аю т обещ ания, но они такж е поддерж иваю т
соглаш ения обратного вы зова N ode с использованием .asC a llb ac k :
d b ( 'a r t i c l e s ')
. s e l e c t ( 't i t l e ')
.where({ t i t l e : 'Today's News' })
.asC allb ack ((err, a r tic le s ) => {
i f (e rr) throw e rr;
c o n so le .lo g (a rtic le s);
});
В листинге 8.6 sqlite исп ользуется д ля р еал и зац и и простой м одели A r tic le . С о
храните ф айл под именем db.js; он будет исп ользоваться в листинге 8.7 д ля в за и
м одействия с базой данных.
const db = knex({
c lie n t: 's q l i t e 3 ',
connection: {
filename: 't l d r . s q l i t e ' Выбор этого режима по умолчанию
}, лучше работает при смене
useNullAsDefault: true подсистемы баз данных.
});
module.exports = () => {
return d b .sch em a.createT ab leIfN o tE x ists('articles', ta b le => {
ta b le .in c re m e n ts ('id ').p rim a ry (); •<- Определяет первичный ключ с именем
t a b l e . s t r i n g ( 't i t l e ') ; «id», значение которого автоматически
t a b le .te x t( 'c o n te n t') ; увеличивается при вставке.
});
};
8.3. Knex 235
m odule.exports.A rticle = {
a ll ( ) {
return d b ( 'a r t i c l e s ') .o r d e r B y ( 'ti t l e ') ;
},
fin d (id ) {
return d b ('a rtic le s ').w h e re ({ id } ) . f i r s t ( ) ;
},
create(d ata) {
return d b ( 'a r tic le s ') .in s e r t( d a ta ) ;
},
d ele te (id ) {
return d b ('a rtic le s ').d e l().w h e re ({ id });
}
};
S Q L ite требует м ин им альной настройки: вам не нуж но загруж ать демон сервера
или создавать базы данны х за пределам и прилож ения. SQ L ite записы вает все д ан
ные в один ф айл. В ы полнив преды дущ ий код, вы увидите, что в текущ ем каталоге
п о яви л ся ф айл articles.sqlite. Ч тобы уничтож ить базу данны х SQ L ite, достаточно
удалить всего один файл:
rm a r t i c l e s .s q l i te
SQ L ite такж е поддерж ивает реж им работы в памяти, при котором запись на диск
вообщ е не осущ ествляется. Э тот реж им обычно используется д ля ускорен ия вы
п ол н ен и я авто м атизи рованны х тестов. Д л я н астройки реж и м а работы в пам яти
и сп ользу ется специ альное и м я ф ай л а :memory:. П ри откры тии н ескольких под
клю чений к ф ай л у :memory: каж дое подклю чение получает собственную и зо л и р о
ванную базу данных:
const db = knex({
c lie n t: 's q l i t e 3 ',
connection: {
filename: ':memory:'
},
useNullAsDefault: true
});
236 Глава 8. Хранение данных в приложениях
npm in s ta l l pg --save
createdb a r tic le s
Все изм енения кода, необходимые д ля использования новой базы данных, вносятся
в кон ф и гурац ии Knex; в остальном A P I и схема исп ользован ия идентичны:
const db = knex({
c lie n t: 'p g ',
connection: {
database: 'a r t i c l e s '
}
})
С тоит заметить, что в реальной ситуации вам такж е придется вы полнить миграцию
всех сущ ествую щ их данных.
О t a b l e . i n c r e m e n t s ( 'i d ') . p r i m a r y ( ) ;
О t a b l e . i n t e g e r ( 'i d ') . p r i m a r y ( ) ;
не при водя к видим ы м ош ибкам. Е сли вы реш или перейти на другую базу данных,
обязательно проведите тщ ательное тестирование.
О PostgreS Q L поддерж ивает более вы разительны е типы данных, вклю чая массивы,
JS O N и типы, определяем ы е пользователем.
И сходн ы й вы бор базы дан ны х вряд ли зн ачительно повл и яет на успех проекта,
поэтому не стоит слиш ком сильно беспокоиться по поводу этого реш ения. Позднее
вы сможете переклю читься на другую базу данных, но, скорее всего, возмож ностей
Postgres будет достаточно для лю бых потребностей в ф ункциональности и м асш та
бируемости. О днако если вы вы бираете м еж ду нескольким и базам и данных, вам
стоит позн аком иться с идеей гарантий ACID.
8.5.1. Атомарность
Атомарная транзакц ия не мож ет быть вы полнена частично: либо вся операция за
верш ается, либо база данны х остается без изм енений. Н апример, если транзакц ия
заклю чается в удален ии всех ком м ентариев некоторого пользователя, то либо все
ком м ентарии будут удалены, либо не будет удален ни один. С итуация, при которой
часть ком м ентариев удаляется, а другая часть остается, невозмож на.
8.5.2. Согласованность
П р и зав ер ш ен и и усп еш н о й т р а н за к ц и и долж н ы собл ю даться все огран и ч ен и я
согласованности (целостности данны х), определенны е в системе. П рим еры таких
ограничений — уникальность первичны х клю чей, соответствие данны х некоторой
схеме и т. д. Т ранзакции, которы е приводят к некорректном у состоянию данных,
обы чно приводят к откату транзакций, хотя незначительны е проблем ы могут р а з
реш аться автом атически (н ап ри м ер, приведени е дан н ы х к п рави льн ой ф орм е).
Э тот вид со гласован ности не следует путать с согласован ностью ( C ) в теорем е
CAP, которая относится к единству представления данны х для всех пользователей
распределенного хранилищ а.
8.6. NoSQL 239
8.5.3. Изоляция
Изолированные тр анзакц ии долж ны приводить к одинаковому результату н езави
симо от их параллельного и ли последовательного прим енения. Уровень изоляции,
предоставляем ы й системой, напрям ую влияет на ее способность вы полнять парал
лельны е операции. Н аи вн ая схема и зо л яц и и основана на прим енении глобальной
блокировки ; вся база данны х блокируется на врем я транзакции, в результате чего
все транзакции ф актически вы полняю тся последовательно. Такая схема предостав
л яет сильны е гарантии изоляци и, но она патологически неэф ф ективна: тр ан зак
ции, работаю щ ие с полностью несвязанны м и наборам и данных, блокирую тся без
необходим ости (напри м ер, добавление пользователем ком м ентария в идеале не
долж но мешать обновлению п роф иля другим пользователем). Н а практике системы
предоставляю т разны е уровни изо л яц и и с прим енением схем блокировки с разной
избирательностью (н а уровне таблиц, строк или полей). Б олее слож ны е системы
могут даж е оптим истически пы таться проводить все транзакц ии с м иним альной
б локировкой, а затем заново п о вто р ять тр ан зак ц и и со сни ж ением детал и зац и и
в случае вы явл ен и я конф ликтов.
8.5.4. Устойчивость
Устойчивость транзакции определяет степень, в которой ее эф ф ект гарантированно
сохранится даж е при перезапуске, сбое питания, системны х ош ибках и даж е сбоях
об орудовани я. Н априм ер, при лож ение, использую щ ее S Q L ite в реж и м е работы
в памяти, не обладает устойчивостью транзакций; все данны е теряю тся при выходе
из процесса. С другой стороны, S Q L ite в реж им е запи си данны х на диск будет об
ладать хорош ей транзакц ионн ой устойчивостью , потому что данны е сохраняю тся
даж е после перезапуска маш ины.
8.6. NoSQL
Х ранилищ а данных, которые не вписываю тся в реляционную модель, объединяются
под термином NoSQL. Так как в наш и дни некоторы е базы данны х N oSQ L поддер
ж иваю т SQ L, смы сл терм ина N oSQ L ближ е к определению «нереляционны й» или
придум анном у задним числом сокращ ению «N ot O nly SQ L» («не только SQ L»).
240 Глава 8. Хранение данных в приложениях
П одмнож ество парадигм и прим еры баз данных, которые мож но отнести к NoSQL:
З а более подробны м списком баз дан ны х N oS Q L обращ айтесь по адресу h ttp ://
nosql-database.org/.
К онцепции N oSQ L бывает трудно усвоить, если вы работали только с реляционными
базами данных, потому что применение N oSQ L часто противоречит установивш ейся
практике: отсутствие определенных схем. Дублирование данных. Слабое соблюдение
ограничений. С истем ы N oSQ L берут на себя обязанности, обычно закрепляем ы е за
базам и данны х, и вы водят их в область ответственности прилож ения. Н а первы й
взгляд все это вы глядит сомнительно.
Д анны е N oS Q L чащ е д ен орм али зирую тся по ум олчанию , и весь этап м од ел и ро
вани я предм етной области м ож ет быть полностью опущен. Такой подход предот
вращ ает чрезм ерное техническое услож нение м одели данны х, позволяет быстрее
отрабаты вать изм ен ен ия и в целом приводит к более простой и производительной
архитектуре.
горизон тальное м асш таби рование обладает нам ного больш им потенц иалом для
роста при д о бавл ен и и новы х процессов и новы х м аш ин. З а все это при ходи тся
платить слож ностью у п равлени я больш им количеством «подвиж ны х частей». Все
растущ ие системы со временем достигаю т точки, в которой приходится проводить
горизонтальное м асш табирование.
8.8. MongoDB
M ongoD B — докум ентно-ориентированная распределенная база данных, исклю чи
тельно популярная среди разработчиков Node. О на стоит на первом месте в модном
технологическом стеке M EAN (M ongoD B , Express, Angular, N ode) и часто становится
одной из первы х баз данных, с которой сталкиваю тся лю ди в начале работы с Node.
Н а рис. 8.2 показано, насколько популярен модуль m ongodb в npm.
M ongoD B при влекает более чем справедливую долю кри ти ки ; несм отря на это,
M ongoD B остается надеж ны м хр ан и ли щ ем дан ны х д л я м ногих разработчиков.
П оддерж ка M ongoD B развернута во м ногих видны х ком паниях, вклю чая Adobe,
LinkedIn и eBay, и даже используется в компоненте Больш ого адронного коллайдера
в Е вропейском центре ядерн ы х исследований (C E R N ).
brew in s t a l l mongodb
О бработчику успеш ного заверш ен ия передается экзем пляр кли ен та базы данных,
из которого вы полняю тся все ком анды базы данных.
Такие операции, как поиск, вставка и удаление, обычно сущ ествую т в нескольких
разнови дн остях — в зависи м ости от того, работаете л и вы с одним или м ногим и
значениям и. П римеры:
Вызов insertM any работает аналогично, если не считать того, что он получает массив
из нескольких докум ентов. О твет in sertM any будет содерж ать массив in s e r te d I d s
в порядке передачи докум ентов вместо одного значения in s e r te d I d .
Запр о сы использую тся для поиска объектов по их уни кальном у значению _id:
О $ in — присутствует в массиве;
module.exports = () => {
return MongoClient
.connect('m ongodb ://lo calh o st:2 7 0 1 7 /articles')
.th e n ((c lie n t) => {
db = c lie n t;
});
};
m odule.exports.A rticle = {
a ll() {
return d b .c o lle c tio n ( 'a r tic le s 2 ') .f in d ( ) .s o r t( { t i t l e : 1 }).toA rray();
8.8. MongoDB 245
},
find(_id) { Добавляет поддержку передачи _id
i f (typeof _id !== 'o b je c t') _id = ObjectID(_id); -<—I в формате String иObjectID.
return d b .c o lle c tio n ('a rtic le s 2 ').fin d O n e ({ _id });
},
create(d ata) {
return d b .c o lle c tio n ('a rtic le s 2 ').in s e rtO n e (d a ta , { w: 1 });
},
d elete(_ id ) {
i f (typeof _id !== 'o b je c t') _id = ObjectID(_id);
return d b .c o lle c tio n ('a rtic le s 2 ').d e le te O n e ({ _id }, { w: 1 });
}
};
С ледую щ ий ф рагм ент показы вает, как исп ользовать л и сти н г 8.10 (listin g 8_1 0 /
index.js в прим ере кода):
В этом ф рагменте обещ ание из листинга 8.10 используется для подклю чения к базе
данны х, после чего объект создается методом c r e a te класса A r tic le . П осле этого
ф рагм ент загруж ает все статьи и вы водит их на консоль.
operator: equal
expected: 577f6b45549a3b991e1c3c18
actu al: 577f6b45549a3b991e1c3c18
operator: equal
expected:
{ _id: 577f6b45549a3b991e1c3c18, t i t l e : 'attractive-m oney' . . . }
actu al:
{ _id: 577f6b45549a3b991e1c3c18, t i t l e : 'attractive-m oney' . . . }
a r tic le 1 ._ id .e q u a ls (a rtic le 2 ._ id );
S trin g (a rtic le 1 ._ id ) === S trin g (a rtic le 2 ._ id ); I Выдает исключение, если усл°вие
asse rt.d e e p E q u al(a rtic le 1 ._ id , a r tic le 2 ._ id ) ; -<— ' не выполняется.
Там, где это возможно, следует поддерживать формат BSON; затраты на марш ализа-
цию в строковую ф орму и обратно снижаю т вы игрыш по быстродействию, которого
M ongoD B стрем и тся достичь за счет работы с кл и ен тск и м и и д ентиф икаторам и
в ф орм ате BSON. З а дополнительной инф орм ацией о ф ормате B SO N обращ айтесь
по адресу http://bsonspec.org/.
П о сл е то го к а к р е п л и ц и р о в а н н ы й н а б о р за р аб о т ае т , M o n g o D B н ео б х о д и м о
п ро в ести н ек о то р у ю и н и ц и а л и за ц и ю . Н ео б х о д и м о п о д к л ю ч и ться к п о р ту э к
з е м п л я р а , к о т о р ы й ст ан е т п е р в ы м первичным у зл о м (2 7 0 1 7 по у м о л ч ан и ю ),
и вы зв ать м етод r s . i n i t i a t e ( ) , как показан о в л и сти н ге 8.12. З а т ем необходим о
д обавить каж ды й эк зем п л яр как уч астн и ка реп л и ц и рован н ого набора. О братите
в н и м ан и е на н ео б х о ди м о сть п еред ачи им ен и хоста той м аш и н ы , к к оторой вы
подклю чаетесь.
248 Глава 8. Хранение данных в приложениях
К лиенты M ongoD B долж ны располагать инф орм ацией обо всех возм ож ны х участ
никах реплицированного набора при подклю чении, хотя не все участники долж ны
быть подклю чены в текущ ий момент. П осле подклю чения вы мож ете использовать
кли ен та M ongoD B , как обычно. В листинге 8.13 показано, как создать р еп л и ц и ро
ванны й набор с трем я участникам и.
const members = [
'${hostname}:27018',
'${hostname}:27017',
'${hostname}:27019'
];
M ongoC lient.connect('m ongodb://${m em bers.join(',')}/test?replSet=rs0') ///
.then(db => {
db.ad m in ().rep lS etG etS tatu s().th en (statu s => { / / /
c o n so le.lo g (statu s);
d b .clo se();
});
});
Е сли на лю бом из узл о в m ongod прои зойдет сбой, систем а п родолж ит работать
(п р и усло ви и вы п о л н ен и я по кр ай н ей м ере двух эк зем п л яр о в). Если сбой п р о
изой дет на первичном узле, то втори ч н ы й узел будет автом атически повы ш ен до
первичного.
успеш ной. Если значение не задано явно, по ум олчанию уровень запи си равен 1;
он гарантирует, что данны е бы ли записаны по край ней мере в один узел. М ожет
оказаться, что это значение не обеспечивает необходимого уровня защ иты к р и ти
ческих данных; если узел отклю чится до того, как данны е будут реплицированы
на других узлах, данны е могут быть потеряны .
d b .c o lle c tio n ('d a ta ').in s e rtO n e (d a ta , { w: 'm ajo rity ' });
К ак обычно, чем больш е защ иты вы добавляете, тем медленнее и сложнее становит
ся ваш а система. Э та проблем а не я в л яется специф ической только д ля M ongoD B;
она присущ а всем хранилищ ам данны х без исклю чения. И деального реш ен ия не
сущ ествует, и вам придется вы брать при ем лем ы й уровень ри ска д ля различны х
частей ваш его прилож ения.
Х ранилищ а «клю ч-значение» часто встречаю тся в частях прилож ения, критичны х
по бы стродействию . В идеале зн ачения располагаю тся в такую структуру, чтобы
д ля вы полнен ия задачи требовалось м ин им альное количество операций чтения.
Х ранилищ а «клю ч-значение» обладают более простой функциональностью запроса,
чем другие типы баз данных. В идеале слож ны е запросы долж ны обрабаты ваться
заранее; в противном случае они долж ны вы полняться в прилож ении, а не в базе
данных. Это ограничение долж но обеспечивать понятны е и предсказуем ы е хар ак
теристи ки бы стродействия.
8.10. Redis
Redis — популярное хранилищ е структур данны х в памяти. И хотя многие разработ
ч и ки считаю т Redis хранилищ ем «клю ч-значение», клю чи и зн ачения составляю т
лиш ь небольшое подмножество ф ункций Redis среди разнообразных полезны х базо
вых структур данных. Н а рис. 8.3 приведена статистика использования redis в npm.
О строки;
О хеши;
О списки;
О множ ества;
Redis такж е вклю чает в себя м ного других полезны х возмож ностей:
В этом разделе при водятся списки ком анд Redis. Н е стоит относиться к ним как
к справочникам ; они даю т лиш ь некоторое представление о том, что мож но делать
252 Глава 8. Хранение данных в приложениях
brew i n s t a ll redis
Э кзем п ляр кли ен та R edis создается ф ункц ией c r e a te C lie n t из npm -пакета redis:
Э кзем пляр клиента Redis расш иряет EventEm itter, поэтому вы можете присоединять
слуш ателей для различны х статусны х собы тий Redis, как показано в листинге 8.14.
Вы мож ете сразу начинать вводить команды д ля клиента; введенны е команды бу
ф еризую тся до готовности подклю чения.
О бработчик e r r o r срабаты вает при возни кн овен ии проблемы с подклю чением или
клиентом . Если при срабаты вании собы тия e r r o r обработчик не присоединен, про
цесс п р и л о ж ен и я вы дает ош ибку и аварийн о заверш ается; это свойство присущ е
всем объектам E ventE m itter в Node. Если при подклю чении происходит сбой, а об
работч и к e r r o r определен, то кли ен т R edis п ы тается восстан овить подклю чение.
8.10. Redis 253
Если указать сущ ествую щ ий ключ, значение будет заменено. Если вы попытаетесь
прочитать значение с несущ ествую щ им клю чом, значение будет равно n u ll; такое
обращ ение не считается ош ибкой.
С ледую щ ие ком анды могут исп ользоваться д ля чтен и я и изм ен ен ия значений:
О append;
О decr;
О decrby;
О get;
О g e tra n g e ;
О g e ts e t;
О in c r;
О in crb y ;
О in c r b y f lo a t;
О mget;
О mset;
О msetnx;
О p setex ;
О s e t;
О s e te x ;
О setn x ;
О se tra n g e ;
О s trle n .
254 Глава 8. Хранение данных в приложениях
О d e l;
О e x is ts ;
О rename;
О renamenx;
О s o r t;
О scan;
О type.
К лиент R edis незам етно преобразует числа, логические значения и даты в строки;
кром е того, он спокойно приним ает объекты буферов. П опы тка задать в качестве
зн ачения лю бой другой тип Ja v a S c rip t (наприм ер, O bject, A rray, RegExp) приводит
к выдаче предупреж дения, к котором у стоит прислуш аться:
В будущем это будет рассм атриваться как ошибка. П рилож ение долж но проследить
за тем, чтобы клиенту R edis передавались допустим ы е типы.
Такие ош ибки нередко про явл яю тся только в усл ови ях реальной эксплуатации.
О ни остаю тся незам еченны м и, если тестовы й пакет генерирует только м ассивы
с одним значением, типичные для усеченных тестовых данных. Будьте внимательны!
О b itc o u n t;
О b itfie ld ;
256 Глава 8. Хранение данных в приложениях
О b ito p ;
О s e tb it;
О b itp o s .
БУФЕРЫ
О hdel ;
О h e x is ts ;
8.10. Redis 257
О hget;
О h g e ta ll;
О hin crb y ;
О h in c r b y f lo a t;
О hkeys;
О hlen;
О hmget;
О hmset;
О h se t;
О hsetnx;
О h s tr le n ;
О h v a ls ;
О hscan.
c lie n t.lp u s h ( 'ta s k s ', 'P ain t the bikeshed r e d . ', re d is .p rin t);
c lie n t.lp u s h ( 'ta s k s ', 'P ain t the bikeshed g re e n .', re d is .p rin t);
c lie n t.lr a n g e ( 'ta s k s ', 0, -1, ( e rr, items) => {
i f (e rr) throw e rr;
item s.forEach(item => co n so le.lo g (' $ {item }'));
});
О s d iffs to re ;
О s in te r;
О s in te rs to re ;
О sismember ;
О smembers ;
О spop;
О srandmember ;
О srem;
О sunion;
О s u n io n s to re ;
О sscan.
Рис. 8.4. Каналы Redis предоставляют простое решение для стандартного сценария
передачи данных
se rv e r.liste n (3 0 0 0 );
Ч тобы использовать hiredis, просто установите пакет вместе с пакетом redis в вашем
прилож ении; пакет N ode redis обнаруж ит его и автом атически использует его при
следую щ ем запуске:
У hiredis есть свои недостатки. Так как пакет ком п и л и руется из кода C, при по
строении hiredis для некоторы х платф орм вы мож ете столкнуться с ослож нениям и
и ли ограничениям и. К ак и со всеми встроенны м и дополнениям и, возможно, вам
придется заново построить hiredis ком андой npm r e b u ild после об новления Node.
встроен ной базой дан ны х обы чно в ы п о л н яется через прям ы е вы зовы процедур
в ваш ем прилож ении, а не через каналы IP C или по сети.
О LevelDB;
О RocksD B;
О Aerospike;
О EJD B;
О NeD B;
О LokiJS;
О Lowdb.
8.12. LevelDB
LevelDB — встр аи ваем о е х р ан и л и щ е « клю ч-зн ачени е», р азработан н ое в начале
2011 года ком п анией G oogle и и зн ачально предназначенное д ля исп ользован и я
в качестве вспом огательного храни ли щ а р еализаци и IndexedD B в Chrom e. А рхи
тектура LevelD B строится на кон цепц иях базы данны х B igtable ком п ании Google.
LevelD B м ож но сравнить с таким и базам и данных, как Berkeley DB, Tokyo/K yoto
C ab in et и A erospike, но в контексте этой книги LevelD B мож но рассм атривать как
встроенную версию Redis с миним альны м набором функций. К ак и многие встроен
ные базы данных, LevelDB не я вл яется м ногопоточной системой и не поддерживает
использование нескольким и экзем плярам и общего ф айлового хранилищ а, поэтому
эта систем а не будет работать в распределен ной среде без прилож ения-обертки.
LevelD B заклады вает ф ундам ент д ля других баз данных. К оличество засл у ж и ва
ю щ их вни м ан ия ответвлений LevelD B о бъясн яется простотой системы LevelDB:
О R ocksD B (Facebook);
О R iak (B asho);
8.12.2. Установка
Главное достоинство исп ользован ия LevelD B в п ри лож ениях N ode — встроенная
природа базы данных: все необходим ое устан авли вается исклю чительно из npm.
8.12. LevelDB 263
О www.npmjs.com/package/levelup;
О www.npmjs.com/package/leveldown.
8.12.4. Инициализация
П осле вы зова l e v e l ( ) возвращ аем ы й экзем пляр L evelU P нем едленно готов к си н
хронном у получению команд. Команды, введенны е перед откры тием хранилищ а
LevelD B, буф еризую тся до его откры тия.
264 Глава 8. Хранение данных в приложениях
О u tf8 ;
О jso n ;
О b in a ry ;
О id;
О hex;
О a s c ii ;
О base64;
О ucs2;
О u tf1 6 le .
Е сли сохранить знач ен и е с уж е сущ ествую щ им клю чом, старое знач ен и е будет
перезапи сано. П о п ы тка ч тен и я зн а ч е н и я с несущ ествую щ и м клю чом приведет
к ош ибке. О бъект ош ибки будет относиться к конкретном у типу N otFoundError то
специальны м свойством e rr.n o tF o u n d , по котором у его мож но отличить от других
типов ош ибок. Н а первы й взгляд это необычно, но поскольку в LevelD B нет встро
енного метода для проверки сущ ествования, L evelU P необходимо как-то отличать
несущ ествую щ ие значения от неопределенны х. В отличие от g et, попы тка вызвать
d e l с несущ ествую щ им клю чом не приводит к ошибке.
О M ySQ L;
О Redis;
О M ongoD B ;
О ф айлы JS O N ;
О AWS D ynam oD B ;
В озмож ность простой зам ены среды хранения данны х и даж е написания собствен
ной подсистемы баз данны х означает, что вы можете использовать один последова
тельны й набор A P I баз данны х и инструм ентов во м ногих ситуациях и условиях.
О дин A P I баз данны х на все случаи!
Ч тобы вы полни ть л и сти н г 8.21, убедитесь в том, что у вас устан овлен ы пакеты
L evelU P и m emdown:
В этом примере можно было использовать тот же пакет level, использовавш ийся ранее,
потому что он представляет собой обертку для LevelUP. Н о если вы не используете
пакет LevelD O W N на базе LevelDB, входящ ий в поставку level, вы можете просто ис
пользовать LevelUP и избежать двоичной зависимости от LevelDB через LevelDOWN.
8.12. LevelDB 267
level-js maxogden
leveldown/TeveMb library for browsers using indexedDB
★ 10 V22.4
level, leveldb
level-http2 aaaristo
Access a Icvddb instance via HTTP2
★ 0 vO.O.4
leveldown-mobile obastemur
A Node.js LevclOB binding (or Android. iOS and Windows UWP. primary backend for LevdUP
★ 0 vl.1.1
^ leveldb. level
О геопространственны е запросы;
О вы теснение LRU;
О запросы SQL;
О вторичны е индексы;
О триггеры;
В вики L evelU P ведется достаточно подробная сводка экосистемы LevelDB: h ttp s://
github.com /Level/levelup/w iki/M odules. Вы такж е м ож ете пои скать ин ф орм аци ю
о leveldb в npm; на м ом ент написания книги там было 898 пакетов. Н а рис. 8.6 п о
казан а ин ф орм аци я о поп улярн ости LevelD B в npm.
В озможно, вам удастся добиться некоторого повы ш ения скорости за счет перехода
на другой ф орм ат сериализаци и (наприм ер, M essagePack или P ro to co l Buffers), но
рассм атривать альтернативны е ф орм аты стоит только после того, как вы вы ж мете
всю возм ож ную эконом ию за счет со кр ащ ен и я разм еров передаваем ы х данны х
и устран ения избы точны х ш агов сери али зац и и /д есери али зац и и .
Cookie также плохо подходят для долгосрочного хранения данных, которые должны
«пережить» границы сеансов, вкладок и окон (например, созданны х пользователем
докум ентов или электронной почты ). Д л я таких сценариев было спроектировано
хранилищ е localStorage. О бъем данных, которые могут храниться в веб-хранилищ е,
ограничивается в зависим ости от конкретного браузера. Д л я м обильны х браузеров
этот порог составляет всего 5 М байт.
Сводка API
A P I localStorage предоставляет следую щ ие методы д ля работы с клю чам и и зн а
чениям и:
О б ращ ени е к данны м в еб-х р ан и л и щ а осу щ ествл яется д остаточно быстро, хотя
и оно так ж е в ы п о л н я е т с я си н х р о н н о . В еб -х р ан и л и щ е б л о к и р у ет U I -п оток на
врем я в ы п о л н ен и я ч тен и я и запи си. Д л я м алы х н агрузок эти затраты останутся
незам етны м и, но вы долж ны п озаб оти ться о том, чтобы избеж ать л и ш н и х о п ер а
ц ий ч тен и я и ли зап и си (особен но при б ольш их объемах дан ны х). К сож алению ,
веб-хран илищ е такж е недоступно д ля веб-работников (w eb w orkers), поэтому все
о п ераци и ч тен и я и зап и си д олж ны в ы п о л н яться в одном U I -потоке. П одробны й
анализ в л и я н и я на бы стродействи е р азл и ч н ы х техн ологи й х ран ен и я на стороне
клиента приведен в посте Н олана Л оусона (N olan Lawson), автора PouchD B : h ttp ://
nolanlawson.com/2015/09/29/indexeddb-websql-localstorage-what-blocks-the-dom/.
A P I веб-хранилищ а не предоставляю т встроенны х средств д ля вы полнен ия запро
сов, вы борки клю чей по диапазонам или поиска по значениям . Р азработчик огра
ничивается возмож ностью обращ ения с перебором клю чей. Ч тобы провести поиск,
вам придется создавать и поддерж ивать собственные индексы; или, если ваш набор
данны х достаточно мал, м ожно полностью перебрать его элементы. В листинге 8.24
перебираю тся все клю чи localStorage.
/ / В виде объекта
console.log(getAllKeysAndValues());
К ак и у бо льш и н ства х р ан и ли щ «клю ч-зн ачени е», у клю чей сущ ествует только
одно п р о стр ан ств о им ен. Н ап р и м ер , если в базе д ан н ы х х р а н я т с я сообщ ен и я
и ком м ентарии, невозм ож но создать два разны х хран и ли щ а д л я сообщ ений и для
комментариев. Впрочем, достаточно легко реализовать собственные «пространства
им ен», снабди в каж ды й клю ч п р еф и к со м д л я об о зн ач ен и я п р о стр ан ств а имен
(л и ст и н г 8.25).
У ч ти те, ч т о эт о т ф р а г м е н т п е р е б и р а е т в се к л ю ч и и з lo c a lS to ra g e ; п о м н и т е
о в о зм о ж н о м с н и ж е н и и б ы с т р о д е й с т в и я п р и п ер еб о р е б о л ьш о го к о л и ч ес т в а
элем ентов.
О IndexedD B ;
О Service w orkers;
О O ffline-first.
8.14. Хранение данных в браузере 273
8.14.3. localForage
О сновны е недостатки веб-хранилищ а — синхронны й A P I с блокировкой и огра
н и ченн ая емкость пам яти в некоторы х браузерах. К ром е веб-хран илищ а многие
современные браузеры поддерживают W ebSQ L и (и л и ) IndexedD B. О ба хранилищ а
данны х работаю т в неблокирую щ ем реж им е и позволяю т хранить гораздо больш е
данны х, чем технологии веб-хранилищ а.
О днако использовать любую из этих баз данны х напрямую , как это делалось с A PI
веб-хранилищ а, не рекомендуется. Технология W ebSQ L считается устаревш ей, а ее
наследник IndexD B имеет неудобны й и гром оздкий A PI, не говоря уж е об и сп рав
лениях, необходимых для поддерж ки в браузерах. Ч тобы удобно и надежно хранить
данны е в браузере без блокировки, мы реком ендуем использовать нестандартны й
и н стр у м ен т д л я « н о р м а л и за ц и и » и н тер ф ей са. О д н и м из т ак и х и н стр у м ен то в
н орм ал и зац и и я в л я е т с я б и бли отека localF orage от M ozilla (http://m ozilla.github.
io/localForage/).
Обзор API
О l o c a l f o r a g e . g e t I t e m ( k e y j c a l l b a c k ) — п о л у ч ает зн а ч е н и е д л я зад ан н о го
клю ча;
Во внутренн ей р еал и зац и и localF orage исп ользует л учш ий м еханизм хранения,
д о ст у п н ы й в те к у щ е й среде б р ау зер а. Е сл и п о д д ер ж к а In d e x e d D B д оступ н а,
localForage использует ее. В противном случае будет сделана поп ы тка п ереклю
читься на W ebSQ L и даж е использовать веб-хранилищ е при необходимости. Вы
м ож ете н астрои ть порядок, в котором будут опробованы м еханизм ы хранения,
и даж е исклю чить некоторы г е гварианты: Никогда не возвращается
/ / Не будет использовать localStorage к использованию localStorage.
я
localforage.setDriver([localforage.INDEXEDDB, localforage.WEBSQL]);
Prom ise.all([
lo calforage.setItem ('num ber', 3),
lo c a lfo ra g e .s e tIte m ('o b je c t', { key: 'v alu e' }),
lo c a lfo ra g e.s etIte m ('ty p e d arra y ', new Uint32Array([1,2,3]))
]);
сервисов с ранней стадии работы мож ет сэконом ить м ного времени, которое было
бы потрачено на неудачную и излиш ню ю реализацию инф раструктуры .
8.15.1. S3
8.17. Заключение
О В N ode могут использоваться как реляционны е базы данных, так и базы данных
N oSQ L.
О R edis — храни ли щ е структур данных, которое мож ет исп ользоваться как база
данны х и кэш.
О LevelD B — быстрое хранилищ е пар «клю ч-значение» ком пании Google, ассоци
ирую щ ее строки со значениям и.
О Такие сервисы, как Am azon S3, могут использоваться для сохранения данны х
у провайдеров облачны х сервисов.
Тестирование
приложений Node
П о мере расш ирения ф ункц иональн ости при лож ения возрастает риск появления
ош ибок. Б ез тести р о в ан и я при ло ж ен и е счи тается незаверш енны м , а поскольку
ручное тестирован ие утом ительн о и чревато новы м и ош ибками, обусл овл ен н ы
ми человеческим ф актором , у разработчиков все более поп улярн ы м становится
автом ати зи рован н ое тестирование. В этом случае вм есто того, чтобы проверять
ф ункциональность при лож ения вручную, разработчик пиш ет логику автом ати зи
рованного тестирован ия кода.
В этой главе рассм атриваю тся два типа автом атизированного тестирования: м о
дульное и приемочное. Модульное тестирование направлено на непосредственную
проверку логики прилож ения, обычно на уровне ф ун кц и и или метода; м одульное
тестирование применимо ко всем типам прилож ений. В плане методологии модуль
ное тестирован ие м ож ет бы ть разделено на две основны е категории: разработка
через тестирование (T est-D riven Developm ent, T D D ) и разработка через реализацию
п о вед ения (B e h a v io r-D riv e n D evelopm ent, B D D ). С п ракти ческой точки зрени я
тесты в стиле T D D и B D D почти во всем одинаковы , несм отря на стилистические
разл и ч и я (которы е могут быть важ ны в зависимости от того, кому придется читать
ваш и тесты ). М еж ду тестам и в стиле T D D и B D D сущ ествую т и некоторы е другие
различия, но их описание вы ходит за рам ки темы книги. Приемочное тестирование
п редставляет собой до п о л н и тел ьн ы й уровень тестирован ия, п ри м ен яем ы й п р е
имущ ественно при отладке веб-приложений. Этот вид тестирования подразумевает
9.1. Модульное тестирование 279