Home

Пошаговая Инструкция По Написанию Простого Клиент-Сервер

Цель данного документа — помочь вам начать разработку приложений на Node.js и научить всему, что необходимо знать о «продвинутом» JavaScript. Это больше, чем обычный «Hello world»-туториал. Статус Вы читаете финальную версию этой книги, в обновлениях исправляются только ошибки или отражаются изменения в новых версиях Node.js. Последнее обновление 12 Февраля 2012. Код примеров этой книги тестировался на 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 репозитории. Простой HTTP-сервер Анализ нашего HTTP-сервера Передача функций в качестве параметра Как анонимная функция делает наш HTTP-сервер рабочим Событийно-ориентированные обратные вызовы Как наш сервер обрабатывает запросы Выбор места для нашего серверного модуля Что необходимо для «роутера»? Исполнение королевских постановлений в царстве глаголов Роутинг реальных обработчиков запроса Создание ответа обработчиков запроса Как делать не надо Блокирование и неблокирование Ответ обработчиков запроса с неблокирующими операциями. 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-помощник из середины 90-х годов, более серьезный 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 состоит из 2 вещей: среды исполнения и полезных библиотек. Для того чтобы их использовать, вам необходимо установить Node.js. Вместо повторения всего процесса установки здесь, я просто приглашу вас посетить официальную инструкцию по инсталляции. Пожалуйста, вернитесь обратно после успешной установки. «Hello world» Хорошо, давайте пойдём сразу с места в карьер и напишем наше первое Node.js-приложение: «Hello world». Откройте ваш любимый редактор и создайте файл под названием helloworld.js. Мы хотим вывести строку «Hello world» в консоль, для этого пишем следующий код: Давайте подумаем о том, как бы мы реализовали это на 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 в корневой директории вашего проекта и поместите туда следующий код: Может сейчас самое время спросить: Почему мы это делаем так? Событийно-ориентированные обратные вызовы Ответ на вопрос a) не так легко дать (по крайней мере для меня), и b) кроется в самой природе работы Node.js — это событийно-ориентированность, то, благодаря чему он работает так быстро. Возможно, вы захотите занять немного своего времени и почитать отличный пост Felix Geisendörfer Понимание node.js, чтобы прояснить этот момент. Все сводится к тому факту, что Node.js работает событийно-ориентированно. Ах да, я тоже до конца не понимаю, что это значит. Но я постараюсь объяснить, почему это так важно для тех, кто хочет писать веб-приложения в Node.js. Когда вызываем метод http.createServer, мы, конечно, не только хотим иметь сервер, слушающий какой-то порт. Мы также хотим что-нибудь сделать, когда приходит HTTP-запрос на этот сервер. Проблема состоит в асинхронности: запрос происходит в любой момент времени, в то время как у нас только один процесс, в котором запущен наш сервер. Когда пишем PHP-приложения, мы не беспокоимся обо всем этом: всякий раз, когда приходит HTTP-запрос, веб-сервер (обычно Apache) ответвляет новый процесс специально для этого запроса и запускает соответствующий PHP-скрипт с нуля, который выполняется от начала до конца. Когда приходит новый запрос на порт 8888, относительно потоков управления, мы находимся в середине нашей Node.js-программы. Как это понять, чтоб не помешаться? Это как раз то, где событийно-ориентированный дизайн Node.js/JavaScript на самом деле помогает. Нам надо узнать некоторые новые понятия, чтобы досконально понять всё это. Мы создаем сервер и передаём функцию в созданный им метод. Всякий раз, когда наш сервер получает запрос, переданная нами функция будет вызываться. Мы не знаем, когда это произойдет, но у нас теперь есть место, где можем обрабатывать входящий запрос. Это наша переданная функция и не имеет значения, определили ли мы её сначала или передали анонимно. Этот принцип называется обратный вызов или callback. Мы передаём в некоторый метод функцию и этот метод исполняет её, когда происходит связанное с методом событие. По крайней мере для меня, это заняло некоторое время, чтобы понять. Просто почитайте блог Felix Geisendörfer снова, если вы всё ещё не уверены. Давайте немного поиграем с этим новым понятием. Можем ли мы доказать, что наш код продолжает работать после создания сервера, даже если нет HTTP-запроса и callback-функция, переданная нами, не вызывается? Давайте попробуем: Конечно этот код ничего не делает, но сейчас этого достаточно. Давайте сначала посмотрим, как скрепить этот роутер с нашим сервером до того как поместим больше логики в роутер. Нашему HTTP-серверу необходимо знать о роутере и использовать его. Мы могли бы жёстко прописать эти зависимости в нашем сервере, но, так как мы знаем только сложные способы из нашего опыта в других языках программирования, мы сделаем слабосвязанную зависимость сервера и роутера через внедрение этих зависимостей (можете почитать отличный пост Мартина Фаулера по внедрениям зависимости на английском языке или статью в Википедии на русском языке для дополнительной информации). Для начала, расширим нашу серверную функцию start(), чтобы дать нам возможность передавать функцию route() как параметр: Просто объясню, что этот код делает: когда функция start() вызвана, Node.js ожидает 10 секунд и только тогда возвращает «Hello Start». Когда вызывается upload(), она выполняется немедленно, как и раньше. (Конечно, вы уже поняли, вместо засыпания на 10 секунд, в start() могут быть реальные блокирующие операции, такие как сложные длительные вычисления.) Давайте посмотрим, что поменялось. Как обычно, нам надо перезапустить сервер. На этот раз я попрошу вас следовать немного более сложному «протоколу», чтобы увидеть, что произошло: во-первых, откройте браузер или таб. В первом окне браузера, введите, пожалуйста, http://localhost:8888/start в адресную строку, но не переходите пока по этому адресу! В адресную строку второго окна браузера введите http://localhost:8888/upload и снова не переходите по адресу. Теперь сделайте, как описано далее: нажмите клавишу Enter в первом окне («/start»), а затем быстро переключитесь на второе окно («/upload») и нажмите тоже Enter. Что вы будете наблюдать: URL /start потребуется 10 секунд для загрузки, как мы и ожидали. Но URL /upload так же потребуется 10 секунд на загрузку, хотя в соответствующем обработчике запроса нет sleep()! Почему? Потому что start() содержит блокирующую операцию. Like in "it's blocking everything else from working". И в этом проблема, потому что, как говорят: «В node всё работает параллельно, за исключением вашего кода». Это значит, что Node.js может обрабатывать одновременно множество вещей, но при этом не разделяет всё на отдельные потоки — Node.js однопоточный. Он делает это, запуская цикл событий, а мы, разработчики, можем использовать это — мы должны избегать блокирующих операций, где это возможно, и использовать неблокирующие операции вместо них. Но для этого нам надо использовать обратные вызовы, передавая функции внутри тех функций, которые могут сделать то, что занимает некоторое время (как например sleep() на 10 секунд или запрос к базе данных или какое-то дорогостоящее вычисление.) Таким образом, мы как бы говорим: «Эй, возможноДолгаяФункция(), пожалуйста, сделай вот это, но я, однопотоковый Node.js, не собираюсь ждать здесь, пока ты закончишь, я продолжу выполнение строчек кода ниже тебя, а ты возьми пока вот эту функцию callbackFunction() и вызови её, когда всё сделаешь. Спасибо!» (Если хотите почитать об этом более подробно, пожалуйста посмотрите пост Mixu на Understanding the node.js event loop.) И мы сейчас увидим, почему способ, которым мы создали «обработчик запроса обрабатывающий ответ» в нашем приложении не позволит правильно использовать неблокирующие операции. Ещё раз давайте попробуем испытать проблему на своей шкуре, модифицировав наше приложение. Мы снова используем наш обработчик запроса start. Пожалуйста, измените его следующим образом (файл requestHandlers.js) И здесь лежит корень нашей проблемы: наш собственный код исполняется синхронно, что означает, что сразу после вызова exec(), Node.js продолжит выполнять return content;. К этому моменту content ещё "empty", из-за того, что callback-функция, переданная в exec(), до сих пор не вызвана — потому что операция exec() асинхронная. Теперь "ls -lah" — очень недорогая и быстрая операция (если только в директории не миллион файлов). Именно поэтому callback вызывается относительно оперативно — но это, всё же, происходит асинхронно. Использование более дорогостоящих команд делает это более очевидным: "find /" занимает около 1 минуты на моей машине, но если я заменяю "ls -lah" на "find /" в обработчике запроса, то я всё ещё немедленно получаю HTTP-ответ, когда открываю URL /start. Ясно, что exec() делает что-то в фоновом режиме, пока Node.js продолжает исполнять приложение и мы можем предположить, что callback-функция, которую мы передали в exec(), будет вызвана только когда команда "find /" закончит выполняться. Но как нам достичь нашей цели, то есть, показать пользователю список файлов в текущей директории? Теперь, после изучения вопроса о том, как делать не надо, давайте обсудим, как заставить наши обработчики запроса реагировать на запросы браузера правильно. Ответ обработчиков запроса с неблокирующими операциями. Я употребил фразу «правильный способ». Опасная вещь. Довольно часто не существует единого «правильного способа». Но одним из возможных решений для этого является, как это часто бывает с Node.js, передача функции внутри. Давайте рассмотрим это. Сейчас наше приложение способно транспортировать контент (который обработчики запроса хотели бы показать пользователю) от обработчиков запроса к HTTP-серверу, возвращая его через слои приложения (обработчик запроса -> роутер -> сервер). Наш новый подход заключается в следующем: вместо доставки контента серверу мы будем сервер доставлять к контенту. Чтобы быть более точным, мы будем внедрять объект response (из серверной callback-функции onRequest()) через роутер в обработчики запроса. Обработчики смогут тогда использовать функции этого объекта для ответа на сами запросы. Достаточно разъяснений. Вот — пошаговый рецепт изменения нашего приложения. Начнём с нашего server.js: Благодаря этому, HTTP-запросы к http://localhost:8888/start будут занимать не менее 10 секунд, но запросы к http://localhost:8888/upload будут получать ответ немедленно, даже если /start всё ещё занят вычислениями. Сделаем что-нибудь полезное До сих пор мы делали всё прекрасно и изысканно, но мы не создали ничего значимого для клиентов нашего супер-сайта. Наш сервер, роутер и обработчики запроса находятся на своих местах, таким образом, теперь мы можем начать добавлять контент на наш сайт, который позволяет нашим пользователям выбирать файл, загружать его и просматривать загруженный файл в браузере. Для простоты будем полагать, что через наше приложение будут загружаться и показываться только файлы картинок. OK, давайте шаг за шагом, но с разъяснением больших техник и принципов JavaScript, и в то же время, давайте немного ускоримся. Автору слишком нравится слушать самого себя. Здесь "шаг за шагом" означает примерно 2 шага: сначала мы посмотрим как обрабатывать входящие POST-запросы (но не загрузку файла), и на втором шаге мы используем внешний модуль Node.js для обработки загрузки файла. Я выбирал этот подход по двум причинам. Во-первых, обрабатывать базовые POST-запросы относительно просто в Node.js, но для обучения это — достаточно стоящее упражнение. Во-вторых, обработка загрузки файла (к примеру, multipart POST-запросы) это не так просто в Node.js, поэтому выходит за рамки этого учебника, но пример использования для этого внешнего модуля имеет смысл включить в учебник для начинающих. Обработка POST-запросов Давайте сделаем попроще: предоставим текcтовое поле, которое может быть заполнено пользователем и отправлено на сервер в POST-запросе. После получения и обработки этого запроса мы отобразим содержимое текстового поля. HTML-код для формы текстового поля должен формировать наш обработчик запроса /start, так давайте сразу же добавим его в файл requestHandlers.js: Если теперь этот код не выиграет 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-запрос получен. В основном, это выглядит так: Это всё, что можно сказать про обработку POST-данных в рамках учебника для начинающих. Обработка загрузки файлов Давайте примемся за последний пункт нашего списка задач. Мы планировали дать возможность пользователям загружать файлы картинок и отображать загруженные картинки в браузере. В 90-х это могло бы быть квалифицировано как бизнес модель для IPO, сейчас же этого достаточно, чтобы научить нас двум вещам: как установливать внешнии библиотки Node.js и как их использовать в нашем коде. Внешний модуль, который мы собираемся использовать, node-formidable от Felix Geisendörfer. Этот модуль поможет нам абстрагироваться от мерзких деталей парсинга входящих файловых данных. В конце концов, обработка входящих файлов это не что иное, как «просто» обработка POST-данных, но, в действительности, дьявол кроется в деталях, поэтому в нашем случае имеет смысл использовать готовое решение. Чтобы использовать код Феликса, соответствующий модуль Node.js должен быть инсталлирован. На борту Node.js есть собственный менеджер пакетов, называемый NPM. Он позволяет нам инсталировать внешние модули Node.js в очень удобной форме. С учетом установленного Node.js, всё сводится к Первый шаг — простой. Нам надо добавить тип кодировки multipart/form-data в нашу HTML-форму, удалить текстовое поле, добавить поле загрузки файла и поменять текст кнопки отправки формы на «Upload file». Давайте просто сделаем это в файле requestHandlers.js: var querystring = require("querystring"), fs = require("fs"); 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" enctype="multipart/form-data" '+ 'method="post">'+ '<input type="file" name="upload">'+ '<input type="submit" value="Upload file" />'+ '</form>'+ '</body>'+ '</html>'; response.writeHead(200, {"Content-Type": "text/html"}); response.write(body); response.end(); } function upload(response, postData) { console.log("Request handler 'upload' was called."); response.writeHead(200, {"Content-Type": "text/plain"}); response.write("You've sent the text: "+ querystring.parse(postData).text); response.end(); } function show(response, postData) { console.log("Request handler 'show' was called."); fs.readFile("/tmp/test.png", "binary", function(error, file) { if(error) { response.writeHead(500, {"Content-Type": "text/plain"}); response.write(error + "\ "); response.end(); } else { response.writeHead(200, {"Content-Type": "image/png"}); response.write(file, "binary"); response.end(); } }); } exports.start = start; exports.upload = upload; exports.show = show; Замечательно. Следующий шаг — немного более сложный, конечно. Первая проблема следующая: мы хотим обрабатывать загрузку файлов в нашем обработчике запроса upload, и тут надо будет передать объект request при вызове form.parse модуля node-formidable. Но всё, что у нас есть — это объект response и массив postData. Грустно. Похоже, что придётся передавать каждый раз объект request из сервера в роутер и обработчик запроса. Может быть, имеется более элегантное решение, но этот способ может делать работу уже сейчас. Давайте полностью удалим всё, что касается postData в нашем сервере и обработчиках запроса — он нам не нужен для обработки загрузки файла и, мало того, — даже создает проблему: мы уже «поглотили» события data объекта request в сервере, а следовательно, form.parse, которому так же надо поглащать эти события, не сможет получить больше данных (потому что Node.js не буферизирует данные). Начнём с server.js — удалим обработку postData и строку с request.setEncoding (node-formidable сам всё сделает) и передадим request в роутер: 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, request); } http.createServer(onRequest).listen(8888); console.log("Server has started."); } exports.start = start; Следующий — router.js — мы больше не передаём postData, а вместо этого передаём request: function route(handle, pathname, response, request) { console.log("About to route a request for " + pathname); if (typeof handle[pathname] === 'function') { handle[pathname](response, request); } else { console.log("No request handler found for " + pathname); response.writeHead(404, {"Content-Type": "text/html"}); response.write("404 Not found"); response.end(); } } exports.route = route; Теперь объект request может быть использован в функции обработчика запроса upload. node-formidable будет заниматься сохранением загруженного файла в локальный файл /tmp, но, конечно, мы сами должны сделать, чтобы этот файл переименовывался в /tmp/test.png. Да, мы придерживаемся действительно простых вещей и принимаем, что могут загружаться только PNG-картинки. Имеется небольшая дополнительная сложность в логике переименования: Windows-реализации Node.js не нравится, когда пытаются переименовать существующий файл, вот почему нам необходимо удалять файл в случае ошибки. Давайте добавим в requestHandlers.js код управления загрузкой файла и переименованием: var querystring = require("querystring"), fs = require("fs"), formidable = require("formidable"); 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" enctype="multipart/form-data" '+ 'method="post">'+ '<input type="file" name="upload" multiple="multiple">'+ '<input type="submit" value="Upload file" />'+ '</form>'+ '</body>'+ '</html>'; response.writeHead(200, {"Content-Type": "text/html"}); response.write(body); response.end(); } function upload(response, request) { console.log("Request handler 'upload' was called."); var form = new formidable.IncomingForm(); console.log("about to parse"); form.parse(request, function(error, fields, files) { console.log("parsing done"); /* Возможна ошибка в Windows: попытка переименования уже существующего файла */ fs.rename(files.upload.path, "/tmp/test.png", function(err) { if (err) { fs.unlink("/tmp/test.png"); fs.rename(files.upload.path, "/tmp/test.png"); } }); response.writeHead(200, {"Content-Type": "text/html"}); response.write("received image:<br/>"); response.write("<img src='/show' />"); response.end(); }); } function show(response) { console.log("Request handler 'show' was called."); fs.readFile("/tmp/test.png", "binary", function(error, file) { if(error) { response.writeHead(500, {"Content-Type": "text/plain"}); response.write(error + "\ "); response.end(); } else { response.writeHead(200, {"Content-Type": "image/png"}); response.write(file, "binary"); response.end(); } }); } exports.start = start; exports.upload = upload; exports.show = show; Вот и всё. Перезапускаем сервер, и последний пункт нашей задачи реализован. Выбираем локальную PNG-картинку с диска, загружаем на сервер и видим её на нашей веб-страничке. Выводы и перспективы Поздравляю, наша миссия выполнена! Мы написали простое, уже полностью готовое web-приложение на Node.js. Мы поговорили о server-side JavaScript, функциональном программировании, блокирующих и неблокирующих операциях, callback-ах, событиях, обычаях, внутренних и внешних модулях и о многом другом. Конечно, имеется много вещей, которые мы не обсудили: как общаться с базой данных, как писать unit-тесты, как создавать внешние модули, с возможностью инсталяции через NPM или даже что-нибудь простое, например, как обрабатывать GET-запросы. Но это судьба каждого учебника, предназначенного для начинающих — нельзя расказать о всех аспектах во всех деталях. Хорошей новостью является то, что Node.js-сообщество — очень активное (думаю, даже гиперактивное от кофеина, но в хорошем смысле), имеется множество ресурсов, кроме этого, и множество мест, где можно получить ответы на ваши вопросы. Node.js community wiki и the NodeCloud directory являются, возможно, лучшими отправными точками.

В пошаговых руководствах содержатся инструкции по выполнению Ресурсы для Share Point Server 2013 · Ресурсы для Windows Server 2012 простого компонента, включая взаимодействие между клиентом и Описание возможностей C#, которые служат для написания выражений запросов LINQ.

В примерах даются пошаговые инструкции выполнения типовых сценариев, которые Пошаговые руководства по приложению Rich Client Описание способа быстрой разработки простого сервера COM с использованием.

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

пошаговая инструкция по написанию простого клиент-сервер

Создание новой стандартной программы C++ и проверка ее функций с помощью Visual Studio. Описание способа создания интерфейса Rich Client с использованием библиотеки базовых классов Microsoft (MFC) (проверенной библиотеки классов, которая поддерживает большое число функциональных возможностей, используемых во всех приложениях Windows) и веб-служб XML, созданных с помощью Visual Basic или Visual C#.

Цель написания данной книги - создание простой пошаговой инструкции на. Код в директории / server исполняется только на сервере. Давайте создадим директорию / client /stylesheets/ и положим туда вот этот файл style. css.

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

Пошаговые руководства по Visual C++ (Visual Studio)

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


Snack's 1967