Zoeken…


Invoering

Express is een minimaal en flexibel Node.js webtoepassingskader, dat een robuuste set functies biedt voor het bouwen van webtoepassingen.

De officiële website van Express is expressjs.com . De bron is te vinden op GitHub .

Syntaxis

  • app.get (pad [, middleware], callback [, callback ...])
  • app.put (pad [, middleware], callback [, callback ...])
  • app.post (pad [, middleware], callback [, callback ...])
  • app ['delete'] (pad [, middleware], callback [, callback ...])
  • app.use (pad [, middleware], callback [, callback ...])
  • app.use (callback)

parameters

Parameter Details
path Hiermee geeft u het padgedeelte of de URL op die door de opgegeven callback wordt verwerkt.
middleware Een of meer functies die vóór het terugbellen worden opgeroepen. In wezen een keten van meerdere callback functies. Handig voor meer specifieke afhandeling, bijvoorbeeld autorisatie of foutafhandeling.
callback Een functie die wordt gebruikt om aanvragen voor het opgegeven path te handelen. Het zal worden genoemd als callback(request, response, next) , waar request , response en next hieronder worden beschreven.
callback request Een object dat details bevat over het HTTP-verzoek dat de callback moet afhandelen.
response Een object dat wordt gebruikt om aan te geven hoe de server op het verzoek moet reageren.
next Een terugbelactie die de besturing doorgeeft aan de volgende overeenkomende route. Het accepteert een optioneel foutobject.

Ermee beginnen

U moet eerst een map maken, deze in uw shell openen en Express installeren met behulp van npm door npm install express --save

Maak een bestand en noem het app.js en voeg de volgende code toe die een nieuwe Express-server maakt en er één eindpunt aan toevoegt ( /ping ) met de methode app.get :

const express = require('express');

const app = express();

app.get('/ping', (request, response) => {
    response.send('pong');
});

app.listen(8080, 'localhost');

Om uw script uit te voeren, gebruikt u de volgende opdracht in uw shell:

> node app.js

Uw toepassing accepteert verbindingen op localhost-poort 8080. Als het hostname-argument voor app.listen wordt weggelaten, accepteert de server zowel verbindingen op het IP-adres van de machine als op localhost. Als de poortwaarde 0 is, wijst het besturingssysteem een beschikbare poort toe.

Zodra uw script actief is, kunt u het in een shell testen om te bevestigen dat u de verwachte reactie "pong" van de server krijgt:

> curl http://localhost:8080/ping
pong

U kunt ook een webbrowser openen, navigeren naar de URL http: // localhost: 8080 / ping om de uitvoer te bekijken

Basis routing

Maak eerst een express-app:

const express = require('express');
const app = express();

Vervolgens kunt u routes als volgt definiëren:

app.get('/someUri', function (req, res, next) {})

Die structuur werkt voor alle HTTP-methoden en verwacht een pad als het eerste argument, en een handler voor dat pad, die de aanvraag- en reactieobjecten ontvangt. Dus voor de basis HTTP-methoden zijn dit de routes

// GET www.domain.com/myPath
app.get('/myPath', function (req, res, next) {})

// POST www.domain.com/myPath
app.post('/myPath', function (req, res, next) {})

// PUT www.domain.com/myPath
app.put('/myPath', function (req, res, next) {})

// DELETE www.domain.com/myPath
app.delete('/myPath', function (req, res, next) {})

U kunt de volledige lijst van ondersteunde werkwoorden controleren hier . Als u hetzelfde gedrag voor een route en alle HTTP-methoden wilt definiëren, kunt u het volgende gebruiken:

app.all('/myPath', function (req, res, next) {}) 

of

app.use('/myPath', function (req, res, next) {})

of

app.use('*', function (req, res, next) {})

// * wildcard will route for all paths

U kunt uw routedefinities voor één pad koppelen

app.route('/myPath')
  .get(function (req, res, next) {})
  .post(function (req, res, next) {})
  .put(function (req, res, next) {})

U kunt ook functies toevoegen aan elke HTTP-methode. Ze worden uitgevoerd vóór de laatste callback en nemen de parameters (req, res, next) als argumenten.

// GET www.domain.com/myPath
app.get('/myPath', myFunction, function (req, res, next) {})

