КулЛиб - Классная библиотека! Скачать книги бесплатно
Всего книг - 712668 томов
Объем библиотеки - 1401 Гб.
Всего авторов - 274519
Пользователей - 125063

Новое на форуме

Новое в блогах

Впечатления

Влад и мир про Шенгальц: Черные ножи (Альтернативная история)

Читать не интересно. Стиль написания - тягомотина и небывальщина. Как вы представляете 16 летнего пацана за 180, худого, болезненного, с больным сердцем, недоедающего, работающего по 12 часов в цеху по сборке танков, при этом имеющий силы вставать пораньше и заниматься спортом и тренировкой. Тут и здоровый человек сдохнет. Как всегда автор пишет о чём не имеет представление. Я лично общался с рабочим на заводе Свердлова, производившего

  подробнее ...

Рейтинг: 0 ( 0 за, 0 против).
Влад и мир про Владимиров: Ирландец 2 (Альтернативная история)

Написано хорошо. Но сама тема не моя. Становление мафиози! Не люблю ворьё. Вор на воре сидит и вором погоняет и о ворах книжки сочиняет! Любой вор всегда себя считает жертвой обстоятельств, мол не сам, а жизнь такая! А жизнь кругом такая, потому, что сам ты такой! С арифметикой у автора тоже всё печально, как и у ГГ. Простая задачка. Есть игроки, сдающие определённую сумму для участия в игре и получающие определённое количество фишек. Если в

  подробнее ...

Рейтинг: 0 ( 0 за, 0 против).
DXBCKT про Дамиров: Курсант: Назад в СССР (Детективная фантастика)

Месяца 3-4 назад прочел (а вернее прослушал в аудиоверсии) данную книгу - а руки (прокомментировать ее) все никак не доходили)) Ну а вот на выходных, появилось время - за сим, я наконец-таки сподобился это сделать))

С одной стороны - казалось бы вполне «знакомая и местами изьезженная» тема (чуть не сказал - пластинка)) С другой же, именно нюансы порой позволяют отличить очередной «шаблон», от действительно интересной вещи...

В начале

  подробнее ...

Рейтинг: +1 ( 1 за, 0 против).
DXBCKT про Стариков: Геополитика: Как это делается (Политика и дипломатия)

Вообще-то если честно, то я даже не собирался брать эту книгу... Однако - отсутствие иного выбора и низкая цена (после 3 или 4-го захода в книжный) все таки "сделали свое черное дело" и книга была куплена))

Не собирался же ее брать изначально поскольку (давным давно до этого) после прочтения одной "явно неудавшейся" книги автора, навсегда зарекся это делать... Но потом до меня все-таки дошло что (это все же) не "очередная злободневная" (читай

  подробнее ...

Рейтинг: +1 ( 1 за, 0 против).
DXBCKT про Москаленко: Малой. Книга 3 (Боевая фантастика)

Третья часть делает еще более явный уклон в экзотерику и несмотря на все стсндартные шаблоны Eve-вселенной (базы знаний, нейросети и прочие девайсы) все сводится к очередной "ступени самосознания" и общения "в Астралях")) А уж почти каждодневные "глюки-подключения-беседы" с "проснувшейся планетой" (в виде галлюцинации - в образе симпатичной девчонки) так и вообще...))

В общем герою (лишь формально вникающему в разные железки и нейросети)

  подробнее ...

Рейтинг: +1 ( 1 за, 0 против).

Основы разработки веб-приложений на платформах Node.js и Deno [Илья Борисович Государев] (pdf) читать онлайн

Книга в формате pdf! Изображения и текст могут не отображаться!


 [Настройки текста]  [Cбросить фильтры]

І/ІТМ О
И.Б. Государев
ОСНОВЫ РАЗРАБОТКИ ВЕБ-ПРИЛОЖЕНИЙ
НА ПЛАТФОРМАХ NODE.JS И DENO

Санкт-Петербург
2023

МИНИСТЕРСТВО НАУКИ И ВЫСШЕГО ОБРАЗОВАНИЯ
РОССИЙСКОЙ ФЕДЕРАЦИИ
УНИВЕРСИТЕТ ИТМО

И.Б. Государев
ОСНОВЫ РАЗРАБОТКИ ВЕБ-ПРИЛОЖЕНИЙ
НА ПЛАТФОРМАХ NODE.JS И DENO
УЧЕБНО-МЕТОДИЧЕСКОЕ ПОСОБИЕ
РЕКОМЕНДОВАНО К ИСПОЛЬЗОВАНИЮ В УНИВЕРСИТЕТЕ ИТМО
по направлению подготовки 09.04.04 Программная инженерия
в качестве Учебно-методическое пособие для реализации основных
профессиональных образовательных программ высшего образования
магистратуры

І/ІТМ О
Санкт-Петербург
2023

Государев И.Б., Основы разработки веб-приложений на платформах Node.js
и D eno- СПб: Университет ИТМО, 2023. - 177 с.
Рецензент(ы):
Жуков Николай Николаевич, кандидат физико-математических наук,
доцент (квалификационная категория "ординарный доцент") факультета
программной инженерии и компьютерной техники, Университета ИТМО.
Данное учебно-методическое пособие развивает идеи первой книги автора
«Введение в веб-разработку на языке JavaScript» и онлайн-курса «Серверные
веб-технологии и системы управления контентом» на платформе openedu.ru. В
пособии рассматриваются фундаментальные основы и прикладные аспекты
использования языка JavaScript (и TypeScript) для разработки веб-ресурсов.
Рассматриваются основные аспекты разработки бэкэнда: шаблонизация,
маршрутизация, скаффолдинг, отладка, развёртывание, контейнеризация.
Обучающимся предложены задания развивающего и проблемного типа,
нацеленные на формирование профессиональных компетенций в области веб­
разработки. Все примеры и задания доступны в интерактивной части пособия
(сайт kodaktor.ru), которая является зарегистрированным в Роспатенте средством
электронного обучения и содержит не только образцы кода, но и скринкасты по
ряду рассматриваемых в текстовой части вопросов. Структура пособия
позволяет организовать изучение материала в заочной и дистанционной форме.

