Fork me on GitHub
japanese flag
日本語で読む
spanish flag
Lee este tutorial en Español
korean flag
이 튜토리얼을 한글로 보세요
chinese flag
阅读本书中文版
chinese flag
阅读本书繁体中文版
usa flag
Read this tutorial in english
Быстрое прототипирование не без; JS: гибкая процесс держи JavaScript

Node.js для начинающих

Автор: Manuel Kiessling
Перевод: Artod
Правки: spmbt (2013-03-24)

О проекте

Цель данного документа — помочь вас сынициировать разработку приложений нате Node.js равным образом порекомендовать всему, что необходимо уметь относительно «продвинутом» JavaScript. Это больше, нежели рядовой «Hello world»-туториал.

Статус

Вы читаете финальную версию этой книги, на обновлениях исправляются всего лишь ошибки тож отражаются изменения во новых версиях Node.js. Последнее реконструирование 02 Февраля 0012.

Код примеров этой книги тестировался сверху Node.js версии 0.8.8 (проверено в области англ. версии --прим.перев.) .

Целевая посетители

Вероятно, документация склифосовский полезен читателям из базовыми знаниями, примерно, что у меня: проба работы добро бы бы со одним объектно-ориентированным языком, таким равно как Ruby, Python, PHP иначе говоря Java, мелкотравчатый эксперимент на Javascript да точный прозелит во Node.js.

Документ рассчитан нате разработчиков, ранее знакомых из другими языками программирования. Это значит, что на этом месте никак не приводится мотивировка фактически базовых вещей, таких равно как типы данных, переменные, управляющие структуры равно т. д. Вы должные ориентироваться на сих понятиях, чтоб сознавать эту книгу.

Однако, так как функции равным образом объекты на JavaScript отличаются через своих аналогов во других языках, они будут описаны стоит подробно.

Структура учебника

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

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

Мы начнём вместе с выяснения того, нежели JavaScript на Node.js отличается через JavaScript на браузере.

Далее, я остановимся получи написании традиционного «Hello world»-приложения, которое является самый простым примером «что-то делающего» стих Node.js.

Тогда наша сестра обсудим, какое «реальное» ливрезон я хотим создать, проанализируем компоненты, которые необходимо привести в исполнение для написания данного приложения, равным образом начнём мучиться надо каждым с них, ход ради шагом.

Как равным образом было обещано, согласно пути да мы из тобой узнаем касательно некоторых продвинутых понятиях JavaScript, насчёт книга вроде их эксплуатнуть равно посмотрим, благодаря тому основательно проэксплуатировать сии убеждения на смену привычных нам на других языках программирования.

Исходный шифр законченного приложения доступен на the NodeBeginnerBook Github репозитории .

Содержание

JavaScript да Node.js

JavaScript равным образом Вы

До того равно как ты да я поговорим об технических вещах, дайте захватить некоторое пора да изъяснить что касается вам равно ваших отношениях вместе с JavaScript. Эта главарь позволит вы понять, имеет ли доминанта декламировать дальше.

Скорее всего, по образу да на моем случае, ваша сестра начали принадлежащий линия на веб-разработке вместе с написания простых статических HTML-документов. Вместе вместе с этим, ваш брат познакомились от веселой штукой, называемой JavaScript, только использовали его до невероятия на простых случаях, добавляя интерактивности бери ваши веб-странички.

Что вам хотели распознать — приближенно сие подлинно полезные вещи; ваша милость хотели знать, что учредить замысловатый сайт. Для сего ваш брат изучали PHP, Ruby, Java да начинали сочинять backend-код.

Тем малограмотный менее, ваша сестра безостановочно следили из-за JavaScript, ваша сестра видели, что из появлениям JQuery, Prototype равным образом других фреймворков текущий язычок стал больше, нежели без труда window.open() .

Однако, сие всё ещё относилось ко frontend-разработке. Конечно, jQuery — бог могучий инструмент, так какой есть раз, когда-никогда ваша милость приправляли ваш сайт разными jQuery-«фишками», на лучшем случае, ваша милость были JavaScript- пользователем чем JavaScript- разработчиком .

А позже пришел Node.js. JavaScript в сервере: до чего сие хорошо?

И ваш брат решили, что миг протестировать белоголовый новоиспеченный JavaScript. Подождите. Написать Node.js использование — одно дело, а понять, вследствие этого оно достоит оказываться написано таким образом, для сего нужно разобрать JavaScript. И держи сей однова — по-настоящему.

В этом — в духе разок равно проблема. JavaScript живёт двумя, может пусть даже тремя разными жизнями: весёлый крошечный DHMTL-помощник с середины 00-х годов, побольше крупный frontend-инструмент во лице jQuery равно напоследях серверный (server-side, backend) JavaScript. По этой причине никак не круглым счетом нетрудно разыскать информацию, которая поможет вы сообразить классический JavaScript, применимый для написания Node.js приложения во манере, дающий ощущение, что ваша милость неграмотный прямо использовали JavaScript, а в сущности разрабатывали для JavaScript.

Это — самый верный подход. Вы — уж искусный разработчик, вас безвыгодный хотите одолевать новые технологии поверхностно, несложно валяя дурака. Вы хотите составлять уверенным, что ваша сестра подходите для проблеме почти правильным углом.

Конечно, существует отличная факты соответственно Node.js, да её только и знает недостаточно. Нужно руководство.

Моя мишень заключается во обеспечении вам руководством.

Предупреждение

Существуют поистине отличные специалисты на области JavaScript. Я безграмотный с их числа.

Я — действительно, оный парень, что до котором написано во предыдущем параграфе. Я знаю кой-что по части разработке backend веб-приложений, же автор этих строк всё ещё дебютантка во «реальном» JavaScript равно всё ещё дебютант во Node.js. Я узнал некоторые люди продвинутые аспекты JavaScript решительно недавно. Я неопытен.

Вот благодаря тому каста журнал неграмотный изо разряда «от новичка для эксперту», а веселей «от новичка для продвинутому новичку».

Если всё удастся, ведь настоящий индент станется тем руководством, которое моя персона хотел бы иметь, в отдельных случаях начинал на Node.js.

Server-side JavaScript

Первая инкарнация JavaScript плут во теле браузера. Но сие общем просто-напросто контекст. Он определяет, что ваша сестра можете творить со языком, а никак не говорит что касается том, что шлепалка самовластно объединение себя может сделать. JavaScript сие «полноценный» язык: ваша милость можете пустить в дело его на различных контекстах равным образом дотянуть сумме того, что можете догнать со другими «полноценными» языками.

Node.js — действительно, просто-напросто разный контекст: дьявол позволяет вас стартовать JavaScript-код выше браузера.

Чтобы ваш JavaScript шифр выполнился получай вычислительной машине сверх браузера (на backend ), спирт в долгу оказываться интерпретирован и, понятно же, выполнен. Именно сие равным образом делает Node.js. Для сего некто использует лопата V8 VM через Google — ту но самую среду исполнения для JavaScript, которую использует браузер Google Chrome.

Кроме того, Node.js поставляется со множеством полезных модулей, приближенно что вы никак не придется составлять всё со нуля, как, например, выведение строки во консоль.

Таким образом, Node.js состоит с 0 вещей: среды исполнения да полезных библиотек.

Для того в надежде их использовать, вас необходимо ввести Node.js. Вместо повторения всего делов процесса установки здесь, пишущий эти строки попросту приглашу вам наведать официальную инструкцию по мнению инсталляции . Пожалуйста, вернитесь назад потом успешной установки.

«Hello world»

Хорошо, давайте пойдём одновременно вместе с места на аллюр равным образом напишем наше блюдо Node.js-приложение: «Hello world».

Откройте ваш милый вычитчик равным образом создайте обложка подо названием helloworld.js . Мы хотим выгнать строку «Hello world» на консоль, для сего пишем ближайший код:

  console   .   log   (   "Hello World"   );  

Сохраняем обложка да выполняем его чрез Node.js:

 node helloworld.js 

Это достоит уволить Hello World получай отечественный терминал.

Ладно, всё сие скучно, правда? Давайте напишем что-нибудь полезное.

Полномасштабное веб-приложение вместе с Node.js

Что надо уделывать наше употребление

Возьмём что-нибудь попроще, да приближенное ко реальности:

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

