die meisten Projekte verwenden express
middleware: via .use()
routing: via .get()
, .post()
, ...
hello world mit express:
// server.js
import express from 'express';
const app = express();
// provide a function that handles a request to "/"
// and sends a response
app.get('/', (req, res) => {
// note: we should actually return a complete HTML file
res.send('<h1>Hello World!</h1>\n');
});
// listen on localhost:3000
app.listen(3000);
hello world ohne express (siehe https://nodejs.org/en/docs/guides/getting-started-guide/):
// server.js
import http from 'http';
const handler = (req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/html; charset=UTF-8');
res.end('<h1>Hello World!</h1>\n');
};
const server = http.createServer(handler);
server.listen(3000);
Erstellen von package.json:
{
"type": "module",
"eslintConfig": {
"sourceType": "module"
},
"scripts": {
"start": "node server.js"
}
}
Installation von express:
npm install express
ausführen via:
npm run start
öffne http://localhost:3000 im Browser für das Resultat
Web-Entwicklung in node geschieht über Request-Hanlder-Funktionen, z.B.:
(req, res) => {
res.send('<h1>Hello World!</h1>\n');
};
Ein Request Handler bekommt zwei Argumente:
req
- repräsentiert den eingehenden request (Klasse IncomingMessage in node, Unterklasse Request in express)res
- repräsentiert die response / Antwort, die gensendet wird (Klasse ServerResponse in node, Unterklasse Response in express)Übung: erstelle eine Seite, die die aktuelle Uhrzeit anzeigt
Lösung:
import express from 'express';
const app = express();
app.use((req, res) => {
const timeString = new Date().toTimeString();
res.send('<h1>current time: ' + timeString + '</h1>');
});
app.listen(3000);
Beispiel für ein Request-Objekt:
{
"url": "/",
"method": "GET",
"headers": {
"user-agent": "Mozilla/5.0 (Windows ..."
}
}
Klasse in reinem Node bzw in connect: IncomingMessage
Klasse in express: Request
Ãœbung: Erstelle eine Website mit unterschiedlichen Antworten basierend auf der angefragten URL
Klasse in reinem Node bzw in connect: ServerResponse
Klasse in express: Response
Methoden in Express:
.send()
.set()
.status()
Beispiel:
res.set({ 'Content-Type': 'text/plain' });
res.send('Date:\n' + new Date().toLocaleDateString());
res.status(404);
res.set({ 'Content-Type': 'text/plain' });
res.send('Document not found.\n');
Setzen eines Cookies:
res.cookie('a', '1');
bzw explizit:
res.set({ 'Set-Cookie': 'a=1' });
app.get('/', (req, res) => {
res.send('<h1>Home</h1>\n');
});
app.get('/about', (req, res) => {
res.send('<h1>About</h1>\n');
});
andere Methoden: .post
, .put
, .delete
, ...
app.get('/articles/:id', (req, res) => {
const articleId = req.params.id;
// ...
});
app.get('/home', (req, res) => {
res.redirect('/');
});
manuell (gefährlich):
app.get('/', (req, res) => {
const name = 'world';
res.send(`
<!DOCTYPE html>
<html>
<head><title>Hello ${name}</title></head>
<body>Hello ${name}</body>
</html>
`);
});
mit Hilfe einer Template Engine:
allgemeines Prozedere:
import express from 'express';
import myengine from 'myengine';
const app = express();
app.engine('myengine', myengine());
app.set('view engine', 'myengine');
app.get('/', (req, res) => {
const name = 'world';
// renders 'views/index.myengine'
res.render('index', { name: name });
});
registrieren verschiedener Template Engines:
import ejs from 'ejs';
import expressHandlebars from 'express-handlebars';
import expressReactViews from 'express-react-views';
app.engine('ejs', ejs);
app.engine('handlebars', expressHandlebars());
app.engine('jsx', expressReactViews.createEngine());
npm-Pakete: express-react-views, react, react-dom
views/index.jsx:
import React from 'react';
const Index = ({ name }) => {
return (
<html>
<head>
<title>Hello, {name}!</title>
</head>
<body>
<h1>Hello, {name}!</h1>
</body>
</html>
);
};
export default Index;
Ãœbungen:
Pokeapi Teil 1:
app.get('/:id', async (req, res) => {
const id = req.params.id;
const dataRes = await fetch(
`https://pokeapi.co/api/v2/pokemon/${id}`
);
const data = await dataRes.json();
await res.render('pokemon', { id: id, data: data });
});
app.get('/', (req, res) => {
res.redirect('/1');
});
Pokeapi Teil 2:
// views/pokemon.jsx
import React from 'react';
const Pokemon = (props) => {
const id = Number(props.id);
const data = props.data;
return (
<div>
<article>
<h1>{data.species.name}</h1>
<img src={data.sprites.front_default} />
</article>
<a href={`/${id - 1}`}>prev</a>
<br />
<a href={`/${id + 1}`}>next</a>
</div>
);
};
export default Pokemon;
Zugangsdaten und Konfiguration werden üblicherweise via Umgebungsvariablen bereitgestellt
Zugangsdaten sollten nicht unter Versionskontrolle stehen
verbreitete Möglichkeit, um Zugangsdaten bereit zu stellen: speichern in einer Datei namens .env, laden als Umgebungsvariablen mittels dotenv
Stelle sicher, dass .env nicht unter Versionskontrolle steht (füge es zur Datei .gitignore hinzu)
Beispiel für .env-Datei:
PORT=3000
NODE_ENV=production
DB_URL=mongodb+srv://...
AUTH0_DOMAIN=xxx.eu.auth0.com
laden in JavaScript:
import dotenv from 'dotenv';
dotenv.config();
const PORT = process.env.PORT;
const NODE_ENV = process.env.NODE_ENV;
Umgebungsvariable NODE_ENV
: spielt z.B. bei express eine wichtige Rolle
in Produktivumgebungen sollte immer NODE_ENV=production
gesetzt sein - ansonsten werden z.B. dem Endnutzer JavaScript-Fehlermeldungen im Detail angezeigt (mit Stack Traces)
{
"type": "module",
"eslintConfig": {
"sourceType": "module"
},
"scripts": {
"start": "node server.js"
}
}
installiere express, dotenv und eine Template Engine
Template Engines:
Beispiel für .gitignore:
.env
node_modules
Beispiel für .env:
PORT=3000
Laden der Konfiguration:
import dotenv from 'dotenv';
dotenv.config();
const PORT = process.env.PORT;
import express from 'express';
import expressReactViews from 'express-react-views';
const app = express();
app.engine('jsx', expressReactViews.createEngine());
app.set('view engine', 'jsx');
app.get('/', (req, res) => {
res.render('index', { name: 'world' });
});
app.listen(PORT);
// views/index.jsx
import React from 'react';
export default ({ name }) => {
return (
<html>
<head>
<title>Hello, {name}!</title>
</head>
<body>
<h1>Hello, {name}!</h1>
</body>
</html>
);
};
Online-Editoren:
"echtes" Hosting (kostenlose Optionen verfügbar):
siehe auch: codeinwp: 9 of the Best Node.js Hosting Platforms for 2021 (Free and Paid)
auf https://glitch.com, wähle "get started" - "hello express"
// server.js
const express = require('express');
const app = express();
app.use((req, res) => {
// note: we should actually return a complete HTML file
res.send('<h1>Hello World!</h1>\n');
});
app.listen(process.env.PORT);
registriere dich für einen Heroku-Account, wähle Node.js als "primary development language"
wähle create new app, wähle einen eindeutigen Namen und eine Server-Location
im "deploy"-Tab, wähle "connect to GitHub"
klicke Deploy Branch zum erstmaligen Deployment
aktiviere automatische Deployments, um bei jeder Änderung des Repositories automatisch zu deployen
setzen von Umgebungsvariablen zur Konfiguration unter "settings" - "config vars"
die Umgebungsvariable PORT
ist in Programmen automatisch verfügbar und muss hier nicht gesetzt werden
mehr Informationen: https://devcenter.heroku.com/articles/deploying-nodejs
Middleware kann in express und in connect verwendet werden
Middleware kann auf Requests reagieren / antworten sowie die req / res - Objekte abändern
Beispiel:
import compression from 'compression';
import express from 'express';
import morgan from 'morgan';
const app = express();
// log all incoming requests
app.use(morgan('common'));
// serve content of the "public" folder as-is if found
app.use(express.static('public'));
// compress all responses
app.use(compression());
req.body
zur Verfügungreq.cookies
zur Verfügungreq.session
)Standardmäßig sendet der Browser Formular-Inhalte im URL-encoded Format, z.B.:
foo=1&bar=2&baz=3
in get-Requests: als Teil der URL, z.B. https://google.com/search?ei=xyzg&q=foo...
in post-Requests: im Request-Body
in einem get-Request: lies req.query
in einem post-Request: verwende express.urlencoded
als Middleware, lies req.body
Ein Identity Provider kann die Identität eines Benutzers überprüfen (kann den Benutzer authentifizieren)
Beispiele:
der aktuelle Endnutzer ist auf dieser Domain als Benutzer "foo" eingeloggt
der aktuelle Endnutzer ist al Benutzer "x" bei Google / als Benutzer "y" bei Facebook authentifiziert
Mechanismus für den Benutzer:
Benutzer klickt auf login, wird zu einer Login-Seite weitergeleitet und nach erfolgreichem Login zur ursprünglichen Seite zurückgeleitet
im Hintergrund erhält der Benutzer ein Identity Token, einen kleinen Datensatz, der die Identität des Benutzers im Zusammenspiel mit dem Identity Provider belegen kann
das Identity Token wird üblicherweise als Cookie gesetzt (z.B. appSession
)
Standards:
Auth0 (auth-zero) is a widely-used identity provider
supports authentication via "internal" accounts or external identity providers (e.g. Google, Apple, Facebook, ...)
Application Settings:
Beispiel für Callback URLs:
http://localhost:3000/callback,
https://mydomain.com/callback
Beispiel für Logout URLs:
http://localhost:3000,
https://mydomain.com
Beispiel für .env
Datei für lokale Entwicklung (Quellen auf nächster Slide)
ISSUER_BASE_URL=https://dev-xxxxxxxx.eu.auth0.com
CLIENT_ID=jA0EAwMCxamDRMfOGV5gyZPnyX1BBPOQ
SECRET=7qHciKUpXk7pCXqG45bweRBQxBTMpztB
BASE_URL=http://localhost:3000
PORT=3000
unter Settings:
jeder Auth0-Klient hat zumindest eine domain (z.B. dev-xxxxxxxx.eu.auth0.com)
jede App hat eine bestimmte client ID (z.B. jA0EAwMCxamDRMfOGV5gyZPnyX1BBPOQ)
selbst erstellt:
secret: erstelle eine lange, zufällige Zeichenfolge (empfohlen: mindestens 32 Zeichen)
Guides:
Guide mit der aktuellsten Library (express-openid-connect): https://auth0.com/docs/quickstart/webapp/express
Guide mit älteren (verbreiteteren) Libraries: https://auth0.com/docs/quickstart/webapp/express
npm-Paket: express-openid-connect
Middleware für Authentifizierung
stellt automatisch die URLs /login, /logout, /callback bereit
app.use(
expressOpenidConnect.auth({
authRequired: false,
auth0Logout: true,
baseURL: process.env.BASE_URL,
clientID: process.env.CLIENT_ID,
issuerBaseURL: process.env.ISSUER_BASE_URL,
secret: process.env.SECRET,
})
);
app.get('/', (req, res) => {
if (req.oidc.isAuthenticated()) {
res.send(
`<p>logged in <a href="/logout">log out</a></p>
<pre>${JSON.stringify(req.oidc.user)}</pre>`
);
} else {
res.send(
'<div>not logged in <a href="/login">log in</a></div>'
);
}
});