Scrittura middleware per l'utilizzo in applicazioni Express
Le funzioni Middleware sono funzioni che hanno accesso al request object (req), il response object (res), e la funzione next nel ciclo request-response dell’applicazione. La funzione next è una funzione nel router Express che, quando invocato, esegue il middleware con successo al middleware corrente.
Le funzioni Middleware possono eseguire le seguenti attività:
- Esegue qualsiasi codice.
- Effettuare modifiche alla richiesta e agli oggetti di risposta.
- Terminare il ciclo richiesta-risposta.
- Chiama il prossimo middleware nello stack.
Se la funzione middleware corrente non termina il ciclo richiesta-risposta, deve chiamare next() per passare il controllo alla successiva funzione middleware. In caso contrario, la richiesta sarà sospesa.
La figura seguente mostra gli elementi di una chiamata di una funzione middleware:

A partire da Express 5, le funzioni middleware che restituiscono una Promise chiameranno next(value) quando rifiutano o lanciano un errore. next verrà chiamato con il valore rifiutato o con l’errore lanciato.
Esempio
Ecco un esempio di una semplice applicazione “Hello World” Express.
Il resto di questo articolo definirà e aggiungerà tre funzioni middleware all’applicazione:
uno chiamato myLogger che stampa un semplice messaggio di registro, uno chiamato requestTime che
visualizza il timestamp della richiesta HTTP e uno chiamato validateCookies che convalida i cookie in entrata.
const express = require('express');const app = express();
app.get('/', (req, res) => { res.send('Hello World!');});
app.listen(3000);import express from 'express';
const app = express();
app.get('/', (req, res) => { res.send('Hello World!');});
app.listen(3000);import express, { type Express, type Request, type Response } from 'express';
const app: Express = express();
app.get('/', (req: Request, res: Response) => { res.send('Hello World!');});
app.listen(3000);Funzione middleware myLogger
Ecco un semplice esempio di una funzione middleware chiamata “myLogger”. Questa funzione solo stampa
“LOGGED” quando una richiesta all’app passa attraverso di essa. La funzione middleware è assegnata a una variabile
chiamata myLogger.
const myLogger = function (req, res, next) { console.log('LOGGED'); next();};import { type Request, type Response, type NextFunction } from 'express';
const myLogger = function (req: Request, res: Response, next: NextFunction) { console.log('LOGGED'); next();};Here myLogger is defined on its own rather than passed directly to app.use(), so TypeScript has
no context to infer its parameters and they are annotated explicitly. You can instead type the whole
function as RequestHandler, which types req, res, and next for you. When the middleware is
written inline in the app.use() call, Express infers those three parameters and no annotations are
needed.
Notare la chiamata sopra a next(). Chiamando questa funzione invoca la prossima funzione middleware nell’app
. La funzione next() non è una parte del Node. s o Express API, ma è il terzo argomento
che viene passato alla funzione middleware. La funzione next() potrebbe essere chiamata qualsiasi cosa,
ma per convenzione è sempre chiamata “next”. Per evitare confusione, utilizzare sempre questa convenzione.
Per caricare la funzione middleware, chiama app.use(), specificando la funzione middleware.
Ad esempio, il seguente codice carica la funzione middleware myLogger prima del percorso al percorso root (/).
const express = require('express');const app = express();
const myLogger = function (req, res, next) { console.log('LOGGED'); next();};
app.use(myLogger);
app.get('/', (req, res) => { res.send('Hello World!');});
app.listen(3000);import express from 'express';
const app = express();
const myLogger = function (req, res, next) { console.log('LOGGED'); next();};
app.use(myLogger);
app.get('/', (req, res) => { res.send('Hello World!');});
app.listen(3000);import express, { type Express, type Request, type Response, type NextFunction } from 'express';
const app: Express = express();
const myLogger = function (req: Request, res: Response, next: NextFunction) { console.log('LOGGED'); next();};
app.use(myLogger);
app.get('/', (req: Request, res: Response) => { res.send('Hello World!');});
app.listen(3000);Ogni volta che l’app riceve una richiesta, stampa il messaggio “LOGGED” al terminale.
L’ordine di caricamento del middleware è importante: le funzioni middleware che vengono caricate per primo vengono eseguite per prime.
Se il file myLogger viene caricato dopo il percorso verso il percorso root, la richiesta non lo raggiunge mai e l’applicazione non stampa “LOGGED”, perché il gestore del percorso del percorso radice termina il ciclo richiesta-risposta.
La funzione middleware myLogger semplicemente stampa un messaggio, poi passa la richiesta alla funzione middleware successiva nello stack chiamando la funzione next().
Richiesta funzione Middleware
Successivamente, creeremo una funzione middleware chiamata “requestTime” e aggiungeremo una proprietà chiamata requestTime
all’oggetto richiesta.
Because the middleware adds a property to req, extend the request type in TypeScript with
declaration merging. Declare
the property (as optional, since a middleware may not run for every request) in a .d.ts file that
is part of your project:
declare global { namespace Express { interface Request { requestTime?: number; } }}
export {};TypeScript picks up the file automatically, so no tsconfig.json change is needed unless you have
set a custom include that does not cover its location. See
Extending the API in TypeScript for
adding other custom properties and methods.
const requestTime = function (req, res, next) { req.requestTime = Date.now(); next();};import { type Request, type Response, type NextFunction } from 'express';
const requestTime = function (req: Request, res: Response, next: NextFunction) { req.requestTime = Date.now(); next();};L’app utilizza ora la funzione middleware requestTime. Inoltre, la funzione callback del percorso del percorso del percorso di root utilizza la proprietà che la funzione middleware aggiunge a req (il oggetto richiesta).
const express = require('express');const app = express();
const requestTime = function (req, res, next) { req.requestTime = Date.now(); next();};
app.use(requestTime);
app.get('/', (req, res) => { let responseText = 'Hello World!<br>'; responseText += `<small>Requested at: ${req.requestTime}</small>`; res.send(responseText);});
app.listen(3000);import express from 'express';
const app = express();
const requestTime = function (req, res, next) { req.requestTime = Date.now(); next();};
app.use(requestTime);
app.get('/', (req, res) => { let responseText = 'Hello World!<br>'; responseText += `<small>Requested at: ${req.requestTime}</small>`; res.send(responseText);});
app.listen(3000);import express, { type Express, type Request, type Response, type NextFunction } from 'express';
const app: Express = express();
const requestTime = function (req: Request, res: Response, next: NextFunction) { req.requestTime = Date.now(); next();};
app.use(requestTime);
app.get('/', (req: Request, res: Response) => { let responseText = 'Hello World!<br>'; responseText += `<small>Requested at: ${req.requestTime}</small>`; res.send(responseText);});
app.listen(3000);Quando si effettua una richiesta alla root dell’app, l’app ora visualizza il timestamp della richiesta nel browser.
Middleware function validateCookies
Infine, creeremo una funzione middleware che convalida i cookie in arrivo e invia una risposta 400 se i cookie non sono validi.
Ecco una funzione di esempio che convalida i cookie con un servizio asincronico esterno.
async function cookieValidator(cookies) { try { await externallyValidateCookie(cookies.testCookie); } catch { throw new Error('Invalid cookies'); }}async function cookieValidator(cookies: Record<string, string>) { try { await externallyValidateCookie(cookies.testCookie); } catch { throw new Error('Invalid cookies'); }}Il middleware validateCookies restituisce una Promessa che al momento del rifiuto attiverà automaticamente il nostro gestore di errori. Qui utilizziamo il middleware cookie-parser per analizzare i cookie in arrivo fuori dall’oggetto req e passarli alla nostra funzione cookieValidator.
const express = require('express');const cookieParser = require('cookie-parser');const cookieValidator = require('./cookieValidator');
const app = express();
async function validateCookies(req, res, next) { await cookieValidator(req.cookies); next();}
app.use(cookieParser());
app.use(validateCookies);
// error handlerapp.use((err, req, res, next) => { res.status(400).send(err.message);});
app.listen(3000);import express from 'express';import cookieParser from 'cookie-parser';import cookieValidator from './cookieValidator';
const app = express();
async function validateCookies(req, res, next) { await cookieValidator(req.cookies); next();}
app.use(cookieParser());
app.use(validateCookies);
// error handlerapp.use((err, req, res, next) => { res.status(400).send(err.message);});
app.listen(3000);import express, { type Express, type Request, type Response, type NextFunction } from 'express';import cookieParser from 'cookie-parser';import cookieValidator from './cookieValidator';
const app: Express = express();
async function validateCookies(req: Request, res: Response, next: NextFunction) { await cookieValidator(req.cookies); next();}
app.use(cookieParser());
app.use(validateCookies);
// error handlerapp.use((err: Error, req: Request, res: Response, next: NextFunction) => { res.status(400).send(err.message);});
app.listen(3000);Nota come next() viene chiamato dopo await cookieValidator(req.cookies). Questo assicura che se
cookieValidator si risolve, il prossimo middleware nello stack verrà chiamato. Se passi qualcosa
alla funzione next() (tranne la stringa 'route' o 'router'), Espresso considera l’attuale richiesta
come un errore e salterà tutte le rimanenti funzioni di routing e middleware
.
Poiché hai accesso all’oggetto richiesta, all’oggetto risposta, alla funzione middleware successiva nello stack, e all’intero Node. s API, le possibilità con funzioni middleware sono infinite.
Per ulteriori informazioni su middleware Express, consultare: Using Express middleware.
Middleware configurabile
Se hai bisogno del tuo middleware per essere configurabile, esporta una funzione che accetta un oggetto di opzioni o altri parametri, che, poi, restituisce l’implementazione middleware in base ai parametri di input.
module.exports = function (options) { return function (req, res, next) { // Implement the middleware function based on the options object next(); };};export default function (options) { return function (req, res, next) { // Implement the middleware function based on the options object next(); };}import { type Request, type Response, type NextFunction } from 'express';
export default function (options: Record<string, unknown>) { return function (req: Request, res: Response, next: NextFunction) { // Implement the middleware function based on the options object next(); };}Il middleware può ora essere utilizzato come mostrato di seguito.
const mw = require('./my-middleware.cjs');
app.use(mw({ option1: '1', option2: '2' }));import mw from './my-middleware.mjs';
app.use(mw({ option1: '1', option2: '2' }));Fare riferimento a cookie-session e compression per esempi di middleware configurabile.