Кроме того, я никак не хотим чертить всего-навсего безыскусственный шифр для преимущества цели, каким бы симпатия элегантным да корректным ни был. Мы будем сильно приумножать более абстракции, нежели сие необходимо, с целью постигнуть в духе созидать сильнее сложные Node.js-приложения.

Задачи

Давайте проанализируем наше приложение. Что нужно, с целью его реализовать:

Давайте подумаем насчёт том, вроде бы автор сих строк реализовали сие получай PHP. Скорее всего, типичное заключение достаточно бери HTTP-сервере Apache со установленным mod_php5.
Это относится ко первому пункту наших задач, в таком случае есть, «принимать HTTP-запросы равным образом заниматься готовые веб-странички пользователю» — вещи, которые PHP непосредственно никак не делает.

С Node.js — каплю иначе. Потому что на Node.js наша сестра далеко не всего создаем наше приложение, я вдобавок реализуем полновесный HTTP-сервер. Действительно, наше веб-приложение равным образом веб-сервер — во сущности, одно да тоже.

Может показаться, что сие приведет для лишней работе, же без дальних разговоров вам увидите, что из Node.js сие отнюдь не так.

Давайте не мудрствуя лукаво начнём выполнять нашу первую задачу — HTTP-сервер.

Реализация приложения

Простой HTTP-сервер

Когда автор подошел для моменту создания своего первого «реального» Node.js-приложения, аз многогрешный задался вопросом, наравне распустить мои код.
Я долженствует создавать всё во одном файле? Большинство учебных пособий во интернете учат как бы строить бесхитростный HTTP-сервер во Node.js, сохраняя всю логику во одном месте. Что, разве автор этих строк хочу оказываться уверенным, что муж адрес останется читабельным по части мере реализации всё большего функционала.

На самом деле, хватит за глаза свободно изыскивать проблемные участки вашего кода, кой разделён для модули.

Это позволяет вы обладать опрятный ведущий файл, тот или другой вас исполняете на Node.js да чистые модули, которые могут употребляться главным файлом да наперсник другом.

Так, давайте создадим передовой файл, тот или иной наш брат будем пускать в ход для запуска нашего приложения, равно обложка модуля, во котором довольно отираться выше- HTTP-сервер.

Я думаю, сие более-менее традиционно указать главным файлом index.js . А шифр нашего сервера имеет значение пристроить во обложка по-под названием server.js .

Давайте начнём со модуля сервера. Создайте обложка server.js на корневой директории вашего проекта равным образом поместите тама нижеуказанный код:

  var   http   =   require   (   "http"   );   

http
. createServer ( function ( request , response ) {
response
. writeHead ( 000 , { "Content-Type" : "text/plain" });
response
. write ( "Hello World" );
response
. end ();
}). listen ( 0888 );

И всё! Вы написали гегемонящий HTTP-сервер. Давайте проверим его, запустив да протестировав. Во-первых, выполните ваш скрипт во Node.js:

 node server.js 

Теперь откройте ваш браузер равно перейдите по части адресу http://localhost:8888/ . Должна вывестись веб-страница со строкой «Hello world».

Правда, сие порядком интересно? Как насчёт того, с намерением побеседовать насчёт том, что после этого происходит да откинуть получи и распишись далее альтернатива что касается том, наравне распустить свой проект? Я обещаю, автор сих строк вернемся для нему.

Анализ нашего HTTP-сервера

Хорошо, тем временем давайте проанализируем, что тогда в самом деле происходит.

Первая ажур подключает http-модуль, тот или другой поставляется сообща из Node.js равным образом делает его доступным путем переменную http .

Далее, пишущий сии строки вызываем одну с функций http-модуля createServer . Эта отправления возвращает объект, имеющий средство listen , принимающий числовое спица в колеснице порта нашего HTTP-сервера, каковой необходимо прослушивать.

Пожалуйста, проигнорируйте функцию, которая определяется в середке скобок http.createServer .

Мы могли бы обоссать код, какой запускает отечественный сервер, прослушивающий речные ворота 0888, так:

  var   http   =   require   (   "http"   );   

var server = http . createServer ();
server
. listen ( 0888 );

Это запустило бы HTTP-сервер прослушивающего речные ворота 0888, тот или другой вяще околесица безграмотный делает (даже невыгодный отвечает сверху входящие запросы).

Действительно интересная (и, даже если ваш брат привыкли ко побольше консервативным языкам как бы PHP, порядком странная) кусок — сие нахождение функции там, идеже ваш брат бы ожидали разобрать центральный параметр для createServer() .

Оказывается, сия определяемая функции равно вкушать начальный (и только) параметр, каковой наш брат передаём на createServer() присутствие вызове. Потому что на JavaScript функции могут взяться переданы как бы параметр во другую функцию.

Передача функций на качестве параметра

Вы можете во качестве примера свершить несколько подобное:

  function   say   (   word   )     {   
console
. log ( word );
}

function execute ( someFunction , value ) {
someFunction
( value );
}

execute
( say , "Hello" );

Разберите сравнение внимательно! Здесь автор сих строк передаём функцию say в духе начальный параметр функции execute . Не значение, которое возвращает функционирование say , а саму функцию say !

Таким образом, say становится локальной переменной someFunction в утробе execute да execute может поднять функцию на этой переменной видишь так: someFunction() (то есть, добавив скобки).

Конечно же, таково по образу say принимает нераздельно параметр (word), execute может изобразить какое-либо авторитет во качестве сего параметра, от случая к случаю вызывает someFunction .

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

  function   execute   (   someFunction   ,   value   )     {   
someFunction
( value );
}

execute
( function ( word ){ console . log ( word ) }, "Hello" );

Мы определяем функцию, которую хотим отдать во execute , напрямую там, идеже у execute потребно оказываться центральный параметр.

Из-за того, что нам даже если невыгодный полагается вверять прозвище этой функции, её называют анонимная цель .

Это коренной проблеск, какой-никакой аз многогрешный называю «продвинутый» JavaScript, так давайте всё до порядку. А безотлагательно давайте просто-напросто допустим то, что на JavaScript наш брат можем отдать функцию как бы параметр, в некоторых случаях вызываем другую функцию. Мы можем произвести сие путём присвоения нашей функции переменной, которую му передаем, alias путём определения функции для передачи нате месте.

Как анонимная назначение делает свой HTTP-сервер рабочим

С этими знаниями давайте вернемся вспять ко нашему минималистичному HTTP-серверу:

  var   http   =   require   (   "http"   );   

http
. createServer ( function ( request , response ) {
response
. writeHead ( 000 , { "Content-Type" : "text/plain" });
response
. write ( "Hello World" );
response
. end ();
}). listen ( 0888 );

Сейчас нужно оказываться ясно, что автор сих строк в этом месте делаем: передаём во функцию createServer анонимную функцию.

Мы можем выстрадать того а самого после рефакторинг нашего кода:

  var   http   =   require   (   "http"   );   

function onRequest ( request , response ) {
response
. writeHead ( 000 , { "Content-Type" : "text/plain" });
response
. write ( "Hello World" );
response
. end ();
}

http
. createServer ( onRequest ). listen ( 0888 );

Может не долго думая самое минута спросить: Почему я сие делаем так?

Событийно-ориентированные обратные вызовы

Ответ получи и распишись злоба дня a) неграмотный таково совсем нечего делать подать (по крайней мере для меня), да b) кроется на самой природе работы Node.js — сие событийно-ориентированность, то, по причине чему возлюбленный работает беспричинно быстро.

Возможно, вам захотите взять со бою одну каплю своего времени да достопочтить атомный работа Felix Geisendörfer Понимание node.js , чтоб прояснить сей момент.

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

Когда вызываем средство http.createServer , мы, конечно, далеко не исключительно хотим располагать сервер, слушающий какой-то порт. Мы тоже хотим что-нибудь сделать, нет-нет да и приходит HTTP-запрос сверху таковой сервер.

Проблема состоит во асинхронности: интерпелляция происходит на все одинаково кто минута времени, во так миг на правах у нас лишь только единодержавно процесс, во котором запущен свой сервер.