Uw laatste callbacks kunnen worden opgeslagen in een extern bestand om te voorkomen dat u teveel code in één bestand plaatst:

// other.js
exports.doSomething = function(req, res, next) {/* do some stuff */};

En vervolgens in het bestand met uw routes:

const other = require('./other.js');
app.get('/someUri', myFunction, other.doSomething);

Dit maakt je code veel schoner.

Informatie krijgen van het verzoek

Om informatie te krijgen van de aanvragende url (merk op dat req het aanvraagobject is in de handlerfunctie van routes). Beschouw deze /settings/32135?field=name /settings/:user_id en dit specifieke voorbeeld /settings/32135?field=name

// get the full path
req.originalUrl // => /settings/32135?field=name

// get the user_id param
req.params.user_id // => 32135     

// get the query value of the field
req.query.field // => 'name'

U kunt ook koppen van het verzoek krijgen, zoals deze

req.get('Content-Type')
// "text/plain"

Om het verkrijgen van andere informatie te vereenvoudigen, kunt u middlewares gebruiken. Om bijvoorbeeld de body-info van het verzoek te krijgen, kunt u de body-parser middleware gebruiken, die de ruwe body van het verzoek in een bruikbaar formaat zal transformeren.

var app = require('express')();
var bodyParser = require('body-parser');

app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded

Stel nu een verzoek als dit

PUT /settings/32135
{
  "name": "Peter"
}

Je hebt op deze manier toegang tot de geposte naam

req.body.name
// "Peter"

Op dezelfde manier hebt u vanaf het verzoek toegang tot cookies, u hebt ook een middleware zoals cookie-parser nodig

req.cookies.name

Modulaire uitdrukkelijke toepassing

Om express webapplicaties modulair te gebruiken, gebruik je routerfabrieken:

module:

// greet.js
const express = require('express');

module.exports = function(options = {}) { // Router factory
    const router = express.Router();

    router.get('/greet', (req, res, next) => {
        res.end(options.greeting);
    });

    return router;
};

Toepassing:

// app.js
const express = require('express');
const greetMiddleware = require('./greet.js');

express()
    .use('/api/v1/', greetMiddleware({ greeting:'Hello world' }))
    .listen(8080);

Dit maakt uw applicatie modulair, aanpasbaar en uw code herbruikbaar.

Bij toegang tot http://<hostname>:8080/api/v1/greet de uitvoer Hello world

Meer gecompliceerd voorbeeld

Voorbeeld met services die de voordelen van de middleware-fabriek laten zien.

module:

// greet.js
const express = require('express');

module.exports = function(options = {}) { // Router factory
    const router = express.Router();
    // Get controller
    const {service} = options;

    router.get('/greet', (req, res, next) => {
        res.end(
            service.createGreeting(req.query.name || 'Stranger')
        );
    });

    return router;
};

Toepassing:

// app.js
const express = require('express');
const greetMiddleware = require('./greet.js');

class GreetingService {
    constructor(greeting = 'Hello') {
        this.greeting = greeting;
    }

    createGreeting(name) {
        return `${this.greeting}, ${name}!`;
    }
}

express()
    .use('/api/v1/service1', greetMiddleware({
        service: new GreetingService('Hello'),
    }))
    .use('/api/v1/service2', greetMiddleware({
        service: new GreetingService('Hi'),
    }))
    .listen(8080);

Bij toegang tot http://<hostname>:8080/api/v1/service1/greet?name=World de uitvoer Hello, World en toegang tot http://<hostname>:8080/api/v1/service2/greet?name=World de uitvoer is Hi, World .

Een Template Engine gebruiken

Een Template Engine gebruiken

De volgende code zal Jade instellen als sjabloon-engine. (Opmerking: Jade is vanaf december 2015 hernoemd in pug .)

const express = require('express');  //Imports the express module
const app = express();  //Creates an instance of the express module

const PORT = 3000; //Randomly chosen port

app.set('view engine','jade'); //Sets jade as the View Engine / Template Engine
app.set('views','src/views'); //Sets the directory where all the views (.jade files) are stored.

//Creates a Root Route
app.get('/',function(req, res){
    res.render('index');  //renders the index.jade file into html and returns as a response. The render function optionally takes the data to pass to the view.
});

