4. hét
Express keretrendszer
Kell nekünk:
Mit old meg ebből a node.js?
Egy általános keretrendszer node.js felett, ami (az adattárolás problémáján kívül) megpróbál általános megoldást adni a többi kérdésre.
Az express app létrehozása rendkívül egyszerű:
const express = require('express'); const app = express(); app.get('/', (req, res, next) => { res.send('Hello World!'); }); app.listen(3000, () => { console.log('Running on :3000'); });
Azt mondja meg, hogy egy lekérdezés milyen szerver oldali logika fusson le egy adott url meghívására.
Pl:
/ = Főoldal
/books/new = Új task könyv oldal
/books = Könyv lista oldal
/book/12 = 12-es számú könyv oldala
HTTP metódus alapján is szétválaszthatóak az egyes routok, de általánosan is fel lehet rájuk iratkozni
app.use('/books', (req, res, next) => { console.log('hello /books'); }); app.get('/', (req, res, next) => { console.log('hello / with GET'); }); app.post('/', (req, res, next) => { console.log('hello / with POST'); });
A routoknál megadható paraméter is, erre külön paraméter függvény is illeszthető:
app.param('bookid', (req, res, next, id) => { console.log('Bookid: ' + id); return next(); // can throw here as well }); app.get('/book/:bookid', (req, res, next) => { console.log('Valid book id!!'); return res.end(); });
Egy routera egy lépésben több middlewaret is fel lehet iratkoztatni.
app.get('/book/:bookid', (req, res, next) => { console.log('Itt is csinalunk valamit'); return next(); }, (req, res, next) => { console.log('Meg itt is'); return res.end(); });
Több routera is feliratkozhatunk egyszerre, regexpet is megadhatunk, de ezek nem ajánlottak. A /* elhagyható az első példában, a route nélküli app.use meghívás esetében az összes lekérdezésre feliratkozunk.
app.use('/*', (req, res, next) => { console.log('Mindenhol'); return next(); }); app.use(['/book/:valami','/book/*'], (req, res, next) => { console.log('Tobbhelyen'); return next(); }); app.use(/^\/commits\/(\w+)(?:\.\.(\w+))?$/, (req, res, next) => { console.log('Lol regexp'); return next(); });
Eddig csak körbejártuk, most nézzük meg mi ez:
function mw(req, res, next) { // ... return next(); }A middleware egy egyszerű függvény, ami hozzáfér a request (req) és response (res) objektumokhoz, illetve hatással lehet az őt meghívó végrehajtási láncra is.
Miven minden async, a végrejatási lánc next() hatására lép tovább (a return nem működik, csak egyszerűsítő hatása lesz)
Egy adott routera feliratkozott middlewarek egy láncot alkotnak:
Ennek a láncnak az első elemét hívja meg az express.
Ez gyakorlatilag a "Controller" rétegünk.
app.get('/book/*', (req, res, next) => { console.log('Valami tortenik'); return next(); }); app.get('/book/:userid', (req, res, next) => { console.log('Meg itt is'); return res.end(); });
Akkor miért nem controller? Miért két függvény és nem egy?
const authMW = (req, res, next) => { //ellenorizzuk, hogy be van-e jelentkezve a user // ha nincs, atiranyitjuk a bejelentkezo oldalra // ha be van, akkor nextet hivunk return next(); } app.get('/user/:userid', authMW, (req, res, next) => { console.log('Mar biztosan be van lepve a user.'); }) app.get('/books', authMW, (req, res, next) => { console.log('Mar biztosan be van lepve a user.'); })
Ezért kell mindent értelmes méretű middlewarekre darabolni.
Mi van, ha nem hívok nextet?
app.get('/user/*', (req, res, next) => { console.log('Elso blokk'); }, (req, res, next) => { console.log('Masodik blokk'); if (alma === korte){ return next(); } console.log('Harmadik'); });
const promiseWrapMW = (otherMw) => { return (...args) => { const wrappedMw = otherMw(...args); return (req, res, next) => wrappedMw(req, res, next) .catch(error => next(error)); }; }; async function myMW(req,res,next) { await iLovePromise(); return next(); } app.get('/fancy',promiseWrapMW(myMW));
A request (req) object tartalmazza a böngészőtől szerver irányba jövő kérés főbb paramétereit. Persze ezek mind feldolgozott formában, függvényeken keresztül érhetőek el.
Most csak a fontosabbakról lesz szó.
POST metódus esetében itt van találhatóak a postolt paraméterek
<form method="POST">
<input type="text" name="username"/>
</form>
app.post('/login', (req, res, next) => { if (typeof req.body.username !== 'undefined'){ console.log('Username:' + req.body.username); } return next(); })
Viszont a POST-os parsoláshoz már kell egy külső modul: body-parser. 4.0 vs 4.16.0
const express = require('express'); const app = express(); const bodyParser = require('body-parser'); app.use(bodyParser.urlencoded()); app.use(bodyParser.json()); app.post('/', (req, res, next) => { console.log(req.body.username); }); app.listen(3000, () => {});
A bodyParser.json() egy middleware függvényt ad vissza, amit fel tudunk iratkoztatni az összes routera.
Emlékszünk még a paraméterekre? Ezek a request.params alatt laknak.
app.get('/book/:bookid', (req, res, next) => { console.log(`Ez a bookid: ${req.params.bookid}`); return res.end(); });
GET paraméterek lekérdezhetőek ezen keresztül.
/books?search=alma&category[type]=sci-fi lekérdezéssel
app.get('/books', (req, res, next) => { console.log(`Search: ${req.query.search}`); console.log(`Category type: ${req.query.category.type}`); return res.end(); });
Itt is mindig érdemes ellenőrizni, hogy létezik-e a paraméter (undefined).
Az egyszerűség kedvéér a params, body és query paraméterek (ez a prioritás is) elérhetőek a param() hívással is.
app.get('/book/:bookid', (req, res, next) => { console.log(`Ez a bookid: ${req.param('userid')}`); return res.end(); });
Ezt tényleg csak egyszerű esetekben használjuk, ahogy a manual is mondja: Direct access to req.body, req.params, and req.query should be favoured for clarity - unless you truly accept input from each object.
A response objektum segítségével adunk vissza adatot / állapotot a böngészőnek. Itt is csak a fontosabb függvényeket fogom bemutatni.
A status hívással tudtok http kódokkal jelezni a kliensnek. Ha nem hívtok statust-t, 200-as lesz a státusz (minden ok).
app.get('/book/:bookid', (req, res, next) => { return res.status(404).end(); });
A böngészőnek küldhetünk egy jelzést, hogy az oldal "máshol található". Ilyenkor a böngészőbe az url ténylegesen megváltozik, és mi kapunk egy 2. http hívást az új urlre.
app.get('/book/:bookid', (req, res, next) => { return res.redirect('/login'); });
Egy egyszerű hívással küldhetünk json formátumú adatot kliens oldalra. Instant REST api
app.get('/book/:bookid', (req, res, next) => { return res.json({ key: 'value' }); });
Szöveget (akár html-t) küldhetünk kliens oldalra.
app.get('/book/:bookid', (req, res, next) => { return res.send('Lorem ipsum'); });
Ha content típust is kell állítani: res.set('Content-Type', 'text/html');
Az end metódus meghívással le lehet zárni a http választ (ez ugye elválik a végrehajtási lánctól), így a böngésző nem fog több adatot várni. Két opcionális paramétere van: end([data] [, encoding]). Ha tényleges adat utazik, inkább használjuk a json és send metódusokat.
app.get('/book/:bookid', (req, res, next) => { return res.end('Üzenet - őűáéüóöí','utf8'); });
Templating engine segítségével és hozzáadott paraméterekkel hozhatunk létre webes tartalmat. Templatingről később lesz szó.
app.get('/book/:bookid', (req, res, next) => { res.render('bookpage',{ title: 'The Hitchhiker\'s Guide to the Galaxy', ratings: [42, 0, 42, 0, 42] }); });
Az EJS egy HTML alapú templating nyelv, pár egyszerű művelettel. Ez lényegében egy modul: ejs.
app.set('view engine', 'ejs');
Gyakorlaton szó volt róla, lényegében egy könyvtár kiajánlása adott routeon statikus tartalomként. Express része, nem kell külön package hozzá.
app.use(express.static('static')); // vagy app.use('/static', express.static('static'));
Utóbbi ajánlott, mert nem keveredik össze a statikus és dinamikus tartalom.
A session egy böngésző session szintű szerver oldali oldalbetöltések közötti perzisztens tároló. A modul neve: express-session
const session = require('express-session'); app.use(session({ secret: 'keyboard cat', resave: false, saveUninitialized: true }));
Ezzel egy req.session változó válik elérhetővé, ami session szinten megtartja a tartalmát. Az alap beállítás szerint a session memóriában van, tehát ha a node.js újraindul, akkor elveszik!!
Biztonsági modul, ami segít az express alkalmazások biztonságosabbá tételéhez. A modul neve: helmet
A használata nagyon egyszerű:
var express = require('express'); var helmet = require('helmet'); var app = express(); app.use(helmet());
Gyakorlaton... :)