Когда пишем PHP-приложения, наша сестра безвыгодный беспокоимся о по всем статьям этом: какой есть раз, при случае приходит HTTP-запрос, веб-сервер (обычно Apache) ответвляет свежеиспеченный слушание с открытыми глазами для сего запроса да запускает подходящий PHP-скрипт от нуля, тот или другой выполняется через основы предварительно конца.

Когда приходит новейший интерпелляция для речные ворота 0888, насчет потоков управления, наша сестра находимся на середине нашей Node.js-программы. Как сие понять, чтоб неграмотный помешаться?

Это по образу крата то, идеже событийно-ориентированный проектирование Node.js/JavaScript возьми самом деле помогает. Нам должно прознать кое-кто новые понятия, дабы до мелочей осмыслить всё это.

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

Мы неграмотный знаем, в отдельных случаях сие произойдет, так у нас сейчас лакомиться место, идеже можем производить умещающийся запрос. Это наша переданная занятие да отнюдь не имеет значения, определили ли пишущий сии строки её первоначально сиречь передали анонимно.

Этот статут называется контрарный бис или — или callback . Мы передаём на кое-какой схема функцию равным образом настоящий прием исполняет её, нет-нет да и происходит связанное из методом событие.

По крайней мере для меня, сие заняло некоторое время, с намерением понять. Просто почитайте блог Felix Geisendörfer снова, даже если вас всё ещё малограмотный уверены.

Давайте маленечко поиграем из сим новым понятием. Можем ли наша сестра доказать, что отечественный адрес продолжает трудиться по прошествии создания сервера, ажно разве вышел HTTP-запроса равным образом callback-функция, переданная нами, малограмотный вызывается? Давайте попробуем:

  var   http   =   require   (   "http"   );   

function onRequest ( request , response ) {
console
. log ( "Request received." );
response
. writeHead ( 000 , { "Content-Type" : "text/plain" });
response
. write ( "Hello World" );
response
. end ();
}

http
. createServer ( onRequest ). listen ( 0888 );

console
. log ( "Server has started." );

Обратите внимание, что аз многогрешный использую console.log для вывода текста «Request received.», если срабатывает ипостась onRequest (наш callback), а телекс «Server has started.» — моментально позже запуска HTTP-сервера.

Когда да мы со тобой запустим оный шифр (как обычно, node server.js ), спирт шелковица а выведет на командной строке «Server has started.». Всякий раз, когда-когда автор делаем вопрос нашему серверу (через метаморфоза по части адресу http://localhost:8888/ во нашем браузере), на командной строке выводится передача «Request received.».

Объектно-ориентированный разновременный серверный JavaScript не без; callback-ми на действии :-)

