Чтобы отправить что-то назад, используются методы объекта response
. Первый, writeHead
, пишет заголовки ответа (см. главу 17). Вы даёте ему код статуса (в этом случае 200 для “OK”) и объект, содержащий значения заголовков. Здесь мы сообщаем клиенту, что он должен ждать документ HTML.
Затем идёт тело ответа (сам документ), отправляемое через response.write
. Этот метод можно вызывать несколько раз, если хотите отправлять ответ по кускам, к примеру, передавая потоковые данные по мере их поступления. Наконец, response.end
сигнализирует конец ответа.
Вызов server.listen
заставляет сервер слушать запросы на порту 8000. Поэтому вам надо в браузере заходить на localhost:8000, а не просто на localhost (где портом по умолчанию будет 80).
Для остановки такого скрипта Node, который не завершается автоматически, потому что ожидает следующих событий (в данном случае, соединений), надо нажать Ctrl-C.
Настоящий веб-сервер делает гораздо больше того, что описано в примере. Он смотрит на метод запроса (свойство method
), чтобы понять, какое действие пытается выполнить клиент, и на URL запроса, чтобы понять, на каком ресурсе это действие должно выполняться. Далее вы увидите более продвинутую версию сервера.
Чтобы сделать HTTP-клиент, мы можем использовать функцию “http”
модуля request
.
var http = require("http");
var request = http.request({
hostname: "eloquentjavascript.net",
path: "/20_node.html",
method: "GET",
headers: {Accept: "text/html"}
}, function(response) {
console.log("Сервис ответил с кодом ",
response.statusCode);
});
request.end();
Первый аргумент request
настраивает запрос, объясняя Node, с каким сервером будем общаться, какой путь будет у запроса, какой метод использовать, и т. д. Второй – функция, которую надо будет вызвать по окончанию запроса. Ей передаётся объект response
, в котором содержится вся информация по ответу – к примеру, код статуса.
Как и объект response
сервера, объект, возвращаемый request
, позволяет передавать данные методом write
и заканчивать запрос методом end
. В примере не используется write
, потому что запросы GET
не должны содержать данных в теле.
Для запросов на безопасные URL (HTTPS), Node предлагает модуль https
, в котором есть своя функция запроса, схожая с http.request
.
Потоки
Мы видели два примера потоков в примерах HTTP – объект response
, в который сервер может вести запись, и объект request
, который возвращается из http.request
.
Потоки с возможностью записи – популярная концепция в интерфейсах Node. У всех потоков есть метод write
, которому можно передать строку или объект Buffer
. Метод end
закрывает поток, а при наличии аргумента, выведет перед закрытием кусочек данных. Обоим методам можно задать функцию обратного вызова через дополнительный аргумент, которую они вызовут по окончанию записи или закрытию потока.
Возможно создать поток, показывающий на файл, при помощи функции fs.createWriteStream
. Затем можно использовать метод write
для записи в файл по кусочкам, а не целиком, как в fs.writeFile
.
Потоки с возможностью чтения будут чуть сложнее. Как переменная request
, переданная функции для обратного вызова в сервере HTTP, так и переменная response
, переданная в HTTP-клиенте, являются потоками с возможностью чтения. (Сервер читает запрос и потом пишет ответы, а клиент пишет запрос и читает ответа). Чтение из потока осуществляется через обработчики событий, а не через методы.
У объектов, создающих события в Node, есть метод on
, схожий с методом браузера addEventListener
. Вы даёте ему имя события и функцию, и он регистрирует эту функцию, чтоб её вызвали сразу, когда произойдёт событие.
У потоков с возможностью чтения есть события "data"
и "end"
. Первое происходит при поступлении данных, второе – по окончанию. Эта модель подходит к потоковым данным, которые можно сразу обработать, даже если получен не весь документ. Файл можно прочесть в виде потока через fs.createReadStream
.
Следующий код создаёт сервер, читающий тела запросов и отправляющий их в ответ потоком в виде текста из заглавных букв.
var http = require("http");
http.createServer(function(request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
request.on("data", function(chunk) {
response.write(chunk.toString().toUpperCase());
});
request.on("end", function() {
response.end();
});
}).listen(8000);
Переменная chunk
, передаваемая обработчику данных, будет бинарным Buffer
, который можно преобразовать в строку, вызвав его метод toString
, который декодирует его с помощью кодировки по умолчанию (UTF-8).