\section{Middleware} \begin{frame}[shrink=15]{Mi az a middleware?} \begin{block}{Definíció} Függvény HTTP kérés-válasz között, hozzáfér \texttt{req}, \texttt{res}, \texttt{next}-hez. \end{block} \begin{itemize} \item Láncolható \item Kérés előfeldolgozás, válasz utófeldolgozás \item Kérés megszakítható \end{itemize} \end{frame} \begin{frame}[shrink=15]{Middleware működése} \begin{center} \begin{tikzpicture}[node distance=1.4cm, auto] \node (browser) [rectangle, draw, text width=1.5cm, text centered] {Browser}; \node (mw1) [rectangle, draw, right of=browser, xshift=1cm, text width=2cm, text centered] {MW 1\\Logger}; \node (mw2) [rectangle, draw, right of=mw1, xshift=1.5cm, text width=2cm, text centered] {MW 2\\Auth}; \node (route) [rectangle, draw, right of=mw2, xshift=1.5cm, text width=1.5cm, text centered] {Route}; \draw[->, thick] (browser) -- node[above] {Req} (mw1); \draw[->, thick] (mw1) -- node[above] {\tiny next()} (mw2); \draw[->, thick] (mw2) -- node[above] {\tiny next()} (route); \draw[->, thick, dashed] (route) -- node[below] {Res} (browser); \end{tikzpicture} \end{center} \begin{alertblock}{Fontos} \texttt{next()} nélkül kérés megáll! \end{alertblock} \end{frame} \begin{frame}[shrink=15]{Middleware típusok} \begin{enumerate} \item \textbf{Application-level}: \texttt{app.use()} \item \textbf{Router-level}: \texttt{router.use()} \item \textbf{Error-handling}: \texttt{(err, req, res, next)} \item \textbf{Built-in}: \texttt{express.json()}, \texttt{express.static()} \item \textbf{Third-party}: \texttt{cors}, \texttt{helmet}, \texttt{morgan} \end{enumerate} \end{frame} \begin{frame}[fragile,shrink=20]{Logger middleware} \begin{verbatim} const logger = (req, res, next) => { console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`); next(); }; app.use(logger); app.get('/', (req, res) => { res.json({ msg: 'Hello' }); }); // Kimenet: // [2026-02-24T10:30:15.000Z] GET / \end{verbatim} \end{frame} \begin{frame}[fragile,shrink=20]{Request timing middleware} \begin{verbatim} const timer = (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(timer); // GET /api/users - 145ms \end{verbatim} \end{frame} \begin{frame}[fragile,shrink=20]{Validation middleware} \begin{verbatim} const validateEmail = (req, res, next) => { const { email } = req.body; const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!email || !re.test(email)) { return res.status(400).json({ error: 'Invalid email' }); } next(); }; app.post('/register', validateEmail, (req, res) => { res.json({ success: true }); }); \end{verbatim} \end{frame} \begin{frame}[fragile,shrink=20]{Auth middleware} \begin{verbatim} const auth = (req, res, next) => { const token = req.headers.authorization?.split(' ')[1]; if (!token) { return res.status(401).json({ error: 'Token required' }); } try { const decoded = jwt.verify(token, SECRET); req.user = decoded; next(); } catch (err) { res.status(403).json({ error: 'Invalid token' }); } }; app.get('/protected', auth, (req, res) => { res.json({ user: req.user }); }); \end{verbatim} \end{frame} \begin{frame}[fragile,shrink=20]{Role-based middleware} \begin{verbatim} const requireRole = (role) => { return (req, res, next) => { if (req.user.role !== role) { return res.status(403).json({ error: 'Forbidden' }); } next(); }; }; app.delete('/user/:id', auth, requireRole('admin'), (req, res) => { res.json({ deleted: true }); }); \end{verbatim} \end{frame} \begin{frame}[fragile,shrink=20]{Error handling middleware} \begin{verbatim} // Mindig utolsóként! app.use((err, req, res, next) => { console.error(err.stack); res.status(err.status || 500).json({ error: err.message || 'Internal Server Error' }); }); // Használat app.get('/error', (req, res, next) => { const err = new Error('Something broke!'); err.status = 500; next(err); }); \end{verbatim} \end{frame} \begin{frame}[fragile,shrink=20]{CORS middleware} \begin{verbatim} const cors = require('cors'); // Minden origin engedélyezése app.use(cors()); // Specifikus beállítás app.use(cors({ origin: 'https://frontend.com', credentials: true, methods: ['GET', 'POST', 'PUT', 'DELETE'] })); \end{verbatim} \end{frame} \begin{frame}[shrink=15]{Built-in middleware} \begin{itemize} \item \texttt{express.json()}: JSON body parsing \item \texttt{express.urlencoded()}: URL-encoded body parsing \item \texttt{express.static()}: Static fájlok kiszolgálása \end{itemize} \begin{exampleblock}{Használat} \texttt{app.use(express.json());}\\ \texttt{app.use(express.static('public'));} \end{exampleblock} \end{frame} \begin{frame}[shrink=15]{Third-party middleware} \begin{description} \item[helmet] HTTP security headers \item[morgan] HTTP request logger \item[compression] Response compression (gzip) \item[cookie-parser] Cookie parsing \item[express-session] Session kezelés \item[passport] Authentication \end{description} \end{frame} \begin{frame}[shrink=15]{Middleware best practices} \begin{enumerate} \item Mindig hívd meg \texttt{next()}-et (vagy küldjél választ) \item Error middleware utolsóként \item Sorrendiség számít! \item Global middleware-ek előre (\texttt{cors}, \texttt{helmet}) \item Route-specifikus middleware-ek közvetlenül route-hoz \item Async middleware-eknél használj try-catch-et \end{enumerate} \end{frame}