Тело запроса и заголовки
Тело запроса и заголовки — то, что клиент присылает помимо метода и пути.
«Заголовки — это метаданные письма, тело — само содержимое. Express помогает читать и то, и другое.»
Чтобы принять данные формы регистрации или JSON от мобильного приложения, сервер должен прочитать тело запроса. А чтобы понять, в каком формате эти данные и кто их прислал, — заголовки. В этом уроке научимся читать тело через встроенные парсеры Express и доставать нужные заголовки.
Чтение тела запроса
Тело приходит потоком байтов. Чтобы превратить его в удобный объект, нужен middleware-парсер. Express встроил два главных:
const express = require('express');
const app = express();
app.use(express.json()); // тело в формате JSON
app.use(express.urlencoded({ extended: true })); // данные HTML-форм
app.post('/register', (req, res) => {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).json({ error: 'нужны email и password' });
}
res.status(201).json({ email });
});Без express.json() поле req.body в Express 5 будет undefined — частый источник ошибок "cannot read property of undefined".
Чтение заголовков
Заголовки доступны через req.headers (объект) или удобный метод req.get():
app.get('/whoami', (req, res) => {
const ua = req.get('User-Agent');
const type = req.get('Content-Type');
res.json({ userAgent: ua, contentType: type });
});Валидация тела своими руками
Любые входные данные нужно проверять. Соберём простой валидатор в браузере: он проходит по правилам и собирает ошибки. Express-приложения делают так же, часто с библиотекой вроде Zod или Joi:
function validate(body, rules) {
const errors = [];
for (const field in rules) {
const value = body[field];
if (rules[field].required && (value === undefined || value === '')) {
errors.push(field + ' обязательно');
}
if (value && rules[field].minLength && value.length < rules[field].minLength) {
errors.push(field + ' слишком короткое');
}
}
return errors;
}
const body = { email: '[email protected]', password: '123' };
const errors = validate(body, {
email: { required: true },
password: { required: true, minLength: 6 }
});
console.log(errors); // ['password слишком короткое']Эта функция — прообраз middleware-валидатора, который мы прикрутим к маршрутам в следующем разделе.
Как работает под капотом
Когда приходит запрос с телом, Node отдаёт данные как поток. Middleware express.json() подписывается на события потока, накапливает чанки, а в конце парсит собранную строку через JSON.parse и кладёт результат в req.body. Поэтому парсер должен стоять до маршрутов — иначе к моменту обработчика тело ещё не разобрано.
Частые ошибки
- Парсер после маршрутов.
app.use(express.json())должен идти раньше маршрутов, которым нужно тело. - Имена заголовков с регистром. В
req.headersвсе ключи приведены к нижнему регистру;req.get()регистронезависим — используй его. - Слепое доверие телу. Никогда не передавай
req.bodyв базу без валидации.
Best practices
- Подключай
express.json()иexpress.urlencoded()один раз в начале приложения. - Ограничивай размер тела (опция
limit), чтобы не получить отказ в обслуживании огромным запросом. - Валидируй входные данные до бизнес-логики и возвращай 400 с понятным сообщением.
Итоги
Тело читается встроенными парсерами и попадает в req.body, заголовки — в req.headers и req.get(). Главное правило: парсеры — до маршрутов, данные — всегда под подозрением. Эти парсеры — наш первый пример middleware, и в следующем разделе мы разберём этот механизм во всю глубину.
Content-Type решает всё
Какой парсер сработает, определяет заголовок Content-Type запроса. express.json() разбирает тело только если клиент прислал application/json; express.urlencoded() — только для application/x-www-form-urlencoded (обычные HTML-формы). Если заголовок не совпал, парсер просто пропускает запрос, и тело остаётся неразобранным. Отсюда классическая ловушка: фронтенд шлёт JSON, но забывает выставить Content-Type, и сервер видит пустое тело. При отладке "почему req.body пуст" первым делом смотри именно на этот заголовок запроса — в девяти случаях из десяти причина там.