//Starts the Express server with a callback
app.listen(PORT, function(err) {
    if (!err) {
        console.log('Server is running at port', PORT);
    } else {
        console.log(JSON.stringify(err));
    }
});

Evenzo zouden andere sjabloon motoren ook worden gebruikt zoals Handlebars ( hbs ) of ejs . Vergeet niet npm install de Template Engine te npm install . Voor sturen gebruiken we het hbs pakket, voor Jade hebben we een jade pakket en voor EJS hebben we een ejs pakket.

EJS-sjabloonvoorbeeld

Met EJS (net als andere express-sjablonen) kunt u servercode uitvoeren en toegang krijgen tot uw servervariabelen vanuit uw HTML.
In EJS wordt het gedaan met " <% " als start-tag en " %> " als eind-tag, variabelen worden doorgegeven terwijl de render-params toegankelijk zijn met <%=var_name%>
Als u bijvoorbeeld een array met benodigdheden in uw servercode hebt
je kunt er een lus over gebruiken met

<h1><%= title %></h1>
   <ul>
<% for(var i=0; i<supplies.length; i++) { %>
    <li>
        <a href='supplies/<%= supplies[i] %>'>
            <%= supplies[i] %>
        </a>
    </li>
<% } %>

Zoals je in het voorbeeld kunt zien, moet je elke keer dat je schakelt tussen server side code en HTML de huidige EJS-tag sluiten en later een nieuwe openen, hier wilden we li in de opdracht for dus moesten we onze EJS-tag sluiten aan het einde van de for en maak een nieuwe tag alleen voor de accolades
een ander voorbeeld
als we input standaardversie als een variabele van de server willen instellen, gebruiken we <%=
bijvoorbeeld:

 Message:<br>
<input type="text" value="<%= message %>" name="message" required>

Hier is de berichtvariabele die wordt doorgegeven vanaf uw serverzijde de standaardwaarde van uw invoer. Houd er rekening mee dat als u de berichtvariabele niet hebt doorgegeven vanaf uw serverzijde, EJS een uitzondering genereert. U kunt parameters doorgeven met behulp van res.render('index', {message: message}); (voor ejs-bestand index.ejs).

In de EJS-tags kunt u ook if , while of een andere javascript-opdracht gebruiken.

JSON API met ExpressJS

var express = require('express');
var cors = require('cors'); // Use cors module for enable Cross-origin resource sharing

var app = express();
app.use(cors()); // for all routes

var port = process.env.PORT || 8080;

app.get('/', function(req, res) {
    var info = {
        'string_value': 'StackOverflow',
        'number_value': 8476
    }
    res.json(info);

    // or
    /* res.send(JSON.stringify({
        string_value: 'StackOverflow',
        number_value: 8476
    })) */

  //you can add a status code to the json response
   /* res.status(200).json(info) */
})

app.listen(port, function() {
    console.log('Node.js listening on port ' + port)
})

Op http://localhost:8080/ uitvoerobject

{
    string_value: "StackOverflow",
    number_value: 8476
}

Presenteren van statische bestanden

Bij het bouwen van een webserver met Express is het vaak nodig om een combinatie van dynamische inhoud en statische bestanden weer te geven.

Het is bijvoorbeeld mogelijk dat index.html en script.js statische bestanden in het bestandssysteem zijn.

Het is gebruikelijk om de map 'public' te gebruiken om statische bestanden te hebben. In dit geval kan de mappenstructuur er als volgt uitzien:

project root
├── server.js
├── package.json 
└── public
    ├── index.html
    └── script.js

Zo configureert u Express om statische bestanden weer te geven:

const express = require('express');
const app = express();

app.use(express.static('public'));

Opmerking: zodra de map is geconfigureerd, zijn index.html, script.js en alle bestanden in de map "public" beschikbaar in het rootpad (u moet niet /public/ opgeven in de url). Dit komt omdat Express zoekt naar de bestanden met betrekking tot de geconfigureerde statische map. U kunt een virtueel padvoorvoegsel opgeven zoals hieronder wordt weergegeven:

app.use('/static', express.static('public'));

stelt de bronnen beschikbaar onder het /static/ prefix.

Meerdere mappen

Het is mogelijk om meerdere mappen tegelijkertijd te definiëren:

