title |
---|
Fyrirlestur 2.2 — Express |
Ólafur Sverrir Kjartansson, osk@hi.is
- Gætum vel skrifað okkar eigin bakenda frá grunni í Node.js
- Aðeins notað grunneiningar! Treystum engum!
- Værum að finna upp mörg hjól og eyða tíma
- Express: Fast, unopinionated, minimalist web framework for Node.js
- Smátt í sniðum en gefur mikla möguleika á að bæta við virkni til að gera hér um bil hvað sem er
npm install express --save
import express from 'express';
const app = express();
const host = '127.0.0.1';
const port = 3000;
app.use((req, res) => {
res.send('Hello World!');
});
app.listen(port, host, () => {
console.log(
`Server @ http://${host}:${port}/`,
);
});
- Búum til Express forrit með því að kalla í
express()
- Sér m.a. um:
- routing fyrir HTTP beiðnir
- Middleware uppsetningar
- Stillingar á birtingu (með HTML)
- Veita okkur aðgang að response og request hlutum
- Getum stillt
app
meðget()
ogset()
, t.d.env
, segir til um hvort app keyri íproduction
eðadevelopment
view engine
, hvaða template vél við notumviews
, í hvaða möppu eru template- o.fl.
app.use()
festir kóða við app, bæði fyrir ákveðna slóð og án- Ef við tilgreinum ekki slóð mun kóði keyra fyrir öll request
- Kóði er middleware, fyrstu tvö argument eru
req
ogres
app.use((req, res) => {
res.send('Hello World!');
});
app.use('/bye', (req, res) => {
res.send('Bye!');
});
- Alltaf þegar við notum
console
, t.d.console.log
, þá skrifast það út í skelina þar sem við kveiktum á vefþjóninum - Allt sem fer yfir HTTP til notanda þarf að fara gegnum
res
hlutinn
- Getur orðið hvimleitt að handvirkt slökkva og kveikja aftur á vefþjón í þróun
- Til slatti af tólum sem fylgjast með og restarta fyrir okkur
- Eins og
browser-sync
en fyrir bakenda
- Eins og
- nodemon eftir Remy Sharp eitt af þeim
> npm install -g nodemon
...
> nodemon app.js
[nodemon] watching: *.*
[nodemon] starting `app.js`
...
[nodemon] restarting due to changes...
Eða í projectinu sjálfu:
npm install --save-dev nodemon
í package.json
, undir scripts
:
"dev": "nodemon app.js"
> npm run dev
[nodemon] watching: *.*
req
stendur fyrir HTTP request sem kemur frá client- Getum skýrt annað (t.d.
request
eðafoo
) með því að skýra argument annað
- Getum skýrt annað (t.d.
- Hefur bæði eigindi og föll
req.url
, erft fráhttp
, gæti hafa verið átt viðreq.originalUrl
,url
án þess að átt hafi verið viðreq.ip
, IP tala þess sem gerði beiðnireq.query
, hlutur með öllum querystring breytum- o.fl.
req.accepts(types)
, athugar hvort client tekur við ákveðnu MIME type- MIME type segir til um á hvaða formi gögn eru, t.d.
application/json
- MIME type segir til um á hvaða formi gögn eru, t.d.
req.get(header)
, skilar request header, ekki hástafanæmt- o.fl.
res
stendur fyrir HTTP response sem við erum að skila til client- Hefur bæði eigindi og föll
res.headersSent
, boolean sem segir okkur hvort headers hafi verið sendir client eða ekkires.locals
, hlutur sem inniheldur gögn sem verða aðgengileg fyrir view/template, getum bætt við upplýsingum frá request
res.write(content)
, erft fráhttp
, skrifar í response straumres.end(content)
, erft fráhttp
, endar response straum og sendir á client- Á ekki við headers, skrifar
content
í straum
- Á ekki við headers, skrifar
- o.fl.
res.send(content)
, skrifar í response „straum“, t.d. seturContent-Length
header og endar hannutf-8
verður sett sem charset fyrir efni
res.status(statusCode)
, setur HTTP status á responseres.set(field, value)
, setur response header
res.json(data)
, eins ogsend()
nema sendir JSON gögnres.redirect(location)
, framkvæmir redirect álocation
- o.fl.
- Getum unnið með mörg app í einu með
use
- Hópum saman virkni, t.d. eitt „subapp“ fyrir formið, annað fyrir innskráninguna
use
er líka notað til að skilgreina middleware
import express from 'express';
const app = express();
const subapp = express();
app.get('/', (req, res) => {
res.send('hello world');
});
subapp.get('/', (req, res) => {
res.send('hello from subapp');
});
app.use('/sub', subapp);
- Höfum séð leiðir til þess að útbúa slóðir sem keyra ákveðin kóða
- Ekki sérstaklega handhægt að gera í höndunum
- Framework geta hjálpað okkur við að útbúa góðar og skýrar slóðir
- „Friendly URL“ eru slóðir sem auðvelt er að lesa úr og nota, bæði af fólki og vélum
/programming/web
ekki/?cat=programming&sub=web
- Einnig stuðningur við gagnaflutning, t.d. senda streng gegnum URL
/article/hello-world
skilar okkur færslu með auðkennihello-world
- Express hefur stuðning við routing
- Route eru slóð (URL) sem forritið okkar svarar beiðnum frá client á
- Passar við HTTP aðgerðir
- Samanstendur af URL, HTTP aðferð og einum eða fleirum föllum
app.METHOD(URL, callback)
- Margar HTTP aðferðir til, en við notum að mestu
get
ogpost
all
er hægt að nota til að svara fyrir allar aðferðir
- URL getur notað regular expressions en ættum að forðast nema þurfum virkilega
app.get(/foo.*$/, callback)
svarar fyrir allt sem byrjar á/foo
- Query string er ekki partur af route path, nálgumst með
req.query
app.get('/hello', (req, res) => {
res.send('hello');
});
app.get('/hello/world', (req, res) => {
res.send('hello world');
});
app.post('/data', (req, res) => {
res.send('posted to data');
});
- Callback sem skilgreint er fyrir route vísar í middleware
- Geta verið mörg í röð:
app.get('/', cb1, cb2)
- Í fylki:
app.get('/', [cb1, cb2])
- Eða blanda:
app.get('/', [cb1, cb2], cb3)
- Ef við skilgreinum route með parameter getum við nálgast þau gögn í
req
app.get('/users/:userId', cb)
req.params.userId
- Dýnamísk route, bregðast við beiðni á keyrslutíma
app.get('/hello/:name', (req, res) => {
res.send(`hello ${req.params.name}`);
});
- Getum búið til route án þess að hafa
app
með því að notaconst router = express.Router()
- Exportum síðan
router
- Exportum síðan
- Skiptum forriti upp í einingar þar sem hver sér um ákveðin hluta af routes
router.js
import express from 'express';
export const router = express.Router();
router.get('/', (req, res) => {
res.send('Foo!');
});
import router from './router.js';
const app = express();
app.use('/foo', router);
- Middleware er kóði sem sér um einhvern part af því að útbúa svar
- Getur bætt einhverjum gögnum við fyrir önnur middleware
- Séð um innskráningu
- Loggað hvað er að gerast
- o.fl.
- Middleware hefur aðgang að request og response hlutum og næsta middleware
function middleware(req, res, next)
- Getur
- Keyrt kóða
- Breytt
req
eðares
- Endað request-response keyrslu
- Kallað í næsta middleware
- Middleware eru keyrð í FIFO röð
- Verðum að keyra
next()
á einhverjum tímapunkti til þess að næsta middleware geti tekið við- Fyrir route sem skilar upplýsingum viljum við samt oftast ekki kalla í
next()
heldurreq.send()
til að enda reponse
- Fyrir route sem skilar upplýsingum viljum við samt oftast ekki kalla í
- Getum skilgreint fyrir
- Allt forrit
- Per route
- Villumeðhöndlun
function helloWorld(req, res, next) {
console.log('Hello world!');
next();
}
app.use(helloWorld);
function userHandler(req, res, next) {
// ...
next();
}
router.get(
'/user/:id',
helloWorld,
userHandler,
);
- Við skilgreinum villumeðhöndlunar middleware með auka argument
function (err, req, res, next)
- Skilgreind seinast í app, á eftir öllum öðrum middleware
- Sér um að taka til, logga villu, senda notanda villuskilaboð o.sfr.
- Getum nýtt okkur middleware til þess að grípa 404 villur
- Verið að reyna að opna route sem við höfum ekki skilgreint
- Setjum middleware á eftir öllum routes sem við vitum að muni aðeins keyra ef ekkert hefur sent reponse
- En samt á undan villumeðhöndlun
app.use((req, res, next) => {
res.status(404).send('404 Not Found');
});
app.use((err, req, res, next) => {
console.error(err);
res.status(500).send('Error occured');
});
- Ef við endum í 404 middleware, vitum við að ekkert route „að ofan“ höndlaði
request
- Þ.a.l. skilum við
404 Not Found
- Getum bæðið sent
req
áfram úr dýnamísku route ef ekkert fannst, eða höndlað í route
- Þ.a.l. skilum við
- Ef við sendum
Error
hlut ínext()
mun villumeðhöndlunar route taka við því
app.get('/error', (req, res) => {
throw new Error('Villa!');
});
app.use((err, req, res, next) => {
console.error(err);
res.status(500).send('Villa!');
});
- Express hefur nokkur innbyggð middleware
- Eitt af þeim er til að birta statískar skrár
- t.d. CSS, myndir
- Serving static files in Express
Fyrir CommonJS:
import path from 'path';
import express from 'express';
const app = express();
const publicPath = path.join(
__dirname, 'public',
);
app.use(express.static(publicPath));
Fyrir ES Modules:
import express from 'express';
const app = express();
const url = import.meta.url;
const publicPath = new URL('./public', url)
.pathname;
app.use(express.static(publicPath));
import.meta.url
er slóð á skránna sem keyrir.
new URL
býr til slóð út frá þeim „grunn“.
- Til að birta síðu þurfum við að hafa útlit fyrir hana – eitthvað ákveðið HTML
- Ekki skilvirkt að útbúa sjálf HTML með því að setja saman strengi
- Template leyfa okkur að útbúa útlit óháð virkni, við fáum gögn og við birtum þau á ákveðinn hátt
- Mörg template mál til, t.d.
- Pug, EJS, Handlebars, Mustache
- Sækjum útfærslu á template máli fyrir Express
- Skilgreinum á
app
hvaða template mál við notum og hvar template eru geymdapp.set('view engine', 'ejs');
app.set('views', viewsPath);
(viewsPath
útfært eins og fyrir static middleware)
res.render(template, data)
notar skilgreindar template vél til að birtatemplate
meðdata
- Notum í staðinn fyrir
res.send()
- Aðskiljum HTML frá kóða
Skrifum HTML en með EJS syntax til að nálgast gögn og setja saman síður
<% %>
fyrir flæðistýringar- Blöndum JavaScript inn í template
<%= %>
til að birta gögn
<%- %>
til að birta gögn unescaped- Birtir hrátt HTML í gögnum, hættulegt!
<% include header %>
bætir template úrheader.ejs
inn á viðeigandi stað- Öll gögn skilgreind með
app.locals
eru aðgengileg í template - Nánari skjölun á EJS
app.locals
er hlutur sem geymir gögn fyrir app- Aðgengileg á meðan app keyrir
- Aðgengilegt frá
request
meðreq.app.locals
- Aðgengilegt frá template úr
locals
- Ef við notum
async await
í Express middleware þurfum við að passa upp á að grípa villur - Getum gert per middleware eða búið til higher-order fall sem sér almennt um
- Sama pæling og með að keyra „main“ fall í dæmum í fyrirlestri 1
async function asyncMW(req, res, next) {
// ...
next();
}
/**
* Wrap an async function with error handling
* @params {function} fn Function to wrap
* @returns {Promise} Promise w/error handling
*/
function catchErrors(fn) {
return (req, res, next) =>
fn(req, res, next).catch(next);
}
app.get('/', catchErrors(asyncMW));