\section{Middleware} \begin{frame}{Mi az a middleware?} \begin{block}{Definíció} Függvény, amely HTTP kérés és válasz között fut, hozzáfér \texttt{req}, \texttt{res}, \texttt{next}-hez. \end{block} \begin{itemize} \item Láncolható függvények \item Kérés előfeldolgozás \item Válasz utófeldolgozás \item Kérés megszakítás \end{itemize} \begin{exampleblock}{Express middleware} \texttt{function middleware(req, res, next) \{ ... \}} \end{exampleblock} \end{frame} \begin{frame}[shrink=5]{Middleware működése} \begin{center} \begin{tikzpicture}[node distance=1.5cm, auto] \node (browser) [rectangle, draw, text width=2cm, text centered] {Browser}; \node (mw1) [rectangle, draw, right of=browser, xshift=1.5cm, text width=2.5cm, text centered] {Middleware 1\\(Logger)}; \node (mw2) [rectangle, draw, right of=mw1, xshift=2cm, text width=2.5cm, text centered] {Middleware 2\\(Auth)}; \node (route) [rectangle, draw, right of=mw2, xshift=2cm, text width=2cm, text centered] {Route Handler}; \draw[->, thick] (browser) -- node[above] {Request} (mw1); \draw[->, thick] (mw1) -- node[above] {\texttt{next()}} (mw2); \draw[->, thick] (mw2) -- node[above] {\texttt{next()}} (route); \draw[->, thick, dashed] (route) -- node[below] {Response} (browser); \end{tikzpicture} \end{center} \begin{alertblock}{Fontos!} Ha nem hívod meg a \texttt{next()}-et, a kérés megáll és nem jut el a következő middleware-hez vagy route handler-hez! \end{alertblock} \end{frame} \begin{frame}[shrink=5]{Middleware típusok} \begin{enumerate} \item \textbf{Application-level} \begin{itemize} \item Egész app-ra \end{itemize} \item \textbf{Router-level} \begin{itemize} \item Adott routerre \end{itemize} \item \textbf{Error-handling} \begin{itemize} \item 4 param: \texttt{(err, req, res, next)} \end{itemize} \item \textbf{Built-in} \begin{itemize} \item \texttt{express.json()} \end{itemize} \item \textbf{Third-party} \begin{itemize} \item \texttt{cors}, \texttt{helmet} \end{itemize} \end{enumerate} \end{frame} \begin{frame}[fragile,shrink=10]{Egyszerű logger middleware} \begin{block}{Request logging - 1. rész} \small \begin{verbatim} const express = require('express'); const app = express(); const logger = (req, res, next) => { const timestamp = new Date().toISOString(); console.log(`[${timestamp}] ${req.method} ${req.url}`); next(); }; \end{verbatim} \end{block} \end{frame} \begin{frame}[fragile,shrink=10]{Egyszerű logger middleware (folyt.)} \begin{block}{Request logging - 2. rész (használat)} \small \begin{verbatim} app.use(logger); app.get('/', (req, res) => { res.json({ message: 'Hello World' }); }); app.get('/users', (req, res) => { res.json({ users: ['Alice', 'Bob'] }); }); app.listen(3000); // Kimenet: // [2026-02-23T10:30:15.000Z] GET / // [2026-02-23T10:30:20.000Z] GET /users \end{verbatim} \end{block} \end{frame} \begin{frame}[fragile,shrink=10]{Request timing middleware} \begin{block}{Válaszidő mérése} \small \begin{verbatim} const requestTimer = (req, res, next) => { req.startTime = Date.now(); res.on('finish', () => { const duration = Date.now() - req.startTime; console.log(`${req.method} ${req.url} - ${duration}ms`); }); next(); }; app.use(requestTimer); // Kimenet: // GET /api/users - 145ms // POST /api/login - 523ms \end{verbatim} \end{block} \end{frame} \begin{frame}[fragile]{Validation middleware} \begin{block}{Input validáció - 1. rész} \small \begin{verbatim} // email validáló middleware const validateEmail = (req, res, next) => { const { email } = req.body; if (!email) { return res.status(400).json({ error: 'Email is required' }); } const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { return res.status(400).json({ error: 'Invalid email format' }); } next(); // Validáció sikeres, tovább }; \end{verbatim} \end{block} \end{frame} \begin{frame}[fragile]{Validation middleware (folyt.)} \begin{block}{Input validáció - 2. rész (használat)} \small \begin{verbatim} app.use(express.json()); // Body parser middleware // Route-specific middleware app.post('/register', validateEmail, (req, res) => { const { email, password } = req.body; // Email már validálva van a middleware által // Regisztráció logika... res.json({ message: 'User registered', email }); }); // Több middleware egyszerre app.post('/login', validateEmail, validatePassword, // Másik validator (req, res) => { // Login logika... } ); \end{verbatim} \end{block} \end{frame} \begin{frame}[fragile]{Authentikációs middleware - JWT} \begin{block}{Token validáció - 1. rész} \small \begin{verbatim} const jwt = require('jsonwebtoken'); const authenticateJWT = (req, res, next) => { // Token a Authorization header-ből const authHeader = req.headers.authorization; if (!authHeader) { return res.status(401).json({ error: 'Access token required' }); } // Bearer TOKEN formátum const token = authHeader.split(' ')[1]; if (!token) { return res.status(401).json({ error: 'Token not found' }); } \end{verbatim} \end{block} \end{frame} \begin{frame}[fragile]{Authentikációs middleware - JWT (folyt.)} \begin{block}{Token validáció - 2. rész} \small \begin{verbatim} // Token verify try { const decoded = jwt.verify(token, process.env.JWT_SECRET); // Felhasználói adat hozzáadása a request objektumhoz req.user = decoded; // { userId, email, role, ... } next(); // Sikeres authentikáció } catch (error) { return res.status(403).json({ error: 'Invalid or expired token' }); } }; // Használat app.get('/api/profile', authenticateJWT, (req, res) => { // req.user elérhető itt res.json({ user: req.user }); }); \end{verbatim} \end{block} \end{frame} \begin{frame}[fragile]{Autorizációs middleware - Role-based} \begin{block}{Szerepkör ellenőrzés} \small \begin{verbatim} // Role-based access control middleware factory const requireRole = (role) => { return (req, res, next) => { // Feltételezzük, hogy az authenticateJWT már futott if (!req.user) { return res.status(401).json({ error: 'Not authenticated' }); } if (req.user.role !== role) { return res.status(403).json({ error: `Forbidden: ${role} role required` }); } next(); // Jogosultság OK }; }; \end{verbatim} \end{block} \end{frame} \begin{frame}[fragile]{Autorizációs middleware (folyt.)} \begin{block}{Használat szerepkör ellenőrzésre} \small \begin{verbatim} // Admin-only endpoint app.delete('/api/users/:id', authenticateJWT, // 1. Authentikáció requireRole('admin'), // 2. Autorizáció (req, res) => { // Csak admin férhet ide const userId = req.params.id; // Delete user logika... res.json({ message: 'User deleted' }); } ); // Több szerepkör támogatása const requireAnyRole = (...roles) => { return (req, res, next) => { if (!req.user || !roles.includes(req.user.role)) { return res.status(403).json({ error: 'Forbidden' }); } next(); }; }; app.get('/api/reports', authenticateJWT, requireAnyRole('admin', 'manager'), ...); \end{verbatim} \end{block} \end{frame} \begin{frame}[fragile]{Rate limiting middleware} \begin{block}{API hívások korlátozása} \small \begin{verbatim} const rateLimit = require('express-rate-limit'); // Rate limiter konfiguráció const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 perc max: 100, // Max 100 kérés 15 percenként message: 'Too many requests, please try again later', standardHeaders: true, // RateLimit-* headers legacyHeaders: false }); // Application-level app.use('/api/', limiter); // Strict limiter login-hoz const loginLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 5, // Max 5 login kísérlet 15 percenként skipSuccessfulRequests: true // Sikeres login nem számít bele }); app.post('/api/login', loginLimiter, (req, res) => { ... }); \end{verbatim} \end{block} \end{frame} \begin{frame}[fragile]{Error handling middleware} \begin{block}{Központi hibakezelés} \small \begin{verbatim} // Error handling middleware (4 paraméter!) const errorHandler = (err, req, res, next) => { // Logolás console.error('Error:', err); // Custom error osztályok kezelése if (err.statusCode) { return res.status(err.statusCode).json({ error: err.name, message: err.message }); } // Váratlan hibák res.status(500).json({ error: 'InternalServerError', message: 'Something went wrong' }); }; // Error handler middleware UTOLJÁRA kell regisztrálni! app.use(errorHandler); \end{verbatim} \end{block} \end{frame} \begin{frame}[fragile]{Error handling használata} \begin{block}{Hibák továbbítása a next()-el} \small \begin{verbatim} // Async route handler app.get('/api/users/:id', async (req, res, next) => { try { const user = await User.findById(req.params.id); if (!user) { const error = new Error('User not found'); error.statusCode = 404; throw error; // vagy: return next(error); } res.json({ user }); } catch (error) { next(error); // Error handler middleware-nek továbbítja } }); // A errorHandler middleware automatikusan feldolgozza \end{verbatim} \end{block} \end{frame} \begin{frame}[fragile]{CORS middleware} \begin{block}{Cross-Origin Resource Sharing} \small \begin{verbatim} const cors = require('cors'); // Egyszerű CORS - minden origin engedélyezve app.use(cors()); // Konfigurált CORS const corsOptions = { origin: 'https://frontend.example.com', // Engedélyezett origin methods: ['GET', 'POST', 'PUT', 'DELETE'], allowedHeaders: ['Content-Type', 'Authorization'], credentials: true, // Cookie-k engedélyezése maxAge: 86400 // Preflight cache 24 óra }; app.use(cors(corsOptions)); // Route-specific CORS app.get('/api/public', cors(), (req, res) => { res.json({ message: 'Public data' }); }); \end{verbatim} \end{block} \end{frame} \begin{frame}[fragile]{Security middleware - Helmet} \begin{block}{HTTP header biztonság} \small \begin{verbatim} const helmet = require('helmet'); // Helmet middleware - biztonságos HTTP headerek app.use(helmet()); // Helmet beállítja: // - Content-Security-Policy // - X-DNS-Prefetch-Control // - X-Frame-Options (SAMEORIGIN) // - X-Content-Type-Options (nosniff) // - X-XSS-Protection // stb. // Egyedi konfiguráció app.use(helmet({ contentSecurityPolicy: { directives: { defaultSrc: ["'self'"], styleSrc: ["'self'", "'unsafe-inline'"] } } })); \end{verbatim} \end{block} \end{frame} \begin{frame}[fragile]{Middleware láncolás} \begin{block}{Több middleware egyidejű használata} \small \begin{verbatim} // Middleware láncolás egy route-on app.post('/api/posts', authenticateJWT, // 1. Authentikáció requireRole('user'), // 2. Autorizáció validatePost, // 3. Validáció uploadImages, // 4. Képfeltöltés (req, res) => { // 5. Route handler // Post létrehozás logika res.json({ message: 'Post created' }); } ); // Middleware tömb const postMiddlewares = [ authenticateJWT, requireRole('user'), validatePost ]; app.post('/api/posts', postMiddlewares, (req, res) => { // ... }); \end{verbatim} \end{block} \end{frame} \begin{frame}{Middleware Best Practices} \begin{enumerate} \item \textbf{Sorrend számít:} \begin{itemize} \item CORS és security middleware-ek ELŐRE \item Error handler middleware HÁTRA \end{itemize} \vspace{0.2cm} \item \textbf{Mindig hívd meg a next()-et:} \begin{itemize} \item Kivéve ha response-t küldesz vagy hibát dobsz \end{itemize} \vspace{0.2cm} \item \textbf{Error handling:} \begin{itemize} \item Async függvényekben try-catch + next(error) \item 4 paraméteres error handler middleware \end{itemize} \vspace{0.2cm} \item \textbf{Teljesítmény:} \begin{itemize} \item Ne futtass middleware-t szükségtelenül (route-specific) \item Async műveletek csak ha szükséges \end{itemize} \vspace{0.2cm} \item \textbf{Újrafelhasználhatóság:} \begin{itemize} \item Middleware factory pattern (pl. requireRole) \item Konfigurálható middleware-ek \end{itemize} \end{enumerate} \end{frame} \begin{frame}{Middleware execution order} \begin{block}{Tipikus middleware sorrend Express alkalmazásban} \small \begin{enumerate} \item \textbf{helmet()} - Security headers \item \textbf{cors()} - CORS beállítások \item \textbf{morgan/logger} - Request logging \item \textbf{express.json()} - Body parser \item \textbf{express.urlencoded()} - URL-encoded body \item \textbf{cookie-parser()} - Cookie parsing \item \textbf{Rate limiter} - API rate limiting \item \textbf{Custom middlewares} - Saját middleware-ek \item \textbf{Routes} - Route handler-ek \item \textbf{404 handler} - Not found middleware \item \textbf{Error handler} - Központi hibakezelés (4 param) \end{enumerate} \end{block} \end{frame} \begin{frame}{Összefoglalás - Middleware} \begin{itemize} \item \textbf{Middleware} = Függvény a kérés-válasz között \item \textbf{Paraméterek:} (req, res, next) vagy (err, req, res, next) \item \textbf{Típusok:} Application, Router, Error-handling, Built-in, Third-party \item \textbf{next():} Következő middleware-re adja a vezérlést \item \textbf{Használat:} \begin{itemize} \item Logging (morgan, winston) \item Authentication (JWT validáció) \item Authorization (szerepkör ellenőrzés) \item Validation (input ellenőrzés) \item Rate limiting (express-rate-limit) \item Security (helmet, cors) \item Error handling (központi hibakezelés) \end{itemize} \item \textbf{Sorrend fontos:} Security → Parsing → Auth → Routes → Error \end{itemize} \end{frame}