app.use(express.static('public'));
app.use(express.static('images'));
app.use(express.static('files'));

Bij het bedienen van de middelen zal Express de map in de volgorde van de definitie onderzoeken. In het geval van bestanden met dezelfde naam, wordt die in de eerste overeenkomende map weergegeven.

Genoemde routes in Django-stijl

Een groot probleem is dat waardevolle benoemde routes niet direct door Express worden ondersteund. Oplossing is het installeren van een ondersteund pakket van derden, bijvoorbeeld express-reverse :

npm install express-reverse

Sluit het aan op uw project:

var app = require('express')();
require('express-reverse')(app);

Gebruik het dan als:

app.get('test', '/hello', function(req, res) {
  res.end('hello');
});

Het nadeel van deze aanpak is dat u de route Express-module niet kunt gebruiken zoals weergegeven in Geavanceerd routergebruik . De oplossing is om uw app als parameter door te geven aan uw routerfabriek:

require('./middlewares/routing')(app);

En gebruik het als:

module.exports = (app) => {
    app.get('test', '/hello', function(req, res) {
      res.end('hello');
    });
};

Je kunt er vanaf nu achter komen hoe je functies definieert om het samen te voegen met opgegeven aangepaste naamruimten en naar de juiste controllers te wijzen.

Foutafhandeling

Basisfoutafhandeling

Standaard zoekt Express naar een weergave 'fout' in de map /views om te renderen. Maak eenvoudig de weergave 'Fout' en plaats deze in de map 'Weergaven' om fouten af te handelen. Fouten worden geschreven met het foutbericht, de status en de stacktracering, bijvoorbeeld:

views / error.pug

html
  body
      h1= message
      h2= error.status
      p= error.stack

Geavanceerde foutafhandeling

Definieer uw foutafhandeling middleware-functies helemaal aan het einde van de stapel middleware-functies. Deze hebben bijvoorbeeld vier argumenten in plaats van drie (err, req, res, next) :

app.js

// catch 404 and forward to error handler
app.use(function(req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;

    //pass error to the next matching route.
    next(err);
});

// handle error, print stacktrace
app.use(function(err, req, res, next) {
    res.status(err.status || 500);

    res.render('error', {
        message: err.message,
        error: err
    });
});

U kunt verschillende foutafhandelingsmiddleware-functies definiëren, net als bij normale middleware-functies.

Middleware en de volgende callback gebruiken

Express geeft een next callback door aan elke routehandler en middleware-functie die kan worden gebruikt om logica voor afzonderlijke routes over meerdere handlers te doorbreken. Het aanroepen van next() zonder argumenten vertelt express om door te gaan naar de volgende overeenkomende middleware of route-handler. Als u next(err) belt met een fout, wordt elke middleware voor de fouthandler geactiveerd. Als je next('route') aanroept, wordt elke volgende middleware op de huidige route omzeild en spring je naar de volgende overeenkomende route. Hierdoor kan domeinlogica worden ontkoppeld in herbruikbare componenten die op zichzelf staan, eenvoudiger te testen zijn en gemakkelijker te onderhouden en te wijzigen zijn.

Meerdere bijpassende routes

Verzoeken aan /api/foo of /api/bar zullen de initiële handler uitvoeren om het lid op te zoeken en vervolgens de besturing doorgeven aan de feitelijke handler voor elke route.

app.get('/api', function(req, res, next) {
  // Both /api/foo and /api/bar will run this
  lookupMember(function(err, member) {
    if (err) return next(err);
    req.member = member;
    next();
  });
});

app.get('/api/foo', function(req, res, next) {
  // Only /api/foo will run this
  doSomethingWithMember(req.member);
});

app.get('/api/bar', function(req, res, next) {
  // Only /api/bar will run this
  doSomethingDifferentWithMember(req.member);
});

Foutafhandeling

