1. Зачем нужны разные типы ответов?
Когда вы пишете сервер на Node.js, он может общаться с клиентом (браузером или другим приложением) разными "языками". Самые популярные варианты:
- HTML — для отображения страницы в браузере.
- JSON — для передачи структурированных данных (например, ваше приложение на React или Angular запрашивает данные с сервера).
- Plain text — просто текст, иногда для отладки.
Каждый из этих форматов требует, чтобы сервер правильно сообщил клиенту: "Эй, сейчас я пришлю тебе HTML/JSON/текст!" Для этого в HTTP-протоколе используются заголовки.
Что такое HTTP-заголовки?
HTTP-заголовки — это такие "метки" в начале ответа, которые говорят клиенту, что он сейчас получит. Например, если вы отправляете HTML-страницу, браузер должен знать, что это HTML, чтобы правильно её отобразить. Если вы отправляете JSON, браузер или JS-клиент должен понять: "Ага, это данные, а не страница!"
Главный заголовок для типа контента — это Content-Type.
- Для HTML: Content-Type: text/html; charset=utf-8
- Для JSON: Content-Type: application/json; charset=utf-8
- Для обычного текста: Content-Type: text/plain; charset=utf-8
В Node.js вы управляете заголовками через методы объекта response (res):
- res.setHeader('Имя', 'Значение') — установить заголовок.
- res.writeHead(код, { заголовки }) — сразу установить статус и заголовки.
2. Отправка HTML-ответа
Давайте начнём с самого простого: отдаём HTML-страницу.
Пример: Hello, HTML!
const http = require('http');
const server = http.createServer((req, res) => {
// Устанавливаем заголовок: говорим, что это HTML
res.setHeader('Content-Type', 'text/html; charset=utf-8');
// Можно ещё установить статус (например, 200 — OK)
res.statusCode = 200;
// Отправляем HTML-код
res.end(`
<html>
<head>
<title>Привет, Node!</title>
</head>
<body>
<h1>Добро пожаловать на мой сервер!</h1>
<p>Это простой HTML-ответ.</p>
</body>
</html>
`);
});
server.listen(3000, () => {
console.log('Сервер запущен на http://localhost:3000');
});
Что здесь происходит?
- Мы явно указываем браузеру, что отправляем HTML.
- Вызов res.end() завершает ответ и отправляет его клиенту.
- Можно использовать любые HTML-теги. Да, даже <blink>, но только если вы хотите удивить палеонтологов.
Почему важно явно указывать Content-Type?
Если не указать Content-Type, браузер будет гадать, что вы ему прислали. Иногда угадает правильно, иногда — нет. Например, если отправить JSON без нужного заголовка, браузер может просто показать его как текст, а JS-клиент не сможет его распарсить.
3. Отправка JSON-ответа
JSON — это стандартный "язык" обмена данными между сервером и клиентом. Даже если вы не любите JSON, JSON любит вас (и ваши данные).
Пример: Привет, JSON!
const http = require('http');
const server = http.createServer((req, res) => {
// Данные, которые хотим отправить
const user = {
name: 'Алиса',
age: 30,
isAdmin: true
};
// Устанавливаем заголовок: это JSON
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.statusCode = 200;
// Отправляем строку JSON
res.end(JSON.stringify(user));
});
server.listen(3000, () => {
console.log('Сервер слушает http://localhost:3000');
});
Ключевой момент:
- ВСЕГДА сериализуйте объект через JSON.stringify().
- Если попытаться отправить объект напрямую (res.end(user)), Node.js обидится и скажет, что не может отправить объект.
Проверим в браузере или через curl
- Откройте http://localhost:3000 — увидите {"name":"Алиса","age":30,"isAdmin":true}
- Или в терминале:
curl http://localhost:3000
4. Установка других заголовков
Иногда нужно отправить дополнительные заголовки. Например, чтобы указать клиенту, что страница не должна кэшироваться, или чтобы разрешить кросс-доменные запросы (CORS).
Пример: Несколько заголовков
res.setHeader('Content-Type', 'application/json; charset=utf-8');
res.setHeader('Cache-Control', 'no-store');
res.setHeader('X-Powered-By', 'Node.js');
Можно также использовать res.writeHead() — этот метод сразу устанавливает статус и все заголовки:
res.writeHead(200, {
'Content-Type': 'text/html; charset=utf-8',
'X-Custom-Header': 'МойКастом'
});
Внимание:
Если вы вызвали res.writeHead(), не используйте после этого res.setHeader() — заголовки уже отправлены.
5. Практический пример: HTML или JSON в зависимости от запроса
Давайте сделаем сервер, который отдаёт HTML при запросе /, а JSON — при запросе /api/user.
const http = require('http');
const server = http.createServer((req, res) => {
if (req.url === '/') {
// Главная страница — HTML
res.writeHead(200, {
'Content-Type': 'text/html; charset=utf-8'
});
res.end(`
<h1>Главная страница</h1>
<p>Перейдите на <a href="/api/user">/api/user</a> чтобы получить JSON.</p>
`);
} else if (req.url === '/api/user') {
// API — JSON
const user = { name: 'Боб', age: 25 };
res.writeHead(200, {
'Content-Type': 'application/json; charset=utf-8'
});
res.end(JSON.stringify(user));
} else {
// 404 Not Found
res.writeHead(404, {
'Content-Type': 'text/plain; charset=utf-8'
});
res.end('Страница не найдена');
}
});
server.listen(3000, () => {
console.log('Сервер запущен на http://localhost:3000');
});
Что здесь происходит:
- Для разных URL — разные Content-Type и разные данные.
- Если страница не найдена — отдаём 404 и простой текст.
6. Как правильно отправлять статус-коды
По умолчанию статус ответа — 200 (OK), но иногда нужно явно указать другой код:
- 200 — всё хорошо
- 201 — создано (например, при успешном POST-запросе)
- 404 — не найдено
- 500 — серверная ошибка
Пример:
res.statusCode = 404;
res.setHeader('Content-Type', 'text/plain; charset=utf-8');
res.end('Ошибка: страница не найдена');
Или через writeHead:
res.writeHead(201, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ result: 'Создано!' }));
7. Типичные ошибки при отправке HTML и JSON-ответов
Ошибка №1: Не установлен Content-Type
Если не указать заголовок Content-Type, браузер (или другой клиент) может неправильно интерпретировать ответ. Например, JSON отобразится как текст, а HTML — как странный набор символов.
Ошибка №2: Попытка отправить объект напрямую
Вызов res.end(user) — приведёт к ошибке. Нужно всегда сериализовать объект: res.end(JSON.stringify(user)).
Ошибка №3: Множественная отправка ответа
Если случайно вызвать res.end() несколько раз, Node.js выбросит ошибку: "Cannot set headers after they are sent to the client". Ответ можно отправить только один раз!
Ошибка №4: Использование res.setHeader() после res.writeHead() или res.end()
Заголовки должны быть установлены до отправки тела ответа. После вызова res.writeHead() или res.end() заголовки уже отправлены, и изменить их нельзя.
Ошибка №5: Отсутствие кодировки (charset)
Если не указать charset=utf-8, русские буквы могут отображаться "кракозябрами". Всегда добавляйте кодировку: text/html; charset=utf-8.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