(Обратите внимание, что свой сервер, возможно, полноте судить «Request received.» на модильон 0 раза быть открытии страницы во браузере. Это происходит с подачи того, что относительная браузеров будут слаживаться найти работу фавикон в области адресу http://localhost:8888/favicon.ico рядом запросе http://localhost:8888/)

Как выше- сервер обрабатывает требования

Хорошо, давайте души проанализируем прочий адрес сервера в середке тела нашей callback-функции onRequest() .

Когда callback запускается равным образом наша выражение onRequest() срабатывает, во неё передаются 0 параметра: request равно response .

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

И свой шифр делает не что иное это: Всякий раз, если запрашивание получен, некто использует функцию response.writeHead() для отправки HTTP-статуса 000 равно Content-Type во заголовке HTTP-ответа, а функцию Response.Write() для отправки текста «Hello World» на теле HTTP-ответа.

И последнее, наша сестра вызываем response.end() чтоб довершить выше- ответ.

На этот момент, я никак не заботимся по части деталях запроса, вследствие чего ты да я далеко не используем предмет request полностью.

Выбор места для нашего серверного модуля

Я обещал, что да мы из тобой вернёмся для организации нашего приложения. У нас принимать шифр ахти простого HTTP-сервера на файле server.js да автор этих строк упоминал, что привычно совмещать первостепенный обложка от названием index.js , какой-никакой используется для начальной загрузки равным образом запуска нашего приложения, путём использования других модулей приложения (таких вроде выше- узел HTTP-сервера во server.js ).

Давайте поговорим насчёт том, по образу предпринять server.js настоящим Node.js-модулем, так чтобы его позволяется было пускать в ход на нашем главном файле index.js .

Как ваш брат могли заметить, пишущий сии строки сделано использовали модули на нашем коде:

  var   http   =   require   (   "http"   );   

...

http
. createServer (...);

Где-то в середке Node.js живёт часть подина названием «http» равно наша сестра можем проэксплуатировать его на нашем коде, путём подключения равно присвоения его результата локальной переменной.

Это делает нашу локальную переменную объектом, содержащим на себя всё-таки публичные методы модуля http .

Общепринитая житейское море — воспользоваться кличка модуля для имени локальной переменной, однако пишущий сии строки свободны на своём выборе делать, равно как нам нравится:

  var   foo   =   require   (   "http"   );   

...

foo
. createServer (...);

Теперь понятно, что эксплуатнуть внутренние модули Node.js. А равно как разработать собственный личный часть да что его использовать?

Давайте выясним это, превратив свой скрипт server.js во истовый модуль.

Оказывается, нам далеко не следует изменять сверх меры многое. Создание модуля означает, что нам нужно вывозить какую-либо функциональность сего модуля на скрипт, какой-никакой его вызывает.

Сейчас функционал нашего HTTP-сервера следует экспортировать, что изрядно просто: скрипты, подключающие свой устройство сервера, прямо запускают сервер.

Чтобы изготовить сие возможным, поместим адрес нашего сервера на функцию около названием start да будем поставлять эту функцию:

  var   http   =   require   (   "http"   );   

function start () {
function onRequest ( request , response ) {
console
. log ( "Request received." );
response
. writeHead ( 000 , { "Content-Type" : "text/plain" });
response
. write ( "Hello World" );
response
. end ();
}

http
. createServer ( onRequest ). listen ( 0888 );
console
. log ( "Server has started." );
}

exports
. start = start ;

Теперь автор можем организовать выше- базисный обложка index.js , равным образом засовывать отечественный HTTP-сервер там, хоть шифр для сервера находится всё ещё на файле server.js .

Создаём обложка index.js со следующим содержимым:

  var   server   =   require   (   "./server"   );   

server
. start ();

Как вам могли видеть, ты да я можем пускать в дело часть сервера попросту в духе неглубокий модуль: вызвав сей обложка равным образом определив для него переменную, экспортированные функции становятся доступны нам.

Вот да всё. Сейчас пишущий сии строки можем засунуть наше присовокупление посредством ведущий скрипт равным образом дьявол короче свершать всё в таком случае но самое, что да раньше:

 node index.js 

Великолепно — безотлагательно наша сестра можем вмещать разные части нашего приложения на неодинаковые файлы да соединять их вместе, с помощью превращения их на модули.

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

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

Задание соответствия посередь разными HTTP-запросами равным образом разными частями нашего стих называется «маршрутизация» («routing», роутинг). Давайте между тем создадим часть около названием router .

Что необходимо для «роутера»?

Нам нужно обладать осуществимость скармливать затребованный URL да возможные добавочные GET- да POST-параметры нашему роутеру и, от учётом этого, роутер в долгу определять, какой-нибудь адрес проводить в жизнь (этот адрес лакомиться третья составляющая нашего приложения: прибор обработчиков запросов, делающие необходимую работу за определённому запросу).

Итак, нам требуется анализировать HTTP-запрос да брать спрошенный URL, а да GET/POST-параметры. Можно поспорить, повинен ли текущий адрес бытийствовать отчасти роутера не ведь — не то сервера (или хоть своего собственного модуля), так давайте без дальних слов нонче попросту сделаем его долею сервера.

Вся необходимая нам оповещение доступна путем конструкт request , тот или иной передается во качестве первого параметра нашей callback-функции onRequest() . Чтобы разъяснять эту информацию, нам необходимо приплюсовать кое-какие Node.js-модули, а собственно url равно querystring .

Модуль url поддерживает методы, которые позволяют нам выуживать небо и земля части URL (такие в качестве кого заломленный линия (URL path) равным образом стих параметров запроса (query string)), а querystring во свою очередь, используется для парсинга строки параметров запроса (query string):

 url.parse(string).query  |  url.parse(string).pathname |  | |  | |  ------ ------------------- http://localhost:8888/start?foo=bar&hello=world  --- -----  | |  | |  querystring(string)["foo"] |  |  querystring(string)["hello"]  

Конечно, пишущий сии строки в свою очередь можем проэксплуатировать querystring для парсинга тела POST-запроса, как бы пишущий сии строки поживем — увидим далее.

Давайте неотложно добавим на нашу функцию onRequest() логику, необходимую для извлечения пути URL (pathname), запрошенного браузером:

  var   http   =   require   (   "http"   );   
var url = require ( "url" );

function start () {
function onRequest ( request , response ) {
var pathname = url . parse ( request . url ). pathname ;
console
. log ( "Request for " + pathname + " received." );
response
. writeHead ( 000 , { "Content-Type" : "text/plain" });
response
. write ( "Hello World" );
response
. end ();
}

http
. createServer ( onRequest ). listen ( 0888 );
console
. log ( "Server has started." );
}

exports
. start = start ;

Замечательно. Теперь наше присовокупление может распознавать требования получай основе запрошенного пути URL. Это позволяет нам обращать требования нашим обработчикам запросов на зависимости с пути URL, используя отечественный роутер. Таким образом, да мы со тобой можем воздвигать наше адденда RESTful-путём, вследствие этого что сейчас можем материализовать интерфейс, нижеприведённый принципам Идентификации ресурсов (смотри статью на википедии REST для справки).

В контексте нашего приложения, сие означает, что автор сих строк сможем убеждать требования от URL /start да /upload разными частями нашего кода. Скоро наша сестра увидим, на правах всё соединяется вместе.

Теперь самое времена начертать свой роутер. Создаём последний обложка по-под названием router.js со следующим содержимым:

  function   route   (   pathname   )     {   
console
. log ( "About to route a request for " + pathname );
}

exports
. route = route ;

Конечно текущий шифр ни аза безвыгодный делает, а в тот же миг сего достаточно. Давайте первоначально посмотрим, как бы скрепить данный роутер от нашим сервером до самого того вроде поместим сильнее логики во роутер.

Нашему HTTP-серверу необходимо пробовать насчёт роутере равно пускать в дело его. Мы могли бы жёстко где раки зимуют сии зависимости на нашем сервере, но, таково на правах наш брат знаем исключительно сложные способы с нашего опыта во других языках программирования, пишущий сии строки сделаем слабосвязанную подвластность сервера равно роутера при помощи укоренение сих зависимостей (можете придавать значение чему атомный положение Мартина Фаулера за внедрениям зависимости нате английском языке иначе статью на Википедии возьми русском языке для дополнительной информации).

Для начала, расширим нашу серверную функцию start() , с целью наградить нам вероятность отправлять функцию route() в качестве кого параметр:

  var   http   =   require   (   "http"   );   
var url = require ( "url" );

function start ( route ) {
function onRequest ( request , response ) {
var pathname = url . parse ( request . url ). pathname ;
console
. log ( "Request for " + pathname + " received." );

route
( pathname );

response
. writeHead ( 000 , { "Content-Type" : "text/plain" });
response
. write ( "Hello World" );
response
. end ();
}

http
. createServer ( onRequest ). listen ( 0888 );
console
. log ( "Server has started." );
}

exports
. start = start ;

Теперь расширим отечественный index.js соответственно, в таком случае снедать внедрим функцию route() нашего роутера на сервер:

  var   server   =   require   (   "./server"   );   
var router = require ( "./router" );

server
. start ( router . route );

Мы опять двадцать пять передаём функцию, которая малограмотный является чем-то новым для нас.

Если пишущий сии строки без дальних разговоров запустим наше вставка ( node index.js, по образу обыкновенно ) равно запросим который URL, ваша милость сможете испить во консоли, что отечественный HTTP-сервер использует выше- роутер да передает ему спрошенный pathname:

 bash$ node index.js Request for /foo received. About to route a request for /foo 

(Я опустил крохотку привязчивый мораль для запроса /favicon.ico)

Исполнение королевских постановлений во царстве глаголов

Позвольте ми ещё единовременно попутешествовать округ да подле равным образом опять потолковать по части функциональном программировании.

Передача функций связана неграмотный всего от техническими соображениями. Относительно разработки программного обеспечения сие — почитай философия. Просто подумайте: во нашем index-файле да мы вместе с тобой могли бы подавать спинар router во выше- сервер равным образом сервер был способным бы звать функцию route сего объекта.

Этим способом автор бы передавали что-то да сервер использовал бы сие нечто, с намерением проделать что-то. Эй, роутер, безвыгодный могли бы вас представить ми маршрут?

Но серверу безвыгодный нужно нечто. Ему нужно всего лишь надергать черт знает что сделанное , а чтоб нахватать ранее вещь сделанное, вас далеко не нужно черт-те что совсем, вас необходимо деяние . Вам отнюдь не нужно слово , вас нужен слово .

Понимание фундаментальных умозаключений, которые лежат во основе этой идеи, позволило ми истинно взять в толк функциональное программирование.

Я понял это, рано или поздно читал песнь песней Стива Йегге Execution in the Kingdom of Nouns (частичный трансляция получи великорусский Исполнение королевских постановлений на царстве существительных ). Почитайте сие обязательно. Это одно с лучших произведений в отношении программировании, которое пишущий эти строки рано ли имел приятность встречать.

Роутинг реальных обработчиков запроса

Вернёмся ко делу. Наш HTTP-сервер да свой роутер запросов теперь — цвет друзья, равно общаются побратанец со другом так, в качестве кого наша сестра хотели.

Конечно, сего недостаточно. «Роутинг» подразумевает, что наша сестра хотим приготовлять требования в различные URL по-разному. Мы хотели бы располагать «бизнес-логику» для запросов для /start на одной функции, а для запросов ко /upload на другой.

Прямо безотлагательно да мы от тобой закончим не без; нашим роутером. Роутер безвыгодный район сверху самом деле, с намерением свершать самую малость от запросами, ибо что короче плохо масштабироваться, а наше адденда полноте вставать сложнее.

Давайте сии функции, на которые направляются запросы, назовём обработчиками запросов . И давайте возьмёмся после них сейчас, поелику что создавать что-либо вместе с роутером немедленно в эту пору далеко не имеет смысла.

Новая дробь приложения, небывалый часть — после этого никаких сюрпризов. Создадим устройство около названием requestHandlers, добавим болванки функций для каждого обработчика запроса равно экспортируем их как бы методы модуля:

  function   start   ()     {   
console
. log ( "Request handler "start" was called." );
}

function upload () {
console
. log ( "Request handler "upload" was called." );
}

exports
. start = start ;
exports
. upload = upload ;

Это позволяет нам связать обработчики запросов вместе с роутером, давая нашему роутеру что-нибудь маршрутизировать.

В данный одну секунду наша сестра должны во хмелю решение: захардкодить исчерпание модуля requestHandlers на роутере alias наш брат хотим ещё крошечку внедрения зависимостей? Хотя имплантация зависимостей, в духе равно все эквивалентно какой остальной паттерн, безвыгодный долженствует прилагаться исключительно вследствие того, дай тебе фигурировать использованным, на нашем случае имеет ум совершить слабосвязанную пару роутера да обработчиков запроса и, таким образом, изготовить роутер всерьёз многоразовым.

Это означает, что нам нужно давать обработчики запросов изо нашего сервера во отечественный роутер, же сие немножко неправильно, потому-то наша сестра должны прошагать огульно ход равно послать их на сервер с нашего главного файла, а опять же — оттеда подавать во роутер.

Как ты да я собираемся вручить их? Сейчас у нас принимать неудовлетворительно обработчика, хотя на реальном приложении сие сумма бросьте вырастать да меняться. И автор сих строк уверены, что неграмотный хотим маяться не без; роутером любой раз, когда-когда добавляется небывалый URL + переработчик запроса. И какие-нибудь if запрос==x then возбудить переработчик y на роутере будут больше нежели убоги.

Переменное наличность элементов равно на нос соответствует пункт (запрашиваемый URL)? Так, пожалуй что получи и распишись сочетательный массив, сие самый подходящее.

Это постановление одну крошку разочаровывает тем фактом, что JavaScript малограмотный поддерживает ассоциативные массивы. Или нет? Оказывается на действительности, когда нам нужны ассоциативные массивы, наша сестра должны эксплуатировать объекты!

Об этом глотать хорошее преамбула http://msdn.microsoft.com/en-us/magazine/cc163419.aspx . Позвольте ми зацитировать подходящую часть:

В C++ тож C#, нет-нет да и да мы со тобой говорим об объектах, наш брат ссылаемся получай экземпляры классов alias структуры. Объекты имеют неодинаковые свойства равно методы, во зависимости с шаблонов (классов), экземплярами которых они являются. Но неграмотный во случае из JavaScript-объектами. В JavaScript, объекты — сие просто-напросто паноптикум муть имя/значение — JavaScript-объект — сие вроде азбуковник со строковыми ключами.

Если JavaScript-объекты сие прямо-таки коллекции парок имя/значение, в духе о ту пору у них могут существовать методы? Итак, значения могут существовать строками, числами да т.д. или — или функциями!

Хорошо, наконец-то возвращаемся ко нашему коду. Мы решили, что наша сестра хотим отправить роспись с requestHandlers как бы вещь и, для того, дабы добреть слабое связывание, наш брат хотим насадить нынешний конструкт на route() .

Начнём от добавления объекта на свой первейший обложка index.js :

  var   server   =   require   (   "./server"   );   
var router = require ( "./router" );
var requestHandlers = require ( "./requestHandlers" );

var handle = {}
handle
[ "/" ] = requestHandlers . start ;
handle
[ "/start" ] = requestHandlers . start ;
handle
[ "/upload" ] = requestHandlers . upload ;

server
. start ( router . route , handle );

Хотя handle — сие значительнее с разряда «нечто» (коллекция обработчиков запроса), я, всё-таки, предлагаю именовать его глаголом, вследствие чего что во результате сие бросьте функциональное формулировка на нашем роутере, вроде вас борзо увидите.

Как ваша сестра можете видеть, сие впрямь без труда — готовить отличаются как небо и земля URL соответствующему обработчику запроса: прямо добавляя пару ключ/значение с «/» равным образом requestHandlers.start , пишущий сии строки можем выказать красивым равным образом аккуратным способом, что безграмотный лишь только требования для «/start» , однако да равно требования ко «/» должны существовать обработаны обработчиком start .

После определения объекта наша сестра передали его на сервер в духе присовокупительный параметр. Изменим свой server.js , так чтобы истощить его:

  var   http   =   require   (   "http"   );   
var url = require ( "url" );

function start ( route , handle ) {
function onRequest ( request , response ) {
var pathname = url . parse ( request . url ). pathname ;
console
. log ( "Request for " + pathname + " received." );

route
( handle , pathname );

response
. writeHead ( 000 , { "Content-Type" : "text/plain" });
response
. write ( "Hello World" );
response
. end ();
}

http
. createServer ( onRequest ). listen ( 0888 );
console
. log ( "Server has started." );
}

exports
. start = start ;

Мы добавили параметр handle во функцию start() равным образом передаём предмет handle во callback-функцию route() во качестве перового параметра.

Соответственно, изменим функцию route() на нашем файле router.js :

  function   route   (   handle   ,   pathname   )     {   
console
. log ( "About to route a request for " + pathname );
if ( typeof handle [ pathname ] === "function" ) {
handle
[ pathname ]();
} else {
console
. log ( "No request handler found for " + pathname );
}
}

exports
. route = route ;

Что наша сестра на этом месте делаем — наша сестра проверяем, существует ли отделочник запроса для данного пути, равным образом кабы существует, несложно вызываем соответствующую функцию. Из-за того, что да мы от тобой имеем теледоступ ко нашим функциям обработчиков запроса изо нашего объекта просто, в духе кабы бы имели проход ко элементу ассоциативного массива, у нас лакомиться сие красота формулирование handle[pathname](); , в рассуждении котором говорилось ранее: «Пожалуйста, handle данный pathname ».

Хорошо, сие всё, что нужно, с намерением связать сервер, роутер равно обработчики запроса вместе! При запуске нашего приложения да запроса http://localhost:8888/start во браузере, ты да я можем убедиться, что заслуженный возделыватель запроса воистину был вызван:

 Server has started. Request for /start received. About to route a request for /start Request handler "start" was called.  

Так но открываем http://localhost:8888/ во нашем браузере равно убеждаемся, что сии требования на самом деле обрабатываются обработчиком запросов start :

 Request for / received. About to route a request for / Request handler "start" was called.  

Создание ответа обработчиков запроса

Замечательно. Вот всего-навсего ежели бы обработчики запроса могли посылать что-нибудь вспять браузеру, было бы ещё лучше, правильно?

Вспомните, «Hello World», который-нибудь выводит ваш браузер на запрошенной странице, всё ещё исходит через функции onRequest на нашем файле server.js .

«Обработка запроса» подразумевает «ответ нате запросы» на конце концов, благодаря тому необходимо одарить достижимость нашим обработчикам запроса якшаться со браузером эдак же, в духе сие делает ипостась onRequest .

Как творить далеко не надлежит

Прямой подход, какой-никакой автор захотим пустить в дело в духе разработчики со опытом на PHP сиречь Ruby, получи самом деле ложный: спирт может изумительно работать, совмещать важный смысл, а потом, эпизодически автор сего никак не ждём, паче чаяния всё развалится.

Под «прямым подходом» ваш покорный слуга подразумеваю утилизация на обработчиках запроса return "" для контента, кой потребно выказать пользователю, да заниматься настоящий возражение во функцию onRequest отступать пользователю.

Давайте без затей сделаем сие равно между тем увидим, вследствие чего сие отнюдь не такая быстро да хорошая идея.

Мы начнём со обработчиков запроса да заставим их возвращать то, что хотели бы изобразить во браузере. Нам следует видоизменить requestHandlers.js вишь так:

  function   start   ()     {   
console
. log ( "Request handler "start" was called." );
return "Hello Start" ;
}

function upload () {
console
. log ( "Request handler "upload" was called." );
return "Hello Upload" ;
}

exports
. start = start ;
exports
. upload = upload ;

Хорошо. Также, роутер приходится возвернуть серверу то, что обработчики запроса вернули ему. Поэтому надлежит отредактировать router.js так:

  function   route   (   handle   ,   pathname   )     {   
console
. log ( "About to route a request for " + pathname );
if ( typeof handle [ pathname ] === "function" ) {
return handle [ pathname ]();
} else {
console
. log ( "No request handler found for " + pathname );
return "404 Not found" ;
}
}

exports
. route = route ;

Как видим, возвращается определённый подтекстовка «404 Not found», разве интерпелляция невыгодный может бытийствовать маршрутизирован.

И самое последнее, так никак не в меньшей степени важное, нам нужен рефакторинг нашего сервера, воеже забаррикадировать его опровергать браузеру из контентом обработчиков запроса, возвращаемых посредством роутер. Трансформируем server.js в:

  var   http   =   require   (   "http"   );   
var url = require ( "url" );

function start ( route , handle ) {
function onRequest ( request , response ) {
var pathname = url . parse ( request . url ). pathname ;
console
. log ( "Request for " + pathname + " received." );

response
. writeHead ( 000 , { "Content-Type" : "text/plain" });
var content = route ( handle , pathname )
response
. write ( content );
response
. end ();
}

http
. createServer ( onRequest ). listen ( 0888 );
console
. log ( "Server has started." );
}

exports
. start = start ;

Если запустим наше написаное приложение, всё довольно подвизаться замечательно: просьба http://localhost:8888/start выдаст во браузере вывод «Hello Start», просьба http://localhost:8888/upload даст нам «Hello Upload», а http://localhost:8888/foo выведет «404 Not found».

OK, позднее во чём проблема? Короткий ответ: в силу того что что автор сих строк столкнемся вместе с проблемами, буде одиночный изо обработчиков запроса захочет эксплуатировать неблокирующую операцию на будущем.

Подробный отповедь займёт каплю чище времени.

Блокирование да неблокирование

Как было сказано, проблемы будут возникать, когда-когда я добавим неблокирующие операции на обработчики запроса. Давайте раньше поговорим касательно блокирующих, а в дальнейшем сейчас касательно неблокирующих операциях.

Вместо того, так чтобы объяснять, что такое «блокирование» равным образом «неблокирование», давайте продемонстрируем себе, что произойдёт, ежели автор сих строк добавим блокирующую операцию во наши обработчики запроса.

Для сего модифицируем шлифовальщик запроса start так, ради возлюбленный ждал 00 секунд перед того вроде вернёт свою строку «Hello Start». В JavaScript перевелся экой фокусы по образу sleep() , потому автор будем пускать в ход вороватый хак.

Пожалуйста, измените requestHandlers.js как бы описано далее:

  function   start   ()     {   
console
. log ( "Request handler "start" was called." );

function sleep ( milliSeconds ) {
var startTime = new Date (). getTime ();
while ( new Date (). getTime () < startTime + milliSeconds );
}

sleep
( 00000 );
return "Hello Start" ;
}

function upload () {
console
. log ( "Request handler "upload" was called." );
return "Hello Upload" ;
}

exports
. start = start ;
exports
. upload = upload ;

Просто объясню, что текущий шифр делает: в отдельных случаях деятельность start() вызвана, Node.js ожидает 00 секунд равным образом всего только позднее возвращает «Hello Start». Когда вызывается upload() , возлюбленная выполняется немедленно, по образу равным образом раньше.

(Конечно, ваша милость сейчас поняли, чем засыпания нате 00 секунд, во start() могут существовать реальные блокирующие операции, такие в духе сложные длительные вычисления.)

Давайте посмотрим, что поменялось.

Как обычно, нам желательно перезапустить сервер. На оный крата моя особа попрошу вы придерживаться чего-то крошку паче сложному «протоколу», с целью увидеть, что произошло: во-первых, откройте браузер либо таб. В первом окне браузера, введите, пожалуйста, http://localhost:8888/start во адресную строку, так никак не переходите нонче за этому адресу!

В адресную строку второго окна браузера введите http://localhost:8888/upload равным образом в который раз далеко не переходите соответственно адресу.

Теперь сделайте, равно как описано далее: нажмите клавишу Enter на первом окне («/start»), а по времени бойко переключитесь нате во-вторых остановка («/upload») да нажмите в свой черед Enter.

Что ваша сестра будете наблюдать: URL /start потребуется 00 секунд для загрузки, по образу наш брат да ожидали. Но URL /upload этак а потребуется 00 секунд получи и распишись загрузку, хоть на соответствующем обработчике запроса не имеется sleep() !

Почему? Потому что start() включает блокирующую операцию. Like in "it"s blocking everything else from working".

И на этом проблема, благодаря чего что, в качестве кого говорят: «В node всё работает параллельно, из-за исключением вашего кода» .

Это значит, что Node.js может подвергать обработке наряду из этим воз вещей, да возле этом отнюдь не разделяет всё держи отдельные потоки — Node.js однопоточный. Он делает это, запуская индикт событий, а мы, разработчики, можем пускать в ход сие — да мы от тобой должны ускальзывать блокирующих операций, идеже сие возможно, равным образом истощить неблокирующие операции чем них.

Но для сего нам полагается эксплуатнуть обратные вызовы, передавая функции в середине тех функций, которые могут совершить то, что занимает некоторое пора (как хоть бы sleep() получай 00 секунд иначе говоря вопрос ко базе данных alias какое-то дорогостоящее вычисление.)

Таким образом, автор сих строк вроде бы говорим: «Эй, возможноДолгаяФункция(), пожалуйста, сделай видишь это, однако я, однопотоковый Node.js, безвыгодный собираюсь прожидать здесь, ноне твоя милость закончишь, автор этих строк продолжу осуществление строчек заключение внизу тебя, а твоя милость возьми на срок во эту функцию callbackFunction() да вызови её, когда-когда всё сделаешь. Спасибо!»

(Если хотите удостоить об этом сильнее подробно, любезен посмотрите шутцпункт Mixu держи Understanding the node.js event loop .)

И наша сестра немедленно увидим, зачем способ, которым наш брат создали «обработчик запроса обрабатывающий ответ» на нашем приложении невыгодный позволит в точности пускать в ход неблокирующие операции.

Ещё крат давайте попробуем познать проблему бери своей шкуре, модифицировав наше приложение.

Мы который раз используем свой отделочник запроса start . Пожалуйста, измените его следующим образом (файл requestHandlers.js )

  var   exec   =   require   (   "child_process"   ).   exec   ;   

function start () {
console
. log ( "Request handler "start" was called." );
var content = "empty" ;

exec
( "ls -lah" , function ( error , stdout , stderr ) {
content
= stdout ;
});

return content ;
}

function upload () {
console
. log ( "Request handler "upload" was called." );
return "Hello Upload" ;
}

exports
. start = start ;
exports
. upload = upload ;

Как дозволено видеть, автор попросту внедрили новоявленный часть Node.js child_process . Мы сделали так, благодаря чего что сие позволит нам пустить в дело аспидски простую, только полезную неблокирующую операцию: exec() .

Что делает exec() — возлюбленная выполняет shell-команду в середке Node.js. В этом примере я собираемся истощить её, дай тебе выудить ведомость всех файлов на текущей директории ("ls -lah"), позволяя нам отразить настоящий перечень на браузере пользователя, запросившего URL /start .

Что делает таковой код: создает новую переменную content (с начальным значением "empty"), выполняет "ls -lah", заполняет переменную результатом равным образом возвращает её.

Как обычно, запустим наше применение равным образом посетим http://localhost:8888/start .

Которая загрузит нам красивую страничку со строкой "empty". Что после этого отнюдь не так?

Ну, равно как вас сделано догадались, exec() делает свою магию во блокирующий манере. Это хорошая штука, вследствие этого что таким образом автор сих строк можем производить архи дорогостоящие shell-операции (как, например, дублирование больших файлов тож отчего-то подобное), отнюдь не заставляя наше применение сполна останавливаться, непостоянно блокирующая sleep -операция безграмотный выполнится.

Если хотите удостовериться, замените "ls -lah" получай больше дорогостоящую операцию "find /").

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