Foutafhandelaars zijn middleware met de handtekeningfunctie function(err, req, res, next) . Ze kunnen per route worden ingesteld (bijv. app.get('/foo', function(err, req, res, next) ) maar meestal volstaat een enkele foutafhandelaar die een foutpagina weergeeft.

app.get('/foo', function(req, res, next) {
  doSomethingAsync(function(err, data) {
    if (err) return next(err);
    renderPage(data);
  });
});

// In the case that doSomethingAsync return an error, this special
// error handler middleware will be called with the error as the 
// first parameter.
app.use(function(err, req, res, next) {
  renderErrorPage(err);
});

middleware

Elk van de bovenstaande functies is eigenlijk een middleware-functie die wordt uitgevoerd wanneer een aanvraag overeenkomt met de gedefinieerde route, maar een willekeurig aantal middleware-functies kan op een enkele route worden gedefinieerd. Hierdoor kan middleware in afzonderlijke bestanden worden gedefinieerd en kan gemeenschappelijke logica op meerdere routes worden hergebruikt.

app.get('/bananas', function(req, res, next) {
  getMember(function(err, member) {
    if (err) return next(err);
    // If there's no member, don't try to look
    // up data. Just go render the page now.
    if (!member) return next('route');
    // Otherwise, call the next middleware and fetch
    // the member's data.
    req.member = member;
    next();
  });
}, function(req, res, next) {
  getMemberData(req.member, function(err, data) {
    if (err) return next(err);
    // If this member has no data, don't bother
    // parsing it. Just go render the page now.
    if (!data) return next('route');
    // Otherwise, call the next middleware and parse
    // the member's data. THEN render the page.
    req.member.data = data;
    next();
  });
}, function(req, res, next) {
  req.member.parsedData = parseMemberData(req.member.data);
  next();
});

app.get('/bananas', function(req, res, next) {
  renderBananas(req.member);
});

In dit voorbeeld bevindt elke middleware-functie zich in een eigen bestand of in een variabele elders in het bestand, zodat deze op andere routes kan worden hergebruikt.

Foutafhandeling

Basisdocumenten zijn hier te vinden

app.get('/path/:id(\\d+)', function (req, res, next) { // please note: "next" is passed
    if (req.params.id == 0) // validate param
        return next(new Error('Id is 0')); // go to first Error handler, see below

    // Catch error on sync operation
    var data;
    try {
        data = JSON.parse('/file.json');
    } catch (err) {
        return next(err);
    }

    // If some critical error then stop application
    if (!data)
        throw new Error('Smth wrong');

    // If you need send extra info to Error handler
    // then send custom error (see Appendix B)
    if (smth)
        next(new MyError('smth wrong', arg1, arg2))

    // Finish request by res.render or res.end
    res.status(200).end('OK');
});    

// Be sure: order of app.use have matter
// Error handler
app.use(function(err, req, res, next)) {
    if (smth-check, e.g. req.url != 'POST') 
        return next(err); // go-to Error handler 2.

    console.log(req.url, err.message);

    if (req.xhr) // if req via ajax then send json else render error-page
        res.json(err);
    else 
        res.render('error.html', {error: err.message});  
});

// Error handler 2
app.use(function(err, req, res, next)) {
    // do smth here e.g. check that error is MyError
    if (err instanceof MyError) {
        console.log(err.message, err.arg1, err.arg2);
    }     
    ...
    res.end();
});

Bijlage A

// "In Express, 404 responses are not the result of an error, 
// so the error-handler middleware will not capture them." 
// You can change it.
app.use(function(req, res, next) {
    next(new Error(404)); 
});

Bijlage B

// How to define custom error
var util = require('util');
...
function MyError(message, arg1, arg2) {
    this.message = message;
    this.arg1 = arg1;
    this.arg2 = arg2;
    Error.captureStackTrace(this, MyError);
}
util.inherits(MyError, Error);
MyError.prototype.name = 'MyError';

Hook: Hoe code uit te voeren vóór elke req en na een res

app.use() en middleware kunnen worden gebruikt voor "voor" en een combinatie van de nauwe en afwerking gebeurtenissen kunnen worden gebruikt voor "na".

app.use(function (req, res, next) {
    function afterResponse() {
        res.removeListener('finish', afterResponse);
        res.removeListener('close', afterResponse);

        // actions after response
    }
    res.on('finish', afterResponse);
    res.on('close', afterResponse);

    // action before request
    // eventually calling `next()`
    next();
});
...
app.use(app.router);

Een voorbeeld hiervan is de logger- middleware, die standaard aan het logboek wordt toegevoegd na de reactie.