І/ІТМ О
Университет ИТМО - ведущий вуз России в области информационных и
фотонных технологий, один из немногих российских вузов, получивших в 2009
году статус национального исследовательского университета. С 2013 года
Университет ИТМО - участник программы повышения конкурентоспособности
российских университетов среди ведущих мировых научно-образовательных
центров, известной как проект «5 в 100». Цель Университета ИТМО становление
исследовательского
университета
мирового
уровня,
предпринимательского по типу, ориентированного на интернационализацию
всех направлений деятельности.
© Университет ИТМО, 2023
© Государев И.Б., 2023

Содержание

Глава 1. Рекомендации по изучению материалов пособия и основные
термины........................................................................................................................... 5
Методические рекомендации преподавателям и студентам.................... 5
Подготовка среды для работы с JavaScript...................................................6
Понятия фронтэнда и бэкэнда....................................................................... 13
Протоколы, используемые в вебе при создании веб-приложений...... 21
Глава 2. Использование Node.js для отправки сетевых запросов и их
обслуживания.............................................................................................................. 30
Метод net.connect.............................................................................................30
Метод net. Server...............................................................................................32
Глава 3. Fetch A PI.............................................................................................36
Основа: AJAX................................................................................................... 37
О потоках...........................................................................................................39
Отправка запросов........................................................................................... 43
Глава 4. Модель приложения на Node.js.................................................... 45
Отладка............................................................................................................... 54
Простейшая маршрутизация......................................................................... 63
Блокирующий код............................................................................................64
Выдача статики................................................................................................. 68
Приём запроса с телом сообщения.............................................................. 71
Модуль connect.................................................................................................77
Деплой приложения на Next.js/vercel.......................................................... 77
Использование ngrok....................................................................................... 83
Глава 5. Библиотека Express......................................................................... 86
Установка Express............................................................................................87
Понятие middleware. Виды middleware и их стек......................................93
Маршрутизация. Создание express.Router и дерева маршрутов............99
Шаблонизация................................................................................................ 107
Приём запроса с телом.................................................................................. 112
Запуск приложения в формате сервиса..................................................... 116
Использование ядер и потоков................................................................... 117
Глава 6. Нано и микросервисы, REST API........................................... 123
Создание декларативного сервиса на основе ОрепАРІ......................... 124
Критерии RESTful..........................................................................................126
Операции с ресурсами на основе НТТР-глаголов...................................128
3

Реализация Express-приложения в формате микросервиса................. 129
Скаффолдинг.................................................................................................. 139
Создание микросервиса на основе проекта H ydra..................................140
Глава 7. HTTPS и HTTP/2............................................................................145
Локальный сертификат................................................................................. 145
Модуль node:https...........................................................................................148
Контейнер companion.................................................................................... 148
Модуль node:http2..........................................................................................151
Глава 8. Веб-сокеты........................................................................................153
Клиентская реализация W ebSocket........................................................... 153
socket.io............................................................................................................. 158
Глава 9. Использование Deno и B u n ........................................................ 160
D en o .................................................................................................................. 160
B u n .....................................................................................................................170
Заключение........................................................................................................171
Вопросы для самопроверки......................................................................... 172
Примеры заданий автоматизированной проверки..................................174
Литература.........................................................................................................176

4

Глава 1. Рекомендации по изучению материалов пособия и основные термины

Методические рекомендации преподавателям и студентам
Это пособие было подготовлено по следам ранее созданного онлайнкурса «Серверные веб-технологии и системы управления контентом». За
период времени, прошедший после запуска курса, ситуация в экосистеме
Node.js претерпела изменения, и автор решил постараться отразить некоторые
из них в текстовой форме. Курс находится на площадке «Открытое
образование» по адресу https://openedu.m/course/ITMOUniversitv/NODEJS и
содержит в основном видеоуроки в формате скринкастов. Курс и данное
пособие образуют единый комплекс, в силу логики выстраивания материала
ориентированный главным образом на последовательную самостоятельную
работу студентов, и от преподавателя не требуется специальных мер по
организации учебной деятельности, нужно лишь обеспечить сопровождение
решения упражнений и выполнения контрольных мероприятий.
Пререквизитами являются знание основ JavaScript и сетевых технологий,
общий уровень компетенций в области Linux. В книге Д. Флэнагана [1] дано
наиболее полное описание JavaScript в формате подробного справочника, а
предыдущей работой автора данного пособия является [2] с попыткой
сфокусировать внимание на интересных аспектах JavaScript с точки зрения
исследования языка (в [2] обсуждается асинхронность JavaScript и промисы).
Первые пять глав пособия соответствуют первым пяти неделям курса.
Рекомендуется сначала параллельно изучать материалы первых трёх
глав/недель. Затем целесообразно посмотреть материалы 4-й недели и
прочитать 4-ю главу, далее посмотреть материалы 5-й недели и прочитать 5-ю
главу. Далее работа с курсом и пособием может осуществляться параллельно.
В пособии представлены 20 упражнений для самостоятельной работы.
Вероятно, оптимально будет организовать встречи для кросс-проверки
результатов (peer-to-peer), на которых обучающиеся могут обменяться своими
достижениями. Их также можно рассматривать как материалы лабораторных
работ в случае очной организации обучения.
Автор предлагает считать задания с автоматизированной проверкой
(примеры приведены перед списком литературы) основой для организации
аттестации. Курс будет считаться пройденным при условии успешного
выполнения этих заданий и итогового теста (примеры вопросов которого
также приведены в заключительной части пособия).
Также преподавателям и студентам рекомендуется в начале изучения
материалов совместно составить список тем докладов по темам,
расширяющим и дополняющим курс (например: «Синхронизация состояния
веб-приложения на клиенте и сервере» или «Серверный рендеринг в
современных фреймворках») и делиться друг с другом индивидуальным
опытом освоения таких технологий и инструментов.
Первые шаги связаны с терминологией и настройкой среды для работы.
5

Подготовка среды для работы с JavaScript

