Следующий код, будучи запущенным одновременно с сервером, отправит запрос на сервер и выведет полученный ответ:
var http = require("http");
var request = http.request({
hostname: "localhost",
port: 8000,
method: "POST"
}, function(response) {
response.on("data", function(chunk) {
process.stdout.write(chunk.toString());
});
});
request.end("Hello server");
Пример пишет в process.stdout
(стандартный вывод процесса, являющийся потоком с возможностью записи), а не в console.log
. Мы не можем использовать console.log
, так как он добавляет лишний перевод строки после каждого куска кода – это здесь не нужно.
Простой файловый сервер
Давайте скомбинируем наши новые знания о серверах HTTP и работе с файловой системой, и наведём мостик между ними: HTTP-сервер, предоставляющий удалённый доступ к файлам. У такого сервера много вариантов использования. Он позволяет веб-приложениям хранить и делиться данными, или может дать группе людей доступ к набору файлов.
Когда мы относимся к файлам, как к ресурсам HTTP, методы GET
, PUT
и DELETE
можно использовать для чтения, записи и удаления файлов. Мы будем интерпретировать путь в запросе как путь к файлу.
Нам не надо открывать доступ ко всей файловой системе, поэтому мы будем интерпретировать эти пути как заданные относительно корневого каталога, и это будет каталог запуска скрипта. Если я запущу сервер из /home/marijn/public/
(или C:\Users\marijn\public\
на Windows), то запрос на /file.txt
должен указать на /home/marijn/public/file.txt
(или C:\Users\marijn\public\file.txt
).
Программу мы будем строить постепенно, используя объект methods
для хранения функций, обрабатывающих разные методы HTTP.
var http = require("http"), fs = require("fs");
var methods = Object.create(null);
http.createServer(function(request, response) {
function respond(code, body, type) {
if (!type) type = "text/plain";
response.writeHead(code, {"Content-Type": type});
if (body && body.pipe)
body.pipe(response);
else
response.end(body);
}
if (request.method in methods)
methods[request.method](urlToPath(request.url),
respond, request);
else
respond(405, "Method " + request.method
" not allowed.");
}).listen(8000);
Этот код запустит сервер, возвращающий ошибки 405 – этот код используется для обозначения того, что запрошенный метод сервером не поддерживается.
Функция respond
передаётся функциям, обрабатывающим разные методы, и работает как обратный вызов для окончания запроса. Она принимает код статуса HTTP, тело, и, возможно, тип содержимого. Если переданное тело – поток с возможностью чтения, у него будет метод pipe
, который используется для передачи читаемого потока в записываемый. Если нет – предполагается, что это либо null
(тело пустое), или строка, и тогда она передаётся напрямую в метод ответа end
.
Чтобы получить путь из URL в запросе, функция urlToPath
, используя встроенный модуль Node “url”
, разбирает URL. Она принимает имя пути, нечто вроде /file.txt
, декодирует, чтобы убрать экранирующие коды %20
, и вставляет в начале точку, чтобы получить путь относительно текущего каталога.
function urlToPath(url) {
var path = require("url").parse(url).pathname;
return "." + decodeURIComponent(path);
}
Вам кажется, что функция urlToPath
небезопасна? Вы правы. Вернёмся к этому вопросу в упражнениях.
Мы устроим метод GET
так, чтобы он возвращал список файлов при чтении директории, и содержимое файла при чтении файла.
Вопрос на засыпку – какой тип заголовка Content-Type
мы должны возвращать, читая файл. Поскольку в файле может быть всё, что угодно, сервер не может просто вернуть один и тот же тип для всех. Но NPM с этим может помочь. Модуль mime
(индикаторы типа содержимого файла вроде text/plain
также называются MIME types) знает правильный тип для огромного количества расширений файлов.
Запустив следующую команду npm
в директории, где живёт скрипт сервера, вы сможете использовать require("mime")
для запросов к библиотеке типов.
$ npm install mime
npm http GET https://registry.npmjs.org/mime
npm http 304 https://registry.npmjs.org/mime
mime@1.2.11 node_modules/mime
Когда запрошенного файла не существует, правильным кодом ошибки для этого случая будет 404. Мы будем использовать fs.stat
для возврата информации по файлу, чтобы выяснить, есть ли такой файл, и не директория ли это.