Zorg er alleen voor dat deze "middleware" wordt gebruikt vóór app.router want de volgorde doet er toe.


Origineel bericht is hier

POST-aanvragen afhandelen

Net zoals u verzoeken om ontvangen in Express met de app.get-methode afhandelt, kunt u de app.post-methode gebruiken om postverzoeken af te handelen.

Maar voordat u POST-aanvragen kunt afhandelen, moet u de body-parser middleware gebruiken. Het ontleedt eenvoudigweg de inhoud van POST , PUT , DELETE en andere verzoeken.

Body-Parser middleware ontleedt de body van het verzoek en maakt er een object van dat beschikbaar is in req.body

var bodyParser = require('body-parser');

const express = require('express');

const app = express();

// Parses the body for POST, PUT, DELETE, etc.
app.use(bodyParser.json());

app.use(bodyParser.urlencoded({ extended: true }));

app.post('/post-data-here', function(req, res, next){

    console.log(req.body); // req.body contains the parsed body of the request.

});

app.listen(8080, 'localhost');

Het volgende is een voorbeeld voor het instellen en lezen van cookies met behulp van de cookie-parsermodule :

var express = require('express');
var cookieParser = require('cookie-parser'); // module for parsing cookies
var app = express();
app.use(cookieParser());

app.get('/setcookie', function(req, res){
    // setting cookies
    res.cookie('username', 'john doe', { maxAge: 900000, httpOnly: true });
    return res.send('Cookie has been set');
});

app.get('/getcookie', function(req, res) {
    var username = req.cookies['username'];
    if (username) {
        return res.send(username);        
    }

    return res.send('No cookie found');
});

app.listen(3000);

Aangepaste middleware in Express

In Express kunt u middlewares definiëren die kunnen worden gebruikt voor het controleren van aanvragen of als reactie hierop enkele headers.

app.use(function(req, res, next){ });    // signature

Voorbeeld

De volgende code voegt de user aan het aanvraagobject en geeft het besturingselement door aan de volgende overeenkomende route.

var express = require('express');
var app = express();

//each request will pass through it
app.use(function(req, res, next){
    req.user = 'testuser';
    next();    // it will pass the control to next matching route
});

app.get('/', function(req, res){
    var user = req.user;
    console.log(user); // testuser
    return res.send(user);
});

app.listen(3000);

Foutafhandeling in Express

In Express kunt u een uniforme foutafhandelingsfunctie definiëren voor fouten in de toepassing. Definieer vervolgens handler aan het einde van alle routes en logische code.

Voorbeeld

var express = require('express');
var app = express();

//GET /names/john
app.get('/names/:name', function(req, res, next){
    if (req.params.name == 'john'){
        return res.send('Valid Name');
    } else{
        next(new Error('Not valid name'));    //pass to error handler
    }
});

//error handler
app.use(function(err, req, res, next){
    console.log(err.stack);    // e.g., Not valid name
    return res.status(500).send('Internal Server Occured');
});

app.listen(3000);

Middleware toevoegen

Middleware-functies zijn functies die toegang hebben tot het aanvraagobject (req), het responsobject (res) en de volgende middleware-functie in de aanvraag-responscyclus van de toepassing.

Middleware functies kan een code uit te voeren, wijzigingen aanbrengen in res en req voorwerpen, end respons cyclus en bel de volgende middleware.

Veel voorkomend voorbeeld van middleware is de cors module. Om CORS-ondersteuning toe te voegen, installeert u deze, vereist u deze en zet u deze regel:

app.use(cors());

vóór routers of routeringsfuncties.

Hallo Wereld

Hier maken we een eenvoudige hallo wereld-server met behulp van Express. routes:

  • '/'
  • '/ Wiki'

En voor de rest geeft "404", dat wil zeggen pagina niet gevonden.

'use strict';

const port = process.env.PORT || 3000;

var app = require('express')();
    app.listen(port);

app.get('/',(req,res)=>res.send('HelloWorld!'));
app.get('/wiki',(req,res)=>res.send('This is wiki page.'));
app.use((req,res)=>res.send('404-PageNotFound'));

Opmerking: We hebben 404-route als de laatste route gezet, aangezien Express routes in volgorde stapelt en deze voor elke aanvraag opeenvolgend verwerkt.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow