1. Введение
Когда вы создаёте HTTP-сервер в Node.js с помощью модуля http, вы обязательно пишете нечто вроде:
const http = require('http');
const server = http.createServer((req, res) => {
// Тут магия
});
server.listen(3000);
Вот эти самые req и res — это два объекта, которые Node.js автоматически создаёт для каждого входящего HTTP-запроса. Они играют роль «почтальонов»: один приносит вам письмо (запрос клиента), другой — отправляет ваш ответ обратно.
- req (IncomingMessage) — объект, который содержит всю информацию о входящем запросе: URL, метод (GET, POST и т.д.), заголовки, тело запроса и прочее.
- res (ServerResponse) — объект, с помощью которого вы формируете и отправляете ответ клиенту: указываете статус-код, заголовки, тело ответа.
Всё взаимодействие между браузером (или другим клиентом) и вашим сервером происходит через эти два объекта.
2. Объект request (req): свойства и методы
Какой тип у req?
Объект req — это экземпляр класса http.IncomingMessage. Это поток (stream), который позволяет читать данные, поступающие от клиента.
Основные свойства req
Давайте разберём самые полезные свойства, которые пригодятся в 99% случаев:
| Свойство | Что хранит | Пример значения |
|---|---|---|
|
URL, по которому пришёл запрос (всё после домена и порта) | |
|
HTTP-метод запроса (GET, POST, PUT, DELETE и т.д.) | , |
|
Объект всех HTTP-заголовков запроса | |
|
Версия HTTP-протокола | , |
Пример: Выводим информацию о запросе
const http = require('http');
const server = http.createServer((req, res) => {
console.log('URL:', req.url);
console.log('Method:', req.method);
console.log('Headers:', req.headers);
res.end('OK');
});
server.listen(3000, () => {
console.log('Server started on port 3000');
});
Запусти сервер, зайди на http://localhost:3000/about?user=vasya — и посмотри в консоль!
req.url: путь и параметры
req.url — это строка, которая содержит путь и query-параметры. Например, если клиент запросил http://localhost:3000/about?user=vasya, то req.url будет /about?user=vasya.
Чтобы разбирать URL на части (путь, параметры), удобно использовать встроенный класс URL:
const url = new URL(req.url, `http://${req.headers.host}`);
console.log(url.pathname); // '/about'
console.log(url.searchParams.get('user')); // 'vasya'
req.method: что хочет клиент?
HTTP-метод показывает, что именно хочет сделать клиент: получить данные (GET), отправить (POST), изменить (PUT), удалить (DELETE), и так далее. Обычно для одного и того же пути (/users) сервер может по-разному реагировать на разные методы.
if (req.method === 'GET') {
// Вернуть данные
} else if (req.method === 'POST') {
// Принять новые данные
}
req.headers: заголовки запроса
req.headers — это обычный объект JavaScript, где ключи — названия заголовков (в нижнем регистре!), а значения — строки.
console.log(req.headers['user-agent']); // Браузер клиента
console.log(req.headers['content-type']); // Тип передаваемых данных
req.body: где тело запроса?
В отличие от фреймворков типа Express, в «голом» Node.js у объекта req нет свойства body. Если клиент отправляет данные (например, через POST), их нужно читать из потока вручную:
let body = '';
req.on('data', chunk => {
body += chunk;
});
req.on('end', () => {
console.log('Body:', body);
res.end('Received!');
});
Важно: Для GET-запросов тело обычно отсутствует; для POST/PUT/PATCH — может быть.
req.socket: информация о соединении
Иногда нужно узнать IP-адрес клиента или другие детали соединения:
console.log(req.socket.remoteAddress); // Например, '::1' (localhost)
3. Объект response (res): свойства и методы
Какой тип у res?
Объект res — это экземпляр класса http.ServerResponse. Это поток для записи (writeable stream), с помощью которого вы отправляете данные клиенту.
Основные методы res
| Метод | Для чего нужен | Пример использования |
|---|---|---|
|
Установить HTTP-статус ответа (по умолчанию 200) | |
|
Добавить/изменить HTTP-заголовок | |
|
Отправить часть тела ответа (можно несколько раз) | |
|
Завершить ответ и отправить всё клиенту | |
Пример: Отправляем HTML-страницу
const http = require('http');
const server = http.createServer((req, res) => {
res.statusCode = 200; // Можно не писать, если 200
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.write('<h1>Hello, world!</h1>');
res.write('<p>Привет с сервера!</p>');
res.end(); // Завершаем ответ
});
server.listen(3000);
res.statusCode: статус ответа
HTTP-статус — это число, которое сообщает клиенту, как сервер обработал запрос. Самые частые:
- 200 — OK (всё хорошо)
- 404 — Not Found (не найдено)
- 500 — Internal Server Error (что-то сломалось)
Важно: Если не указать явно, будет 200.
res.setHeader(name, value): заголовки ответа
С помощью этого метода вы управляете метаданными ответа: типом содержимого, политикой кэширования, куками и так далее.
res.setHeader('Content-Type', 'application/json');
res.write(data): отправка данных
Можно отправлять часть данных сразу, а потом ещё кусочек. Обычно, если ответ небольшой, пишут только res.end(data).
res.write('Часть 1');
res.write(' и часть 2');
res.end(' — конец!');
res.end([data]): завершение ответа
Обязательный шаг! Без вызова res.end() клиент будет ждать ответа вечно, и браузер «подвиснет».
res.end('Всё, пока!');
res.writeHead(statusCode, headers): старый способ
Можно сразу установить статус и заголовки одним методом (устаревший, но встречается):
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Not found');
4. Примеры: делаем сервер чуть умнее
Пример 1: Простой роутинг по пути
const http = require('http');
const server = http.createServer((req, res) => {
if (req.url === '/') {
res.setHeader('Content-Type', 'text/html');
res.end('<h1>Главная страница</h1>');
} else if (req.url === '/about') {
res.setHeader('Content-Type', 'text/html');
res.end('<h1>О нас</h1>');
} else {
res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain');
res.end('Страница не найдена');
}
});
server.listen(3000);
Пример 2: Отправляем JSON
const http = require('http');
const server = http.createServer((req, res) => {
if (req.url === '/api/user') {
res.setHeader('Content-Type', 'application/json');
const user = { name: 'Вася', age: 30 };
res.end(JSON.stringify(user));
} else {
res.statusCode = 404;
res.end('Not found');
}
});
server.listen(3000);
Пример 3: Читаем тело POST-запроса (например, для формы)
const http = require('http');
const server = http.createServer((req, res) => {
if (req.method === 'POST' && req.url === '/echo') {
let body = '';
req.on('data', chunk => {
body += chunk;
});
req.on('end', () => {
res.setHeader('Content-Type', 'text/plain');
res.end('Вы отправили: ' + body);
});
} else {
res.end('Только POST /echo!');
}
});
server.listen(3000);
5. Типичные ошибки при работе с req и res
Ошибка №1: Не вызван res.end()
Если забыть вызвать res.end(), браузер будет ждать ответа бесконечно. Даже если вы отправили данные через res.write(), обязательно завершайте ответ.
Ошибка №2: Неправильный Content-Type
Если отправляете JSON, но забыли поставить Content-Type: application/json, браузер или клиент может не понять, что это за данные.
Ошибка №3: Множественный вызов res.end()
Вызовите res.end() только один раз! Повторный вызов приведёт к ошибке: Cannot set headers after they are sent to the client.
Ошибка №4: Чтение тела запроса без обработки событий
Если вы читаете тело запроса через req.on('data'), но не слушаете событие end, то не узнаете, когда данные пришли полностью, и ответ отправится раньше времени.
Ошибка №5: Использование req.body в чистом Node.js
В отличие от Express, в Node.js у объекта req нет свойства body по умолчанию. Его нужно собирать вручную из чанков потока.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