Ось урок, створений спеціально для тебе у стилі CS50. Приготуйся, сьогодні ми зазирнемо під капот веб-серверів!
🎓 Урок: Middleware та Життєвий цикл запиту
Вітаю! Я радий бачити вас на цьому занятті.
Сьогодні ми поговоримо про "невидимих героїв" веб-розробки. Про той клей, який тримає наші додатки купи і не дає їм розсипатися на тисячі шматків копіпасту. Ми говоримо про Middleware.
1. 🔥 Вступ: Чому ми тут?
Уявіть собі, що ви будуєте найбезпечніший нічний клуб у місті. У вас є VIP-кімната (це ваш сервер з даними). І є купа людей, які хочуть туди потрапити (це HTTP-запити).
Як ви організуєте вхід?
Варіант А: Ви ставите охоронця безпосередньо в VIP-кімнаті. Людина заходить, сідає на диван, і лише тоді охоронець питає: "А у вас є квиток?". Якщо ні — виганяє. Варіант Б: Ви ставите кордон перед входом. Спочатку фейс-контроль, потім каса, потім гардероб, і лише потім — VIP-кімната.
Риторичне запитання: Який варіант ефективніший? Звісно, другий. Навіщо пускати когось до бару, якщо йому навіть немає 18 років?
У чому проблема в коді?
Уявіть, що у вас є 50 різних маршрутів (сторінка профілю, налаштування, кошик, історія замовлень). Усі вони потребують, щоб користувач був залогінений. Якщо ви будете писати перевірку if (!user.isLoggedIn) у кожній з цих 50 функцій — ви збожеволієте. А якщо логіка зміниться? Вам доведеться правити код у 50 місцях. Це жах. Це "спагеті-код".
Middleware (проміжне ПЗ) — це і є той самий фейс-контроль, гардероб і каса, через які проходить клієнт (запит), перш ніж дістатися до головної вечірки (вашого контролера).
2. 🧠 Теоретична база (Як це працює під капотом)
Давайте спростимо. Що таке веб-сервер? Це функція, яка приймає Запит (Request) і повертає Відповідь (Response).
Але між "прийняв" і "відповів" може статися багато всього.
Життєвий цикл запиту
Уявіть собі конвеєр на заводі. 1. Start: Сировина (HTTP Request) потрапляє на стрічку. 2. Middle: Вона проходить через різні станції. На одній її миють, на іншій фарбують, на третій перевіряють якість. 3. End: Готовий продукт (HTTP Response) відправляється клієнту.
Ці "станції" посередині — це і є Middleware.
Головна формула Middleware
Це просто функція, яка має доступ до трьох речей:
1. req (об'єкт запиту) — що прийшло?
2. res (об'єкт відповіді) — чим відповімо?
3. next (чарівна кнопка) — передати естафету далі.
❗️ Що треба запам’ятати залізно: Middleware має лише два шляхи: 1. Або завершити запит (надіслати відповідь і сказати "до побачення"). 2. Або викликати
next(), щоб передати запит наступному middleware в черзі.Якщо ви не зробите ні того, ні іншого — ваш сервер "зависне", і клієнт вічно бачитиме кружечок завантаження.
Інтуїтивно: Це як шари цибулі. Запит пробивається крізь шари всередину, а відповідь — назовні.
3. 🧪 Приклади (Codetime!)
Ми будемо використовувати синтаксис, схожий на Express.js (Node.js), бо він найбільш наочний, але логіка однакова і в Python (Django/FastAPI), і в PHP (Laravel).
Приклад 1: "Балакучий" Middleware (Логування)
Найпростіший приклад. Ми просто хочемо знати, що хтось прийшов.
// Це наш Middleware
function logger(req, res, next) {
console.log(`🔔 Хтось постукав на сторінку: ${req.url}`);
// Дуже важливо! Передаємо естафету далі.
next();
}
// Використання
app.use(logger);
app.get('/', (req, res) => {
res.send("Привіт, світе!");
});
Питання до вас: Що буде, якщо я видалю рядок next() у функції logger?
(Пауза для роздумів...)
Відповідь: Сервер напише в консоль "Хтось постукав...", але браузер користувача буде вічно вантажитись. До res.send справа просто не дійде. Ланцюжок розірвано.
Приклад 2: Суровий охоронець (Авторизація)
Тепер реальний кейс. Захищаємо адмінку.
function checkPassword(req, res, next) {
const secret = req.query.secret; // Шукаємо пароль у запиті
if (secret === 'cs50') {
// Пароль правильний? Проходь!
next();
} else {
// Пароль неправильний? Стоп!
// Ми НЕ викликаємо next(), ми перериваємо цикл.
res.status(403).send("⛔️ Доступ заборонено!");
}
}
// Застосовуємо тільки до секретної сторінки
app.get('/admin', checkPassword, (req, res) => {
res.send("🔥 Ласкаво просимо до панелі керування!");
});
Тут ми бачимо силу middleware: ми відфільтрували "поганий" трафік ще до того, як він дістався головної логіки.
Приклад 3: Турботливий помічник (Модифікація даних)
Middleware може не тільки пропускати або забороняти. Він може змінювати запит. Уявіть, що ви приходите в готель, і портьє одразу чіпляє вам бирку на валізу.
function addTimestamp(req, res, next) {
// Ми дописуємо нову властивість прямо в об'єкт запиту
req.requestTime = new Date().toISOString();
next();
}
app.use(addTimestamp);
app.get('/', (req, res) => {
// Тепер у нас є доступ до req.requestTime!
res.send(`Відповідь сформовано о: ${req.requestTime}`);
});
Навіщо це? Так працюють парсери JSON. Приходить "сирий" текст, middleware перетворює його на зручний об'єкт, і основна функція вже працює з готовими даними.
4. 🛠 Практична частина
Час забруднити руки кодом. Уявіть, що ви Back-end розробник.
Завдання 1: "Технічні роботи"
Напишіть middleware, який перехоплює всі запити і замість сайту повертає текст: "Вибачте, ми на технічному обслуговуванні. Повернемось за 5 хвилин.".
Підказка: Чи потрібно тут викликати next()?
Завдання 2: "Детектив"
Створіть middleware, який перевіряє, який браузер використовує клієнт (зазвичай це заголовок User-Agent). Якщо це Internet Explorer (жах!) — повертайте помилку "Оновіть браузер". Якщо Chrome/Firefox/Safari — пропускайте далі.
Завдання 3: "Виправ баг" Розробник-початківець написав такий код:
function myMiddleware(req, res, next) {
if (req.url === '/home') {
res.send("Це дім");
}
next();
}
Питання: Що трапиться, якщо користувач зайде на /home? Чому в консолі сервера може з'явитися помилка Error: Cannot set headers after they are sent to the client?
Міні-кейс: "Лічильник" Як би ви реалізували підрахунок кількості відвідувань сайту, використовуючи глобальну змінну і middleware?
5. 💡 Мислення як у розробника
Як відрізнити новачка від профі в темі Middleware?
-
Порядок має значення (Order Matters). Новачки часто ставлять middleware для обробки помилок на самому початку. Але ж помилки виникають в кінці! Правило: Спочатку йдуть ті, що "готують" запит (парсери, логери), потім маршрути (routes), а в самому кінці — обробники помилок. Це як воронка.
-
Принцип єдиної відповідальності. Не пишіть одну гігантську функцію
godMiddleware, яка робить все: логує, перевіряє пароль і парсить боді. Розбийте це на маленькі цеглинки:logger,auth,bodyParser. З маленьких цеглинок легше будувати великі замки. -
Не блокуйте потік. Якщо ваше middleware робить щось дуже важке (наприклад, рахує число Пі до мільярдного знаку), весь сервер зупиниться для всіх користувачів. Важкі операції — це окрема тема (асинхронність), але пам'ятайте: middleware має працювати швидко. Тук-тук — пройшов далі.
6. 🧩 Підсумок
Отже, що ми сьогодні розібрали?
- Middleware — це посередник. Це фільтр, через який проходять дані.
- Ви зрозуміли силу функції
next(): без неї конвеєр зупиняється. - Ви навчилися перехоплювати запити, змінювати їх або відхиляти до того, як вони нароблять лиха.
Тепер ви не просто пишете функції, ви керуєте потоком даних у вашому додатку. Ви архітектор, який будує надійну систему безпеки та обробки.
Що далі? Тепер, коли ми вміємо фільтрувати та обробляти запити, виникає питання: а де зберігати дані про користувачів надовго? У змінних? Вони зникнуть при перезавантаженні. На наступному уроці ми познайомимося з Базами Даних і дізнаємося, як наше middleware може "спілкуватися" з вічною пам'яттю.
А поки що — це був CS50. (Тобто, ваш урок). ;)
📝 Як тобі такий формат?
Якщо ти готовий, спробуй виконати "Завдання 1" прямо зараз у голові або на папері, і ми перевіримо результат!