Давайте если на то пошло пофиксим это. Давайте попытаемся понять, с какой радости текущая конфигурация невыгодный работает.

Проблемой является то, что exec() , воеже трудиться помимо блокирования, использует callback-функцию.

В нашем примере сие анонимная функция, которая передаётся наравне второстепенный параметр на функцию exec() :

  function     (   error   ,   stdout   ,   stderr   )     {   
content
= stdout ;
}

И на этом месте лежит первопричина нашей проблемы: отечественный свой шифр исполняется синхронно, что означает, что враз позднее вызова exec() , Node.js продолжит совершать return content; . К этому моменту content ещё "empty", по вине того, что callback-функция, переданная на exec() , поперед этих пор неграмотный вызвана — благодаря тому что что сделка exec() асинхронная.

Теперь "ls -lah" — адски недорогая да быстрая кампания (если всего на директории неграмотный мильон файлов). Именно потому-то callback вызывается сравнительно действенно — только это, всё же, происходит асинхронно.

Использование паче дорогостоящих команд делает сие паче очевидным: "find /" занимает поблизости 0 минуты нате моей машине, а ежели моя персона заменяю "ls -lah" держи "find /" на обработчике запроса, так пишущий эти строки всё ещё вскоре получаю HTTP-ответ, от случая к случаю открываю URL /start. Ясно, что exec() делает отчего-то во фоновом режиме, все еще Node.js продолжает материализовывать добавление равным образом да мы из тобой можем предположить, что callback-функция, которую наша сестра передали на exec() , хорошенького понемножку вызвана всего только если директива "find /" закончит выполняться.

Но в духе нам дослужиться нашей цели, в таком случае есть, выказать пользователю инвентарь файлов на текущей директории?

Теперь, позже изучения вопроса что касается том, в духе уделывать никак не надо, давайте обсудим, вроде вынудить наши обработчики запроса откликаться возьми требования браузера правильно.

Ответ обработчиков запроса не без; неблокирующими операциями.

Я употребил фразу «правильный способ». Опасная вещь. Довольно нередко далеко не существует единого «правильного способа».

Но одним изо возможных решений для сего является, наравне сие нередко иногда не без; Node.js, сдача функции внутри. Давайте рассмотрим это.

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

Наш новоизобретённый ход заключается на следующем: возмещение доставки контента серверу наш брат будем сервер представлять для контенту. Чтобы фигурировать паче точным, да мы от тобой будем насаждать конструкт response (из серверной callback-функции onRequest() ) путем роутер во обработчики запроса. Обработчики смогут позднее пустить в дело функции сего объекта для ответа в самочки запросы.

Достаточно разъяснений. Вот — пошаговый идея изменения нашего приложения.

Начнём вместе с нашего server.js :

  var   http   =   require   (   "http"   );   
var url = require ( "url" );

function start ( route , handle ) {
function onRequest ( request , response ) {
var pathname = url . parse ( request . url ). pathname ;
console
. log ( "Request for " + pathname + " received." );

route
( handle , pathname , response );
}

http
. createServer ( onRequest ). listen ( 0888 );
console
. log ( "Server has started." );
}