По состоянию дел на период написания этого пособия язык JavaScript, как
и в течение многих предыдущих лет, является основным языком разработки
клиентских веб-приложений. С другой стороны, есть основания ожидать роста
популярности WebAssembly и расширения соответствующей экосистемы,
которая позволит использовать в рамках браузеров принципиально другие
языки, такие как С. А с третьей стороны в рамках Web3 приложения могут
выполняться в Etliereum-блокчейне и быть написанными на языке типа
Solidity. Но пока мы исходим из того, что при наличии желания понимать и
контролировать практически весь процесс разработки веб-приложения с его
клиентской и серверной частью JavaScript является естественным выбором.
Разработчик может писать код на TypeScript, Elm, ReasonML и т.д., но
результатом так или иначе будут JavaScript-сценарии. Поэтому довольно
логично, что JavaScript становится универсальным языком решения
практически всех задач веб-разработки, включая автоматизацию и разработку
бэкэнда (или, во всяком случае, слоя бэкэнда-для-фронтэнда).
Язык JavaScript развивается достаточно динамично, в конвергенции с
другими языками, вбирая в себя некоторые их характеристики. Ежегодно
нововведения фиксируются в очередной версии стандарта (например,
ECMAScript-2023). Часто они заранее «обкатываются» на свежих версиях
платформ, поэтому важно следить за этими обновлениями.
Целью этого пособия не является доказательство или обоснование
преимуществ использования JavaScript для разработки на внебраузерной
платформе. Но некоторые очевидные вещи всё же отметим. Если много лет
назад допустимо было создавать программы на JavaScript прямо в браузере,
напрямую записывая инструкции в элемент script, то современная разработка
для браузера ведётся в специальном окружении, которое включает не только
редактор кода или IDE, но и линтеры, средства для работы с каскадными
стилями, а также библиотеку типа React с сопутствующими инструментами.
Все эти составляющие JavaScript-экосистемы подключаются к проекту в виде
прямых или девелоперских зависимостей через менеджер зависимостей, в
качестве которого в большинстве случаев используется npm, являющийся
частью экосистемы Node.js.
Когда мы говорим о разработке на JavaScript не для браузера, то скорее
всего имеем в виду разработку на платформе Node.js, которая нацелена:




на создание сценариев, работающих не в браузере или не только в
браузере (например, для вычислений или для автоматизации работы
в окружении);
на создание серверных веб-приложений.
6

Сначала кратко обсудим первый аспект.
Node.js и Deno - это программное обеспечение, относящееся к классу сред
исполнения (кратко - runtime). Runtime не просто присутствует в памяти во
время выполнения программы, а предоставляет виртуальную машину, которая
и осуществляет выполнение. В основе Node.js и Deno находится движок виртуальная машина V8, разрабатываемая в Google. На том же движке
работает и браузер Chrome. При этом Deno содержит ещё и транспилятор
TypeScript. В 2022 г. на профильных ресурсах обсуждали также новый
инструмент под названием bun.sh - к этому мы вернёмся в Главе 9. Сложно
сказать, что будет с этим инструментом дальше - но он интересен как феномен
развития, поэтому и фиксируется в этом пособии.
Node используется в двух основных «режимах»:
• выполнение в стиле интерфейса командной строки;
• выполнение «долгоиграющих» сценариев, таких как веб-сервера.
Первый вариант обычен, например, при создании React-приложения по
шаблону.
Современная
клиентская
веб-разработка,
как правило,
подразумевает наличие многих файлов с исходным кодом (таких как файлы
компонентов). Чтобы подготовить production-версию, эти файлы нужно
собрать в единый бандл, а перед этим прогнать через тесты. Эти задачи можно
было бы решать разными способами, но промышленным стандартом стало
использование npm-пакетов на платформе Node, которые работают
практически совершенно одинаково на разных ОС. Например, для
тестирования может быть использован инструмент Jest, для сборки
(бандлинга) - Webpack. Кроме того, вызов соответствующих команд
автоматизируется с помощью инструментов типа create-react-app. Возможно и
код редактировать инструментом, который устанавливается как nodeзависимость, но это не очень удобно, хотя популярные редакторы кода Atom
и Visual Studio Code всё равно написаны на языке JavaScript/TypeScript с
использованием фреймворка Electron.
Рассмотрим пример работы в рамках первого варианта. Допустим, мы
хотим получить инструмент, который извлекает с сайта информацию о
времени по Гринвичу. На начальном уровне сложности мы можем сделать
следующее:
• установить платформу Node.js;
• убедиться, что также установлен менеджер npm (и если не установлен,
то установить; подойдёт и альтернатива в виде yarn либо pnpm);
• создать сценарий на JavaScript.
Чтобы работать с Node, кроме установки вы можете использовать реплит
(repl.it), node.new, виртуальный сервер, виртуальную машину, Docker.
7

Инструкции
по
https://kodaktor.ni/g/node

установке

Node.js

приведены

по

адресу

П римечание
Платформа Node.js, как и вообще весь веб, изначально «настроены»
на работу под управлением Linux/UNIX, поэтому для работы
удобнее всего использовать либо сразу эти ОС (а также, разумеется,
MacOS), либо виртуальные машины или контейнеры, в том числе
подсистему WSL. В тексте пособия приводятся варианты команд,
которые работают в перечисленных типах операционных систем.
Для MacOS с официального сайта скачивается pkg-файл. Для Ubuntu
20.04 и 22.04 работают следующие команды:
apt install nodejs npm -у
npm install -g n
n latest
Для управления версиями используется пакет n, и команда n latest как раз
позволяет перейти к свежайшей версии, также можно указать точный номер:

« Установка: Node.js
Установка успешно завершена.
Введение
• Лицензия
• Размещение
• Тип установки

This package has Installed:




Node.js v18.8.0 to /usr/local/bin/node
npm V8.18.0 to/usr/local/bin/npm

Make sure

that / u s r / lo c a l/ b in is in your $ p a t h .

• Установка


С во дка

ilyagosudarev@MacBook-Pro-Ilya 02-09-2022— 00-00 % sudo n 10.0.0
installing : node-vl0.0.0
mkdir : /usr/local/n/versions/node/10.0.0
fetch : https://nodejs.Org/dist/vl0.0.0/node-vl0.0.0-darvyin-x6A.tar.xz
copying : node/10.0.0
installed : ѴІ0.Ѳ.0 (with npm 5.6.0)

Рисунок 1. Результат установки Node.js на MacOS и переход на другую
версию
8

Может оказаться необходимым вначале выполнить apt update для
обновления списков пакетов. После установки той или иной версии всегда
можно использовать для выполнения кода именно её, примерно так:
/usr/local/n/versions/node/10.0.0/bin/node index.js
В целом универсальным и безопасным вариантом является использование
Docker-контейнера (на этом этапе можно выполнить инструкцию
https://kodaktor.ru/docker intro 2022.pdf для тренировки работы с Docker).
Если имеется файл с JavaScript и Docker, то всегда можно использовать
контейнер с Node в качестве «открывашки» примерно следующим образом (с
точностью до номера версии):
docker run —rm -v ”$PWD":/usr/src/app -w /usr/src/арр node: 19 node index.js
(Чтобы зайти в REPL в контейнере достаточно выполнить
docker run -it —rm node: 19 node
- здесь t означает использование терминала для ввода, а і для вывода)
Пусть Node.js установлен, и у нас есть
содержимым:

файл index.js со следующим

fetch('https://www.worldtimeserver.com/current_time_in_GB.aspx')
then(x => x.textQ)
,then(x => console.log(x.match(Ad\d:\d\d:\d\d ../)[0]));
Тогда для запуска сценария нужно выполнить команду:
node .
Точка означает файл по умолчанию (index.js).
Если всё в порядке и сервер времени доступен, то мы увидим результат в
формате
12:34:56 AM

Мы можем переформулировать код с обращением к Интернет-ресурсу с
помощью aw ait
(async() => {
const result = await (await fetch('https://www.worldtimeserver.com/current_time_in_GB.aspx')).text():

console.log(result.match(Ad\d:\d\d:\d\d ../)[0]);
})();

9

Интерфейс Fetch API стал доступен в Node относительно недавно, и на
этапе написания пособия всё ещё являлся экспериментальным. Более
подробно о нём мы будем говорить в Главе 3.
Если мы работаем под Linux или MacOS и умеем выяснять куда после
установки попал исполнимый файл node (which node), то можно
воспользоваться синтаксисом shebang (hashbang) и добавить указание в файл,
после чего сделать его исполнимым.
П римечание.
Летом 2022 г. включение синтаксиса hashbang в грамматику языка
стало законченным предложением (finished proposal, 4-я стадия) и
таким образом кандидатом на часть стандарта ES2023 (к лету 2023
г.). См. https://eithub.com/tc39/proposal-hashbang

#!/usr/bin/env node
fetch('https://www.worldtimeserver.com/current time in GB.aspx)
then(x => x.text())
then(x => console.log(x.match(Ad\d:\d\d:\d\d ,./)[0]));
После назначения права исполнения (chmod +х index.js) его можно
запускать как команду, например ./index.js (а если добавить путь к нему в
PATH то и просто по имени).
В случае использования await оборачивание кода в async-функцию...
(async 0 => {
})();
.. .необходимо в файлах js вне проекта поскольку на текущем этапе await
можно использовать вне функции только в модулях. Чтобы сделать файл
модулем, достаточно изменить его расширение на .mjs
Тогда сценарий сведётся к двум строкам:
const result = await (await
fetch('https://www.worldtimeserver.com/current_time_in GB.aspx')).text();
console.log(result.match(Ad\d:\d\d:\d\d ,./)[0]);
Второй способ задать режим модуля - использовать манифест проекта
package.json и добавить в описание type="module"
10

В этом сценарии используется самый примитивный способ извлечь
информацию - регулярный паттерн. Нет гарантии, что разработчики сайта
оставят строку времени такой же. Чуть лучше будет извлекать данные из тега,
который имеет вид

html •

• title TypeScript
S

Рисунок 4. Express в списке зависимостей проекта
Б) содержимое будет скачано из интернета, мы получим папку
node modules, в которой будут находиться папка проекта express и множество
папок его зависимостей. Содержимое папки express при этом не будет
включать собственную папку node modules.
Если мы хотим иметь express у себя на локальном компьютере, то можем
поместить содержимое папки express отдельно и выполнить в ней npm і, тогда
зависимости библиотеки express окажутся внутри в папке node modules.
Допустим, у нас после этого есть папка
/Users/ilyagosudarev^HfleKc^ncK.2021 /express
v EXPRESS

> lib
> node_modules
♦ History.md
js

index.js

К LICENSE
{} package.json
(D Readme.md

Рисунок 5. Express и папка node modules
Теперь в локальных проектах мы можем использовать этот путь чтобы
забирать express из локальной папки, а не из интернета
npm і /Users/ilyagosudarev/Яндекс.Диск.2021/express
Кроме того, мы можем загрузить пакет из произвольного источника,
указывающего на файл .tar.gz с архивом проекта или из репозитория. (Этот
файл распаковывается в папку package, в которой размещаются файлы
проекта.)
Чтобы создать на основе своего кода пакет, который затем можно
инсталлировать по URL, нужно указать в package.json имя и версию, а затем
выполнить npm pack
17

js

ПРОВОДНИК

index.js

is index.js >...
1
export const f = x => x.toU pperC aseO ;

^ О ТК РЫ ТЫ Е РЕДАКТОРЫ
ГРУППА 1
JS in d ex.js

package.json x

ГРУП ПА 2
X

(

p a c k a g e .jso n

V 2022072... [ \ E 7 О

#

j s in d ex.js

mypack29072022-1.2...
p a c k a g e .jso n

} package.json > Ѳ description
1
{
2
"name": "mypack29072022",
3
" v e rsio n ": " 1 .2 .3 " ,
4
" ty p e ": "module",
5
" d e s c rip tio n " : "demo package",
6
"main": " in d e x .js " ,
7
"a u th o r": "GossJS",
8
" lic e n s e " : "HIT"
9
>
10
ТЕРМИНАЛ

ПРОБЛЕМЫ

ВЫХОДНЫЕ ДАННЫЕ

КОНСОЛЬ ОТЛАДКИ

> СТРУК ТУРА

3qosudarev@MacBook-Pro-Ilya 20220729_021533 % npm pack
iot
lotic ■Ц m ypack29072022@ l. 2 . 3

> ВРЕМ ЕННАЯ ШКАЛА
> СЦ ЕН А Р И И NPM

Рисунок 6. Выполнение команды npm pack

Упражнение 1
Выполните в новой папке следующие действия:

1. npm init -у
2. npm i https://kodaktor.rU/mvpack29072022-l.2.3.tgz

X

packagejson

2 0 22072... f t

FT

О

v mypack29072022

Ѳ

1
2
3
4
5

<
"name": "20220729_023742",
" v e rs io n ": " 1 .0 .0 " ,
" d e s c rip tio n " :
"m ain": " in d e x .js " ,
Отладка >

JS index.js
() package.json
{ .package-lock.json
U package-lock.json
p a c k a g e .js o n

Рисунок 7.

6
7
8
9
10
11
12
13
14
15
16

" s c r ip ts " : {
" t e s t " : "echo V 'E rro r: no t e s t s p ec ifted V ' SA e x it 1"
}.
"keywords": П ,
" a u th o r":
" lic e n s e " : "ISC",
"d ep endencies": {
"mypack29072022": " h ttp s://k o d a k to r.ru /m y p a c k 2 9 0 7 2 0 2 2 -1 .2 .3 .tg z "
>
}

Изменившийся раздел зависимостей

3. Создайте файл index.mjs
Напечатайте в нём
import { f } from 'mypack29072022';
console.log(f( hello world'));
4. Выполните сценарий node index.mjs

18

Вы должны будете увидеть в консоли вывод заглавными буквами HELLO
WORLD
Напомним, что традиционным способом включения сценарием в веб­
страницы был и остаётся элемент script. Этот вариант изначально не
подразумевал никакой модульности и, соответственно, изоляции фрагментов
кода друг от друга. Чтобы предотвратить порчу объявленных на глобальном
уровне имён, авторы кода и, особенно, библиотек, программисты привыкли
оборачивать код в IIFE типа IfunctionQ{...}(). На платформе Node.js была
реализована собственная модульность (CJS) на основе функции require, но она
не прижилась на браузерной стороне. На современном этапе более или менее
достигнута изоморфность в этом аспекте на основе нативной ES-модульности
(import, export). В браузере мы используем script с атрибутом type="module" и
достигаем практически эквивалентной функциональности. Например, вот тот
же сценарий, экспортирующий функцию, в виде веб-страницы:
https://kodaktor.ru/impoyt esm
Многие современные библиотеки предоставляют разные варианты своих
дистрибутивов, так чтобы их можно было подключить просто как script
https://kodaktor.ru/ react2021/counter
или в виде ESM
https://kodaktor.ru/effector react
В качестве популярного примера подключения широко используемой
библиотеки нельзя не упомянуть lodash. Если мы работаем на внебраузерной
платформе, то будем подключать библиотеку как npm-пакет (часто тоже есть
версии специально для модулей ES)
npm i lodash-es
Но если при этом мы просто проводим эксперименты на Node.js или
создаём отдельный сценарий (без папки проекта и манифеста), то можем
воспользоваться
традиционной
формой
сценария,
такой
как
https://kodaktor.ru/lodash.is
(Его
можно
подключить
и
к
веб-странице

console.Iog(String(x))).write( POST
/api/req/%D0%98%D0%BB%D 1%8C%D 1%8F/%D0%93 %D0%BE%D 1% 81%D
1%81 HTTP/1 .l\nHost:kodaktor.ru iContent-Type:application/x-www-formurlencoded ContentLength: 65' \nname=%D0%98%D0%BB%D 1%8C%D 1%8F&familyname=%DO
%93 %D0%BE%D 1% 81%D 1%8 Г );
Это обращение из REPL имеет тот же эффект, что рассмотренные в
первой главе обращения с помощью telnet, Fetch API и curl. Методы on и write
объединены в цепочку. Такое возможно благодаря тому, что результат
соединения - сокет - реализован как поток, в свою очередь реализующий
интерфейс EventEmitter. Событие data возникает в нём в нашем случае после
того, как в него записали данные. Эти данные являются сырым содержанием
HTTP-запроса. Собственно говоря, именно их мы ранее отправляли с
помощью telnet. Но здесь они представлены как JavaScript-строка. В JavaScript
есть синтаксис шаблонных строк с кавычками в виде обратного косого
апострофа, и это позволяет вместо впечатывания символов \п просто нажимать
Enter:
import { connect} from 'nodemet';
connect(80,'151.248.115.32')
.on('data' , x = > console.log( Stringi x)))
.write(POST
/api/req/%D0%98%D0%BB%D 1%8C%D 1%8F/%D0%93%D0%BE%D 1% 81%D
1%81 HTTP/1.1
Hosfkodaktor.ru
Content-Type :application/x-www-form-url encoded
Content-Length: 65
name=%D0%98%D0%BB%D 1%8C%D 1%8F&familyname=%D0%93%D0
%BE%D 1% 81%D 1%8 Г );
Листинг 1. Отправка запроса клиентским методом модуля nodemet
30

Получается, что у нас асимметричное соединение - на «той» стороне
работает HTTP-сервер, а «здесь» мы используем TCP-клиент. Но этот пример
хорошо показывает, что запрос на «языке» HTTP это просто строка, и сервер
делает веб-сервером именно способность разобрать её, интерпретировать и
выдать ответ в подобном же формате. Поэтому следующим шагом мы
создадим простейший веб-сервер с помощью модуля node:net.

Примечание
Обратите внимание, что современный стиль работы с модулямипакетами предусматривает использование префикса node: перед
именами встроенных модулей, к числу которых относится net. Далее
в тексте исключения (ссылки без префиксов) сделаны для случаев,
когда в той или иной среде префиксы (на этапе работы над пособием)
не поддерживались.
Примечание
Интерфейс
EventEmitter
является
важнейшим
набором
характеристик которые разделяют самые разные объекты и
структуры платформы это прежде всего способность
регистрировать сигналы или события, связывая с ними
функциональный код (коллбэк), и испускать эти сигналы, тем самым
инициируя синхронное исполнение связанного кода.
(base) -► node
Welcome to Node.js Ѵ І 4 . 5 . 0 .
Type ".help" for more information.
!> const { EventEmitter } = requireC'event
undefined
> const e = new EventEmitterO;
undefined
> e.on('hello', О => console.log('hi!’))
EventEmitter {
.events: [Object: null prototype] { hel
.eventsCount: 1,
jnaxListeners: undefined,
[Symbol(kCapture)]: false

}
> e.emit(’goodevening’);
false
> e.emitChello');
hi!

Рисунок 12. EventEmitter

31

Метод net.Server
Мы можем запустить простейший TCP-сервер одной строкой прямо в
REPL:
net.Server(sock => sock.end('hello')).listen(1234);
Этот сервер в ответ на все запросы выдаёт лишь строку hello. Браузер
Chrome не прочитывает такой ответ (во всяком случае на порту, ином чем 80):
sock end('H T T P /1.1 200 n nhello')).listen( 1234);
32

После этого любой браузер будет отображать минимально возможную
веб-страницу, состоящую из простого (plain) текста. В простом тексте такие
символы, как открывающая теговая угловая скобка ( res.end('OK\n'));
s.listen) 4321);
51

Теперь при обращении клиента или инициировании события request
вручную мы будем получать ответ:
curl localhost:4321 -i

HTTP/1.1 200 OK
Date: Sun, 01 Aug 2022 15:28:47 GMT
Connection: keep-alive
Content-Length: 3

OK
Итак, веб-сервер с базовой функциональностью по умолчанию никак не
реагирует на запросы - эти реакции нужно определять самостоятельно. Здесь
усматривается логика JavaScript - если создать на веб-странице кнопку и не
определить слушатель события click, кнопка тоже не будет реагировать на
щелчки.
По умолчанию сервер выдал ответ с кодом 200. Это всегда можно
переопределить.
Кроме того, мы видим, что по умолчанию отсылается минимальный
набор HTTP-заголовков. Среди них - заголовок Content-Length, сообщающий
число переданных байт. В нашем случае это следующие байты:
4f




Первые два - ASCII-коды английских букв О и К, а третий - символ
переноса строки \п.

Метод listen, который инициирует процесс слушания соединений, также
принадлежит серверу. Если указать номер порта, который уже используется,
то получим неотлавливаемую фатальную асинхронную ошибку (Error: listen
EADDRINUSE).
Если же в вызове метода listen убрать номер порта, то он будет назначен
автоматически из числа неиспользуемых в данный момент. Кстати, клиенту
порт назначается автоматически всегда. По умолчанию при таком способе
запуска сервер будет доступен другим компьютерам локальной сети. В методе
listen мы можем указать, что прослушиваем только локальный сетевой
52

интерфейс (прибайндить), и этим отключить возможность посылать запросы
локальному веб-серверу с других компьютеров локальной сети.
.listen(4321,^iГ ...)
Ниже приведён пример сценария с сервером, который не знает заранее,
какой порт будет слушать. Он выдаёт номер после запуска. Вы можете
получить сценарий командой wget littps://kodaktor.m/j/port.mjs и запустить
командой node port.mjs
import { Server } from 'node:http';
const s = ServerQ;
s.on('requesf, (req, res) => res.end('OK\n'));
s.listen(() => console.log('nopT : ${s.address().port)'));
Вы получите номер порта вида 54321. Выполните обращение к
localhost: 54321
(подставьте номер порта, полученный в результате работы сценария)
Такие пакеты, как portfinder, находят свободный порт более «явным»
образом. При этом когда приложение запускается на PaaS типа Heroku, среда
предоставляет порт, и в методе listen указывают выражение вида
process.env.PORT || 4321 либо process.env.PORT ?? 4321. Выражение с
оператором || реализует сокращённую схему вычислений: если каким-либо
способом была установлена переменная окружения PORT, оператор
использует её, иначе вычисление будет продолжено и будет использован
правый операнд с числом. Это удобно для запуска приложения в разных
средах. На сегодняшний день есть смысл перейти от оператора || к оператору
?? (Nullish coalescing) из ECMAScript 2020, семантика которого идеально
соответствует рассматриваемой ситуации.
Так как событие request самое важное, его обработчик может быть
передан в вызов функции Server в качестве слушателя события по умолчанию.
В тех примерах, которые отличаются от нижеприведённого только
содержанием этого обработчика, мы будем приводить лишь его код.
import { Server } from 'nodedittp';
const s = Server((req, res) => res end('OKAn'));
s.listen(4321);
Эта функция - асинхронный коллбэк. Данный факт очень важен для
понимания того, как основанный на Node js веб-сервер обходится с запросами
и объясняет его высокую эффективность при обработке большого числа
одновременно поступающих запросов. Мы ещё вернёмся к этому, а сейчас
53

рассмотрим, как мы можем контролировать выполнение ответа в режиме
пошагового выполнения.

Отладка
Для отслеживания хода выполнения сценария используется инструмент
отладки (debugging). Естественно, мы можем получить всю полноту
информации о том, что используется в программе из документации, но также
полезные сведения можно извлечь в ходе отладки. Мы можем использовать
различные инструменты отладки. Базовым инструментом является команда,
когда мы запускаем Node с ключом inspect-brk и указываем имя сценария.
В нашем случае это будет index.js или index.mjs. При запуске такой
команды создаётся подключение, которым пользуется клиентская часть
отладчика, и в её роли может выступать браузер Chrome и его DevTools.
Существует также инструмент, который называется NDM от Google, но
поскольку мы уже начали работу в редакторе кода Visual Studio Code, логично
будет воспользоваться встроенным инструментом отладки, который
представлен в этом редакторе, он достаточно удобен. Сейчас у нас имеется
приложение, которое откликается на запросы поступающие на localhost и на
порт 4321. Мы просто получаем ответ и ничего более. Теперь задача состоит в
том, чтобы этот ответ посылать не в программе, чтобы мы могли его вручную
отправить в режиме отладки.
Для этого предназначены кнопка Run and Debug и команда debugger,
которая играет роль триггера точки останова.______________
• •
мм—
НЛМЦм»
index.js

RUN

*

U package.ison

index.js > W s > 0

Run and Debug
To customize Run and
Debug create a
launch.json file.

Serverj) callback

1
2
3

import { Server > from 'h ttp ';
const s = Se rver((req , res) => {
debugger;
I

4
5

1> ;
s . lis t e n (4321);

Node.js Debug
Terminal

Рисунок 18.

Команда debugger

Мы должны будем естественно остановить работу предыдущего сеанса,
открытого в терминале, после этого нажать на кнопку Run and Debug и
выбрать вариант Node.js.
запуск и отлад

|выберите отладчик
Deno
Node.js

Рисунок 19.

Выбор отладчика в Visual Studio Code
54

Теперь наше приложение работает в режиме отладки, и, если обратиться
к приложению, то есть послать клиентский запрос, то сработает эта функция,
обработчик запроса и сработает точка останова.
Для удобства откроем ещё один терминал и напишем команду curl
localhost:4321, и после нажатия Enter будем наблюдать как сработает точка
останова.
Index.|s — 20220824J24726

ЗАПУСК ..

^

Нет конфигура

•••

index.js 4 X

ѵ ПЕРЕМЕННЫЕ

С О Ш

p ackage.json

index.js > TypeScript > («) s > 0 Server() callback
1

ѵ Local

im p o rt { S e r v e r > from Чюd e j h t t g ^ ;

const s = S e rv e r!(re£,

> req: IncomingMessage { _ read ableS tate„

n ts )

lt>

I

о шt Tо □

=> {

• 3 debugger;,

> re s : ServerResponse { .e v e n ts : {-},

});

t h i s : undefined

s . l i s t e n (4 3 2 1 );

> Module

ab

V/ КОНТРОЛЬНОЕ ЗНАЧЕНИЕ

V СТЕК вызовов
v

f i L ...

ТЕРМИНАЛ

ПРОБЛЕМЫ ф

ВЫХОДНЫЕ ДАННЫЕ

PAUSED ON DEBUGGER STATEM ENT

Ш zsh


index.js 135
Показать еще 3: Skipped by skipFiles

ф curl

И Т Т Р Ш Ш І Ы M ESSA G E

О

Показать еще 8: S kipped by skipFiles
TCPSERVERW RAP

Показать г ню В: Яkinneri bv skioFiles

Рисунок 20.

Срабатывание точки останова

После срабатывания точки остановка в Debug console мы видим
сообщение о том, что отладчик запустился и можем вручную выдать ответ
ждущему его клиенту...
ѵ СТЕК ВЫЗОВОВ

кг ф L...

•••

PAUSED ON DEBUGGER STATEMENT



index.js

3:5

Показать еще 3: Skipped by skipFile:

HTTPINCOMINGMESSAGE

Ф ильтр (например: text, (exclude)

терминал

losudarev/.volta/bin/node ./index.js

Проблемы (4)
Выходные данные
>/ Консоль отладки

Показать еще 8: Skipped by skipFile:

TCPSERVERWRAP

Jupyter
Com m ents

Показать еше В: Skinned bv skioFiles
> ЗАГРУЖЕННЫЕ СЦЕНАРИИ

Рисунок 21.

Консоль отладки

... то есть написать res.end и предоставить какой-нибудь простой ответ.

55

Фильтр (нап

/Users/ilyagosudarev/.volta/bin/node ./index.js
res.end('OK\n');
^ ServerResponse {.events:
.eventsCount: 1, _mc
d, outputData: Array(0), outputSize: 0, ...}

> res.end(‘0K\n');|

Рисунок 22.

Ручная отправка ответа сервера в консоли отладки

После появления ответа, для того чтобы завершить этот цикл обработки
запроса, мы нажимаем на кнопку Continue, то есть продолжить.
=: l>

^

i

?

о



Продолжить (F5)

Рисунок 23. Кнопки управления отладкой

После этого всё можно повторять заново. Но давайте теперь немного
поисследуем то что происходит. Например, посмотрим чем является req,
который указан в качестве одного из параметров обработчика события
запроса.
Мы здесь видим, что req это экземпляр класса IncomingMessage. В свою
очередь res - это экземпляр класса ServerResponse. И IncomingMessage и
ServerResponse являются классами, которые доступны во встроенном модуле
HTTP. Поскольку у нас есть доступ к переменной req, мы можем исследовать
свойства, которые доступны в req. Одним из таких свойств является uri. Оно
показывает запрашиваемый маршрут. Свойство req.method отображает метод,
которым был отправлен запрос клиента, сейчас это GET.
Кстати, через ссылку req.client доступен TCP-сокет, который
используется в данном HTTP-соединении. После отправки запроса и
срабатывания отладчика мы можем в консоли отладки написать
req.client.write('one');
req.client.end('two');
и нажать Continue
И тогда curl с параметром -і покажет только onetwo, без всяких заголовков
HTTP, потому что мы отправили данные в сыром низкоуровневом виде, как
если бы использовали низкоуровневый модуль node:net.
И ещё отправим точно такой же запрос, но методом POST (curl
localhost:4321 -XPOST). Мы можем увидеть, что свойство req.method теперь
56

содержит POST. Это показывает, что серверное приложение в той форме,
которую мы сейчас перед собой видим, характеризуется свойством которое мы
ранее обозначили как method-agnostic. То есть оно делает одно и тоже,
независимо от того каким методом был послан запрос (впрочем, то же самое
касается и маршрута или пути, который указывается после имени хоста в
комбинации с номером порта).
Построенное нами приложение не зависит ни от маршрута, ни от метода.
Если мы хотим, чтобы оно по-разному реагировало на запросы, приходящие
на разные маршруты и разными методами, мы должны оперировать
свойствами req.url и req.method для того чтобы дифференцировать по этим
параметрам.
Если в ответ на запрос выполнять сначала res.write и только потом res.end,
то ответ будет состоять из совокупности посланных данных, после того как
мы завершим цикл обработки запроса. Обратите внимание на заголовок ответа
Transfer-Encoding: chunked. Информация при обработке запроса поступала в
ответ порциями - чанками.
Г\

RUN >

IV

v VARIABLES
Р

{*,•

*'■> index, js
::

j(

*

г*


О

(
П

ш

pack ag ejso n

(b a se) -► \
lo c a lb o st:4 3 2 1 /h ell;
-X
POST
НТТР/1.1 200 OK
Date: Sun. 05 Jul 2020 20:42:05
GMT
Connection: keep-alive
Transfer-Encoding: chunked

1c u rl

Server!) callback

2
3

co n st s = S e r v e r ! (
1 deb u gger;

ч, ■

> ■> {

4
S

І> :
s . l i s t e n ( 4 3 2 1 ) ; // ro u te a g n o s t i

_

To be c o n tin u e d .. .Ok
(b a se) -► I

од

v WATCH

0?

LU

Ф

v CALLSTACK

%
®oAo

t>

PROBLEMS

DEBUG CONSOLE

•••

~

Ш

x

I

tr u e
r e s .e n d C ’ O k X n ');
Server-Response { .e v e n t s : O b je c t , .e v e n ts C o u n t:
> 1 , ..m o x U ste n e rs: u n d e fin e d , o u tp u tD ato : A rra y
( 0 ) , o u t p u t S iz e : 0 , _}
>
Ln 3, Col 3 Spaces 4 U TF-8 LF JavaScript Prettier

_____J

Рисунок 24. Ответ типа Transfer-Encoding: chunked
В этом случае не происходит автоматического вычисления общей длины
сообщения и браузеру предлагается принимать информацию порциями, а
потом выдать эти порции как единый информационный блок. Это возможно
благодаря тому, что методы write и end являются на самом деле методами
потоков.
^ Подробное освещение темы потоков не входит в задачи нашего
пособия - мы изложили лишь некоторые сведения в Елаве 2, но вы можете
самостоятельно проделать следующий эксперимент: импортировать класс
Stream из стандартного модуля node:stream и проверить тот факт, что объект
res является его инстансом (или экземпляром). Вы должны будете получить в
консоли значение true.
57

import { Stream } from 'stream';
const s = Server((req, res) => {
console.lo g ( res instanceof Stream);
debugger;

*);
Рисунок 25.

Объект res как инстанс потока

Далее, обратившись к документации по классу Stream, вы увидите, что
экземпляры этого класса поддерживают методы write и end. Используя метод
write можно посылать в поток порции информации. В данном случае это
поток, поддерживающий запись. А с помощью метода end можно завершить
отправку данных. Вызов метода end обязателен для завершения обработки
запроса. После его вызова уже нельзя вызвать write, а последующие вызовы
end игнорируются.

i:

II

?

І


Рисунок 26.

Т

О

ШШЯвШШ

у
%

Serv
Slop

corn ^

Выход из режима отладки

Для завершения отладки нажмём на Stop.
Итак, когда событие request возникает, система вызывает функциюобработчик и передаёт ей объекты, описывающие запрос и ответ. Они
являются экземплярами классов, соответственно, http.IncomingMessage и
http.ServerResponse. Соответствующие формальные параметры часто
называются req и res.
import h ttp from 'node:http';
import { EventEmitter j from 'node:events';
import { Readable. Stream } from 'node:stream';
const s = http
Server((req, res) => {
console.log(req instanceof http.IncomingMessage); true
console.log(req instanceof EventEmitter);
/ true
console.log(req instanceof Readable);
true
console.log(res instanceof http.ServerResponse);
console.log(res instanceof EventEmitter);
7true
console.log(res instanceof Stream );
xtrue
res. end ( ok );
})
,listen(() => console.log(s.address().port));
Листинг 10. Демонстрация того факта что req и res суть экземпляры
EventEmitter и потоков
58

Как было показано в Главе 2, при использовании модуля node:net мы
оперируем одним объектом (TCP-сокетом), который представляет собой
полнодуплексный поток, служащий и для отправки данных серверу, и для
ответа клиенту. При использовании node:http мы абстрагируемся от сокета и
работаем с двумя более высокоуровневыми сущностями, двумя потоками, и
через первый посылаем запрос серверу, а через второй получаем ответ.
Итак, мы отправляем данные, при необходимости разбивая их на порциичанки, для чего сначала сколько угодно раз вызываем метод write, затем в
конце один раз метод end:
const s = Server((req, res) => {
res.write('OK');
res.end(' );
});
Примечание
В цепочку
их объединить не получится: res.end возвращает
ServerResponce, a res.write возвращает true или false (true, если все
данные были успешно буферизованы, false если все или часть
данных были поставлены в очередь в памяти пользователя, событие
'drain' испускается, когда буфер снова свободен).
Выдавать можно какое угодно содержимое, в этом случае проблемой
клиента будет способ его обработки. Например, выдадим байты.
res.write(Buffer.from([l, 2, 33]));
res. end();
Это явная работа с двоичными данными, можно указать неявно
1\u 0 0 0 2 \u 0 0 0 1 \u 0 0 2 1 1

Также можно использовать изоморфный вариант с типизированным
массивом:
const bytes = new Uint8Array(3);

bytes[0] = 2; bytes[l] = 1; bytes[2]=34;
res.write(bytes);
Чтобы получить эти данные, выполняем: curl localhost:4321 -о file.dat
и смотрим содержимое файла в НЕХ-режиме.
Создать байты можно и напрямую из массива:
59

new Uint8A rray([2,1, 0x21]);
Если браузер получает содержимое, которое невозможно отобразить в
окне, то предлагает его скачать. Мы можем явно указать такое поведение с
помощью заголовка Content-Disposition, например 'Content-Disposition':
'attachment; filenam e-'file.dat'"
Мы можем судить о чайках, если исследуем сервер на Node как он есть,
без
промежуточных
серверов
типа
NGINX.
Настройка
chunked transfer encoding off; отключает порционную выдачу. Кроме того,
curl собирает в одну строку всё что приходит в виде чайков. Поэтому нужно
использовать клиент на Node и отслеживать событие data.

Упражнение 5
Сравните работу кода на РНР и на JavaScript/Node.js, запустив сервер и
посетив маршрут в браузере
Браузер ждёт всю
(pending)