Перейти к содержимому

13. Request и Response объекты

Иллюстрация к уроку

req и res — два главных объекта в каждом роуте Express. Полный разбор всех свойств и методов.

app.use((req, res, next) => {
// === ТЕЛО ЗАПРОСА ===
req.body // { name: 'Яша' } — парсится middleware express.json()
// === URL ПАРАМЕТРЫ ===
req.params // { id: '42', slug: 'hello-world' }
req.params.id // '42' (всегда строка!)
// === QUERY СТРОКА ===
req.query // { page: '1', limit: '10', tags: ['js', 'node'] }
req.query.page // '1' (тоже строка!)
// === ЗАГОЛОВКИ ===
req.headers // объект всех заголовков
req.headers['content-type'] // 'application/json'
req.headers['authorization'] // 'Bearer token...'
req.get('Authorization') // то же, удобнее
req.header('Authorization') // синоним get()
// === МЕТОД И URL ===
req.method // 'GET', 'POST', 'PUT', 'DELETE'
req.url // '/api/users?page=1'
req.originalUrl // '/api/users?page=1' (после монтирования роутера)
req.path // '/api/users' (без query)
req.baseUrl // '/api' (префикс роутера)
req.hostname // 'localhost' или 'example.com'
req.protocol // 'http' или 'https'
req.secure // true если https
req.ip // '127.0.0.1'
req.ips // ['127.0.0.1'] (при X-Forwarded-For)
// === COOKIES ===
// require cookie-parser middleware
req.cookies // { token: 'abc', lang: 'ru' }
req.signedCookies // подписанные куки
// === ПОЛЕЗНЫЕ ПРОВЕРКИ ===
req.is('application/json') // true/false — проверка Content-Type
req.accepts('json') // 'json' или false
req.fresh // true если ответ не изменился (кэш)
req.stale // !req.fresh
req.xhr // true если Ajax запрос (X-Requested-With: XMLHttpRequest)
next();
});
app.get('/response-demo', (req, res) => {
// === ОТПРАВКА ДАННЫХ ===
res.json({ data: 'value' }); // JSON ответ
res.send('text'); // текст/HTML
res.send(Buffer.from('binary')); // бинарные данные
res.sendStatus(204); // только статус, без тела
res.end(); // завершить без данных
// === СТАТУС-КОД ===
res.status(404).json({ error: 'Not found' });
res.status(201).json({ id: 1 });
// === ЗАГОЛОВКИ ===
res.set('Content-Type', 'application/pdf');
res.set({ 'X-A': '1', 'X-B': '2' });
res.setHeader('X-Custom', 'value'); // синоним set()
res.get('Content-Type'); // получить заголовок
res.removeHeader('X-Powered-By'); // удалить заголовок
res.append('Link', '<https://example.com>; rel="preconnect"');
// === ПЕРЕНАПРАВЛЕНИЯ ===
res.redirect('/new-url');
res.redirect(301, '/permanent');
res.redirect('back'); // на предыдущую страницу
// === ФАЙЛЫ ===
res.sendFile('/absolute/path/file.pdf');
res.sendFile('report.pdf', { root: __dirname });
res.download('/path/to/file.xlsx', 'Отчёт 2024.xlsx');
// === COOKIES ===
res.cookie('token', 'value', {
httpOnly: true, // недоступен из JS
secure: true, // только HTTPS
sameSite: 'strict', // защита CSRF
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 дней
domain: '.example.com', // для поддоменов
path: '/',
});
res.clearCookie('token');
// === РЕНДЕРИНГ ШАБЛОНОВ ===
res.render('index', { title: 'Главная', user: req.user });
// === СОСТОЯНИЕ ===
res.headersSent // true если заголовки уже отправлены
res.finished // true если ответ завершён
});
// Фабрика ответов — единый формат
class ApiResponse {
static success(res, data, status = 200) {
return res.status(status).json({
success: true,
data,
timestamp: new Date().toISOString(),
});
}
static created(res, data) {
return this.success(res, data, 201);
}
static noContent(res) {
return res.sendStatus(204);
}
static error(res, message, status = 500, details = null) {
const body = {
success: false,
error: { message },
timestamp: new Date().toISOString(),
};
if (details) body.error.details = details;
return res.status(status).json(body);
}
static notFound(res, resource = 'Resource') {
return this.error(res, `${resource} not found`, 404);
}
static unauthorized(res) {
return this.error(res, 'Unauthorized', 401);
}
static forbidden(res) {
return this.error(res, 'Forbidden', 403);
}
static validationError(res, details) {
return this.error(res, 'Validation error', 400, details);
}
}
// Использование
app.get('/users/:id', async (req, res, next) => {
try {
const user = await db.findUser(req.params.id);
if (!user) return ApiResponse.notFound(res, 'User');
ApiResponse.success(res, user);
} catch (err) {
next(err);
}
});
const { createReadStream } = require('fs');
// Стримим файл в ответ
app.get('/download/:filename', (req, res) => {
const filename = req.params.filename;
const filepath = `./uploads/${filename}`;
res.setHeader('Content-Type', 'application/octet-stream');
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
const stream = createReadStream(filepath);
stream.on('error', (err) => {
if (!res.headersSent) {
res.status(404).json({ error: 'File not found' });
}
});
stream.pipe(res);
});
// Server-Sent Events (SSE)
app.get('/events', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
let counter = 0;
const interval = setInterval(() => {
res.write(`data: ${JSON.stringify({ count: counter++ })}\n\n`);
}, 1000);
req.on('close', () => {
clearInterval(interval);
});
});
  1. Создай middleware, который добавляет req.requestId (UUID) к каждому запросу
  2. Напиши ApiResponse класс с методами success, error, notFound, paginated
  3. Реализуй скачивание файла через res.download() с проверкой прав
  4. Создай эндпоинт SSE /events, который каждую секунду отправляет серверное время
  5. Добавь глобальный middleware для логирования: метод, URL, статус, время ответа