exports
. start = start ;

Вместо ожидания возврата значения ото функции route() , автор сих строк передаём отечественный вещь response во качестве третьего параметра. Кроме того, пишущий сии строки удалили всякие вызовы методов response с обработчика onRequest() , благодаря этому что пишущий сии строки рассчитываем, что route позаботится об этом.

Далее идёт router.js :

  function   route   (   handle   ,   pathname   ,   response   )     {   
console
. log ( "About to route a request for " + pathname );
if ( typeof handle [ pathname ] === "function" ) {
handle
[ pathname ]( response );
} else {
console
. log ( "No request handler found for " + pathname );
response
. writeHead ( 004 , { "Content-Type" : "text/plain" });
response
. write ( "404 Not found" );
response
. end ();
}
}

exports
. route = route ;

Та но схема: где бы ожидания возврата значения с наших обработчиков события, автор сих строк передаём мира response .

Если шлифовщик запроса далеко не может являться использован, наш брат заботимся об ответе со надлежащим заголовком «404» равным образом веточка ответа.

И последнее, да безвыгодный меньше важное, автор модифицируем requestHandlers.js :

  var   exec   =   require   (   "child_process"   ).   exec   ;   

function start ( response ) {
console
. log ( "Request handler "start" was called." );

exec
( "ls -lah" , function ( error , stdout , stderr ) {
response
. writeHead ( 000 , { "Content-Type" : "text/plain" });
response
. write ( stdout );
response
. end ();
});
}

function upload ( response ) {
console
. log ( "Request handler "upload" was called." );
response
. writeHead ( 000 , { "Content-Type" : "text/plain" });
response
. write ( "Hello Upload" );
response
. end ();
}

exports
. start = start ;
exports
. upload = upload ;

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

Обработчик start короче отзываться внутри анонимного обратного вызова exec() , а отделочник upload хорэ всё ещё публиковать «Hello Upload», да днесь через объекта response .

Если вас запустили наше употребление который раз ( node index.js ), всё должен потеть над чем наравне равно ожидалось.

Если хотите убедиться, что дорогостоящая процедура на /start вяще никак не достаточно заблокировать требования сверху /upload , модифицируйте ваш requestHandlers.js наравне показано далее:

  var   exec   =   require   (   "child_process"   ).   exec   ;   

function start ( response ) {
console
. log ( "Request handler "start" was called." );

exec
( "find /" ,
{ timeout : 00000 , maxBuffer : 00000 * 0024 },
function ( error , stdout , stderr ) {
response
. writeHead ( 000 , { "Content-Type" : "text/plain" });
response
. write ( stdout );
response
. end ();
});
}

function upload ( response ) {
console
. log ( "Request handler "upload" was called." );
response
. writeHead ( 000 , { "Content-Type" : "text/plain" });
response
. write ( "Hello Upload" );
response
. end ();
}

exports
. start = start ;
exports
. upload = upload ;

Благодаря этому, HTTP-запросы ко http://localhost:8888/start будут овладевать невыгодный не так 00 секунд, только требования ко http://localhost:8888/upload будут извлекать опровержение немедленно, инда ежели /start всё ещё занят вычислениями.

Сделаем что-нибудь полезное

До этих пор наш брат делали всё нормально равным образом изысканно, да автор сих строк невыгодный создали сносно значимого для клиентов нашего супер-сайта.

Наш сервер, роутер равным образом обработчики запроса находятся возьми своих местах, таким образом, пока что ты да я можем пофигарить надбавлять контент держи отечественный сайт, какой позволяет нашим пользователям выделять файл, наваливать его равным образом просматривать света вольного никак не видит обложка во браузере. Для простоты будем полагать, что сквозь наше вставка будут загружаться равным образом появляться лишь только файлы картинок.

OK, давайте этап вслед шагом, да вместе с разъяснением больших технарь равным образом принципов JavaScript, равно на в таком случае а время, давайте маленько ускоримся. Автору жирно будет нравится хлопать ушами самого себя.

Здесь "шаг следовать шагом" означает приближённо 0 шага: сперва пишущий сии строки погляжу вроде разделывать входящие POST-запросы (но неграмотный загрузку файла), равно получи и распишись втором шаге ты да я используем видный узел Node.js для обработки загрузки файла. Я выбирал данный отношение объединение две причинам.

Во-первых, возделывать базовые POST-запросы насчет прямо-таки во Node.js, так для обучения сие — порядочно стоящее упражнение.
Во-вторых, исправление загрузки файла (к примеру, multipart POST-запросы) сие невыгодный круглым счетом нетрудно на Node.js, следственно итак следовать предел сего учебника, да модель использования для сего внешнего модуля имеет значение ввести на труд для начинающих.

Обработка POST-запросов

Давайте сделаем попроще: предоставим текcтовое поле, которое может присутствовать заполнено пользователем да отправлено получай сервер на POST-запросе. После получения да обработки сего запроса пишущий сии строки отобразим начинка текстового поля.

HTML-код для сложение текстового полина полагается выковывать свой шлифовщик запроса /start , беспричинно давайте зараз но добавим его во обложка requestHandlers.js :

  function   start   (   response   )     {   
console
. log ( "Request handler "start" was called." );

var body = "<html>" +
"<head>" +
"<meta http-equiv="Content-Type" content="text/html; " +
"charset=UTF-8" />" +
"</head>" +
"<body>" +
"<form action="/upload" method="post">" +
"<textarea name="text" rows="20" cols="60"></textarea>" +
"<input type="submit" value="Submit text" />" +
"</form>" +
"</body>" +
"</html>" ;

response
. writeHead ( 000 , { "Content-Type" : "text/html" });
response
. write ( body );
response
. end ();
}

function upload ( response ) {
console
. log ( "Request handler "upload" was called." );
response
. writeHead ( 000 , { "Content-Type" : "text/plain" });
response
. write ( "Hello Upload" );
response
. end ();
}

exports
. start = start ;
exports
. upload = upload ;

Если в настоящее время настоящий адрес невыгодный выиграет Webby Awards, в таком случае моя особа неграмотный знаю, что за сможет. Вы должны испить эту жуть простую форму, когда-никогда запросите http://localhost:8888/start на вашем браузере. Если сие безграмотный приближенно — возможно, вас отнюдь не перезагрузили приложение.

Я сделано слышу вас: размещать начинка представления неуклонно на обрабатыватель запроса некрасиво. Тем малограмотный менее, моя особа решил отнюдь не вовлекать таковой вспомогательный ярус абстракции (то есть, расчленение представления равно логики) во выше- учебник, благодаря этому что, ваш покорный слуга думаю, что сие никак не научит нас чему-нибудь стоящему во контексте JavaScript иначе Node.js.

Давайте вернее эксплуатнуть появившееся окошечко для побольше интересных проблем, ведь есть, обработки POST-запроса на нашем обработчике запроса /upload около отправке этой сложение пользователем.

Теперь, в некоторых случаях ты да я стали экспертными новичками, наша сестра поуже безграмотный удивляемся тому факту, что переработка POST-данных делается на неблокирующей манере, путем применение асинхронных callback-ов.

Это имеет смысл, благодаря этому что POST-запросы могут бытийствовать потенциально бог большими — ничто безграмотный мешает пользователю учредить конферанс размером во сколько-нибудь мегабайтов. Обработка общей сложности массива данных после единственный крат может вогнать для блокирующей операции.

Чтобы проделать вполне течение неблокирующим, Node.js обслуживает POST-данные небольшими порциями, а callback-функции вызываются подле определённых событиях. Эти действие — data (когда приходит новая еда POST-данных) равным образом end (когда всё-таки части данных были получены).

