Skip to content

Latest commit

 

History

History
629 lines (430 loc) · 14.5 KB

02.2.express.md

File metadata and controls

629 lines (430 loc) · 14.5 KB
title
Fyrirlestur 2.2 — Express

Fyrirlestur 2.2 — Express

Vefforritun 2 — HBV403G

Ólafur Sverrir Kjartansson, osk@hi.is


Stærri bakendar

  • 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


Hello world

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

Settings

  • Getum stillt app með get() og set(), t.d.
    • env, segir til um hvort app keyri í production eða development
    • view engine, hvaða template vél við notum
    • views, í hvaða möppu eru template
    • o.fl.

use

  • 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 og res

app.use((req, res) => {
  res.send('Hello World!');
});

app.use('/bye', (req, res) => {
  res.send('Bye!');
});

Express og console

  • 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

Express og sjálfvirkt restart

  • 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
  • nodemon eftir Remy Sharp eitt af þeim

nodemon

> 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ða foo) með því að skýra argument annað
  • Hefur bæði eigindi og föll

Request eigindi

  • 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ðni
  • req.query, hlutur með öllum querystring breytum
  • o.fl.

Request föll

  • 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
  • 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

Response eigindi

  • res.headersSent, boolean sem segir okkur hvort headers hafi verið sendir client eða ekki
  • res.locals, hlutur sem inniheldur gögn sem verða aðgengileg fyrir view/template, getum bætt við upplýsingum frá request

Response föll

  • res.write(content), erft frá http, skrifar í response straum
  • res.end(content), erft frá http, endar response straum og sendir á client
    • Á ekki við headers, skrifar content í straum
  • o.fl.

  • res.send(content), skrifar í response „straum“, t.d. setur Content-Length header og endar hann
    • utf-8 verður sett sem charset fyrir efni
  • res.status(statusCode), setur HTTP status á response
  • res.set(field, value), setur response header

  • res.json(data), eins og send() nema sendir JSON gögn
  • res.redirect(location), framkvæmir redirect á location
  • o.fl.

Dæmi


Subapps

  • 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);

Routing

  • 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ðkenni hello-world

Route

  • 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 og post
    • 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');
});

Route callback

  • 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)

Route parameters

  • 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}`);
});

Express Router

  • Getum búið til route án þess að hafa app með því að nota const router = express.Router()
    • Exportum síðan router
  • 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);

Dæmi


Middleware í Express

  • 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ða res
    • 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() heldur req.send() til að enda reponse

  • Getum skilgreint fyrir
    • Allt forrit
    • Per route
    • Villumeðhöndlun

Middleware fyrir app

function helloWorld(req, res, next) {
  console.log('Hello world!');
  next();
}

app.use(helloWorld);

Middleware á route

function userHandler(req, res, next) {
  // ...
  next();
}

router.get(
  '/user/:id',
  helloWorld,
  userHandler,
);

Villumeðhöndlun

  • 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.

404 villur

  • 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
  • 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!');
});

Innbyggð middleware


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“.


Dæmi


Templating

  • 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

Template í Express

  • Sækjum útfærslu á template máli fyrir Express
  • Skilgreinum á app hvaða template mál við notum og hvar template eru geymd
    • app.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ð birta template 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 úr header.ejs inn á viðeigandi stað
  • Öll gögn skilgreind með app.locals eru aðgengileg í template
  • Nánari skjölun á EJS

app.locals

  • 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

Dæmi


Express async await

  • 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));