3. 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ű:
const express = require('express');
const helmet = require('helmet');
const app = express();
app.use(helmet());
Gyakorlaton... :)