Надо известить Node.js, какие функции вызывать, от случая к случаю сии действие произойдут. Это делается путём добавления слушателей ( listeners ) во предмет request , каковой передаётся на нашу callback-функцию onRequest , нет-нет да и HTTP-запрос получен.

В основном, сие выглядит так:

  request   .   addListener   (   "data"   ,     function   (   chunk   )     {   
// called when a new chunk of data was received
});

request
. addListener ( "end" , function () {
// called when all chunks of data have been received
});

Возникает вопрос, идеже воплотить в жизнь эту логику. В нынешнее миг наш брат можем надергать ход для объекту request лишь на нашем сервере — пишущий сии строки безвыгодный передаём его во роутер равно на обработчики запроса, что делаем сие со объектом response .

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

Таким образом, соображение — во том, в надежде вместить обратные вызовы событий data да end на сервер, набросать совершенно куски POST-данных во data равно поднимать роутер подле получении перипетии end , временно идёт отпуск собранных порций данных во роутер, тот или иной на свою черед передаёт их во обработчики запроса.

Начинаем из server.js :

  var   http   =   require   (   "http"   );   
var url = require ( "url" );

function start ( route , handle ) {
function onRequest ( request , response ) {
var postData = "" ;
var pathname = url . parse ( request . url ). pathname ;
console
. log ( "Request for " + pathname + " received." );

request
. setEncoding ( "utf8" );

request
. addListener ( "data" , function ( postDataChunk ) {
postData
+= postDataChunk ;
console
. log ( "Received POST data chunk "" +
postDataChunk
+ ""." );
});

request
. addListener ( "end" , function () {
route
( handle , pathname , response , postData );
});

}

http
. createServer ( onRequest ). listen ( 0888 );
console
. log ( "Server has started." );
}

exports
. start = start ;

Здесь, на основном, я сделали три вещи: во-первых, определили, что ожидаем полученные сведения во кодировке UTF-8, спустя время добавили радиослушатель для действие «data», кто ход из-за шажком заполняет нашу новую переменную postData некоторый раз, если прибывает новая приём POST-данных, да а там — переходим для вызову нашего роутера во обратном вызове перипетии end , чтоб убедиться, что затребование происходит, эпизодически безвыездно POST-данные собраны. Мы вот и все передаём POST-данные во роутере, поелику что они нам понадобятся во обработчиках запроса.

Добавление логирования на рента для каждой порции полученных данных — возможно, плохая понятие для конечного стих (мегабайты POST-данных, помните?), а сие имеет смысл, чтоб посмотреть, что происходит.

Я предлагаю маленечко поиграться от этим. Поместите на текстовое фон попервоначалу каплю текста, а позже больше, да ваша сестра увидите, что для больших текстов перевернутый приглашение data воистину вызывается малость раз.

Давайте добавим ещё чище крутизны во наше приложение. На странице /upload наша сестра будем демонстрировать общепринятый контент. Чтобы содеять сие возможным, нам необходимо уполномочивать postData на обработчики запроса. В router.js :

  function   route   (   handle   ,   pathname   ,   response   ,   postData   )     {   
console
. log ( "About to route a request for " + pathname );
if ( typeof handle [ pathname ] === "function" ) {
handle
[ pathname ]( response , postData );
} else {
console
. log ( "No request handler found for " + pathname );
response
. writeHead ( 004 , { "Content-Type" : "text/plain" });
response
. write ( "404 Not found" );
response
. end ();
}
}

exports
. route = route ;

И во requestHandlers.js наша сестра включаем сии способности во нашем ответе обработчика запроса upload :

  function   start   (   response   ,   postData   )     {   
console
. log ( "Request handler "start" was called." );

var body = "<html>" +
"<head>" +
"<meta http-equiv="Content-Type" content="text/html; " +
"charset=UTF-8" />" +
"</head>" +
"<body>" +
"<form action="/upload" method="post">" +
"<textarea name="text" rows="20" cols="60"></textarea>" +
"<input type="submit" value="Submit text" />" +
"</form>" +
"</body>" +
"</html>" ;

response
. writeHead ( 000 , { "Content-Type" : "text/html" });
response
. write ( body );
response
. end ();
}

function upload ( response , postData ) {
console
. log ( "Request handler "upload" was called." );
response
. writeHead ( 000 , { "Content-Type" : "text/plain" });
response
. write ( "You"ve sent: " + postData );
response
. end ();
}

exports
. start = start ;
exports
. upload = upload ;

Вот равным образом всё, сейчас я можем унаследовать POST-данные равным образом утилизировать их во наших обработчиках запроса.

И последнее в области этой теме: то, что автор передаём во роутер равным образом обработчики запроса, является полным веточка нашего POST-запроса. Мы, вероятно, захотим истощить индивидуальные поля, составляющие POST-данные, во нашем случае авторитет полина text .

Мы ранее читали ради устройство querystring , кой поможет нам не без; этим:

  var   querystring   =   require   (   "querystring"   );   

function start ( response , postData ) {
console
. log ( "Request handler "start" was called." );

var body = "<html>" +
"<head>" +
"<meta http-equiv="Content-Type" content="text/html; " +
"charset=UTF-8" />" +
"</head>" +
"<body>" +
"<form action="/upload" method="post">" +
"<textarea name="text" rows="20" cols="60"></textarea>" +
"<input type="submit" value="Submit text" />" +
"</form>" +
"</body>" +
"</html>" ;

response
. writeHead ( 000 , { "Content-Type" : "text/html" });
response
. write ( body );
response
. end ();
}

function upload ( response , postData ) {
console
. log ( "Request handler "upload" was called." );
response
. writeHead ( 000 , { "Content-Type" : "text/plain" });
response
. write ( "You"ve sent the text: " +
querystring
. parse ( postData ). text );
response
. end ();
}

exports
. start = start ;
exports
. upload = upload ;

Это всё, что позволительно высказать насчет обработку POST-данных на рамках учебника для начинающих.

Обработка загрузки файлов

Давайте примемся после концевой параграф нашего списка задач. Мы планировали одарить допустимость пользователям находить работу файлы картинок равным образом отражать загруженные картинки во браузере.

В 00-х сие могло бы присутствовать квалифицировано по образу операция трафарет для IPO, немедленно а сего достаточно, дабы посоветовать нас два вещам: что установливать внешнии библиотки Node.js равно на правах их воспользоваться во нашем коде.

Внешний модуль, какой я собираемся использовать, node-formidable через Felix Geisendörfer. Этот устройство поможет нам отвлечься ото мерзких деталей парсинга входящих файловых данных. В конце концов, возделывание входящих файлов сие малограмотный что иное, по образу «просто» пропуск POST-данных, но, на действительности, враг рода человеческого кроется во деталях, потому-то на нашем случае имеет идея воспользоваться готовое решение.

Чтобы эксплуатнуть адрес Феликса, подобранный устройство Node.js потребно существовать инсталлирован. На борту Node.js очищать приватизированный администратор пакетов, называемый NPM . Он позволяет нам инсталировать внешние модули Node.js во бог удобной форме. С учетом установленного Node.js, всё сводится ко

  npm install formidable  

на нашей командной строке. Если ваша сестра на конце увидели следующее:

  npm info build   Success   :   formidable@1   .   0.9   
npm ok

...это следственно — всё хорошо.

Модуль formidable нынче доступен на нашем коде — всё, что нужно, сие прямо-таки спрашивать его как бы сам изо тех модулей, которые пишущий сии строки использовали ранее:

  var   formidable   =   require   (   "formidable"   );  

По сути, formidable делает форму, отправленную путем HTTP POST, доступной для парсинга на Node.js. Всё, что нам требуется — сие построить новейший пример объекта IncomingForm , который-нибудь является абстракцией отправленной телосложение равным образом может фигурировать использован для парсинга объекта request нашего HTTP-сервера, для полей равным образом файлов, отправленных путем эту форму.

Пример заключение со страницы проекта node-formidable показывает, как бы неодинаковые части сочетаются побратим вместе с другом:

  var   formidable   =   require   (   "formidable"   ),   
http
= require ( "http" ),
sys
= require ( "sys" );

http
. createServer ( function ( req , res ) {
if ( req . url == <