20Sep

nodejs-avanzado

Всем привет! На этом занятии мы создадим эхо-сервер, то есть, такой сервер, который при запросе на url /echo c  параметром message выдает значение этого параметра:

// http://127.0.0.1:3000/echo?message=Hello -> Hello

На все другие запросы отвечает: Страница не найдена.

Начнем вот с такого шаблона:

// http://127.0.0.1:3000/echo?message=Hello -> Hello

var http = require('http');

var server = new http.Server(function(req, res) {

});

server.listen(3000, '127.0.0.1');

Создается сервер, и ему дается функция –  обработчик на request. Первый шаг обработки запроса – это понять, что за запрос вообще к нам пришел. Для этого мы воспользуемся свойствами method и url объекта запроса, добавим в тело нашей функции:

console.log( req.method, req.url);

Запускаю и обращаюсь в браузер к этой странице. Обратите внимание, я перешел по этому url:

http://127.0.0.1:3000/echo?message=Hello

и оно при помощи console.log вывело, что метод называется GET, и то, что было запрошено: GET /echo?message=Hello

Опять переключаюсь на браузер и видно, что он ничего не вывел. Браузер чего-то ожидает. Ответа! Потому что сервер, если не дать явной команды, написать что-то в ответ,  делать ничего не будет. В этом явное отличие  Node.js от многих существующих серверов, он делает только то с соединением, что ему скажешь. Если ему не сказать: отошли ответ,  он ничего не отсылает. Соответственно получается, что соединение есть, функция request отработала, но она ничего не сделала для того, чтобы ответить на него, поэтому запрос завис. Он будет висеть столько, сколько браузер или сервер считают нужным. Кому первому надоест, тот первый и оборвет соединение.

Итак, теперь давайте ответим сообщением, которое нам было прислано в параметре message. Для этого мне нужно понять, что конкретно находится в message. Напомню задачу, если url вот такой:  /echo?message=Hello

и есть параметр message, то нужно отослать его значение, а иначе будет: Страница не найдена. Соответственно, для того, чтобы разобрать url, можно, конечно, воспользоваться регулярными выражениями. Но в Node.js есть для этого специальный модуль url, добавим его:

// http://127.0.0.1:3000/echo?message=Hello -> Hello

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


var server = new http.Server(function(req, res) {
    console.log( req.method, req.url);

    var urlParsed = url.parse(req.url);
    console.log(urlParsed);
});

server.listen(3000, '127.0.0.1');

Использую его для того, чтобы разобрать переданную строку запроса. Этот метод называется url.parse:

var urlParsed = url.parse(req.url, true);

Запустим сервер, обновляем страницу. В терминале виден наш запрос. Больше всего нас интересуют параметры query и pathname, опишем их в той же функции.

 if (urlParsed.pathname == '/echo' && urlParsed.query) {

Есть еще параметр message. Как определить, есть ли параметр message, ведь у меня queryэто строка? Самый простой способ – это добавить еще один аргумент в команде url.parse, которая, если указан true, разберет эту строку в объект

var urlParsed = url.parse(req.url, true);

Соответственно, message будет его значением:

if (urlParsed.pathname == '/echo' && urlParsed.query.message) {

Если все это есть, то давайте ответим:

res.end( urlParsed.query.message );

Ну а если нет? Тогда нужно ответить: Страница не найдена. Для этого ставится соответствующий код ответа и тело страницы:

res.statusCode = 404; // Not Found  
res.end("Page not found"); 

В итоге получиться вот так:

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

var server = new http.Server(function(req, res) {

    var urlParsed = url.parse(req.url, true);

    if (urlParsed.pathname == '/echo' && urlParsed.query.message) {
        res.end( urlParsed.query.message );
    } else {
        res.statusCode = 404; // Not Found
        res.end("Page not found");
    }
});

server.listen(3000, '127.0.0.1');

Перезапускаем сервер и заходим в браузер. Все работает.

Теперь давайте зайдем по другому url: Page not found. Если сейчас открою вкладку Network, то здесь мы увидим, что Page not found имеет код ответа 404, а, соответственно, нормальный urlстандартный код ответа 200.

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

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

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

Закоментируем часть кода для лучшего понимания, получится вот так;

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

var server = new http.Server(function(req, res) {

    console.log(req.headers );


//     var urlParsed = url.parse(req.url, true);
//
//     if (urlParsed.pathname == '/echo' && urlParsed.query.message) {
//         res.end( urlParsed.query.message );
//     } else {
//         res.statusCode = 404; // Not Found
//         res.end("Page not found");
//     }
});

server.listen(3000, '127.0.0.1');

запускаем, переходим на страницу и видим заголовки в терминале, которые отправил браузер. Каждый заголовок имеет имя и значение. Обратите внимание, что все заголовки в Node.js имеют имя в нижнем регистре. Это особенность именно Node.js. В данном случае host передается в заголовки, connectionkeepalive, означает, что браузер хотел по этому сетевому соединению гнать новые и новые запросы, то, о чем мы говорили раньше: Connection один, requestмного.

Далее, детали того, что это за браузер и что он хотел бы получить. В ответ на такой запрос сервер отвечает, конечно же, телом страницы и тоже заголовками, в которых, в частности, находится статус. Как правило, статус – это 200. Такой статус по умолчанию называется OK, и означает, что страница сгенерирована нормально. Раскоментируем наш код. И добавим в if :

res.statusCode = 200; // Ok

Другой распространенный статус – 404, который обычно называется not foundстраница не найдена. Есть и другие статусы, например, 403 – доступ запрещен, 500 – серверная ошибка. Также есть и другие серверные заголовки. Сейчас перезапустим сервер:

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

var server = new http.Server(function(req, res) {

    console.log(req.headers );


    var urlParsed = url.parse(req.url, true);

    if (urlParsed.pathname == '/echo' && urlParsed.query.message) {
        res.statusCode = 200; // Ok
        res.end( urlParsed.query.message );
    } else {
        res.statusCode = 404; // Not Found
        res.end("Page not found");
    }
});

server.listen(3000, '127.0.0.1');

Во вкладке Network, а именно в Headers, видно все эти заголовки. Есть заголовки, которые отправил браузер, и есть те, которые получил браузер от сервера. Нажмите view source , чтобы получить дополнительную картину. Появятся дополнительные заголовки, это более полный вид. Статус ОК, как мы говорили, и прочие заголовки здесь более-менее стандартные.

Давайте поставим какие-нибудь другие наиболее важные серверные заголовки, которые обычно приходится ставить вручную. Это заголовки, которые касаются типов содержимого или кеширования (caching). Например, если я делаю веб-сервис, то я, вполне возможно, не хочу, чтобы результаты ответа сервиса кешировались. Для этого я поставлю заголовок cachecontrol, nocache. Можно сделать его понадежнее, добавив еще опций, но для примера обойдемся коротким вариантом:

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

var server = new http.Server(function(req, res) {

    var urlParsed = url.parse(req.url, true);

    if (urlParsed.pathname == '/echo' && urlParsed.query.message) {
        res.setHeader('Cache-control', 'no-cache');
        res.end( urlParsed.query.message );
    } else {
        res.statusCode = 404; // Not Found
        res.end("Page not found");
    }
});

server.listen(3000, '127.0.0.1');

Теперь запустим сервер и в браузере посмотрим, что у нас с заголовками. Изменения легко заметить: появился дополнительный заголовок cachecontrol, nocache, то есть, тот, который мы высылали.

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

А теперь немного отойдем от общего http и вернемся к Node.js. Для того, чтобы работать с заголовками, у ответа res. есть два принципиально разных метода. Первый метод проиллюстрирован здесь:

if (urlParsed.pathname == '/echo' && urlParsed.query.message) {  
    res.setHeader('Cache-control', 'no-cache');  
    res.end( urlParsed.query.message );  
  } else {  
    res.statusCode = 404; // Not Found  
    res.end("Page not found");  
  }  
});  

В статус-код ставится и setHeader или removeHeader – добавляют или удаляют заголовок. При этом сами заголовки будут отправлены на сервер не сейчас, не когда написал setHeader, а вместе с ближайшей записью каких-то данных, например, вызов res.end – отправляет ответ и заголовки тоже. Второй способ управления заголовками называется явный. Выглядит он так:

res.writeHeader(200 "OK", {'Cache-control':'no-cache'});

Он отличается тем, что при этом, заголовки пишутся в ответ тут же, не ожидая начала ближайшей записи. Иногда такое тоже бывает нужно. Но обычно вполне хватает таких вот: статус-код и setHeader.

Код этого урока находится в нашем репозитории.

echodeveloplogo

Материалы урока взяты из следующего скринкаста.

We are looking forward to meeting you on our website soshace.com 

9. Уроки Node.js. События, EventEmitter и Утечки Памяти

Следующий объект, который нас интересует, это EventEmitter или, как его иногда называют, ЕЕ. EventEmitter представляет собой основной объект, реализующий работу с событиями в Node.js. Большое количество других встроенных объектов, которые генерируют события, ему наследуют. Для того чтобы воспользоваться EventEmitter достаточно подключить модуль “events”встроенный и взять с него соответствующее свойство (создадим ee.js) :

var EventEmitter = require(‘events’).EventEmitter;