Middleware
Un middleware est une fonction qui est appelée avant le handler de route. Les fonctions intermédiaires ont accès aux objets request et response, ainsi qu'à la fonction intermédiaire next()
dans le cycle requête-réponse de l'application. La fonction next de l'intergiciel est généralement désignée par une variable nommée next
.

Les middlewares Nest sont, par défaut, équivalents aux middlewares express. La description suivante, tirée de la documentation officielle d'express, décrit les capacités des middlewares :
Les fonctions middleware peuvent effectuer les tâches suivantes :
- exécuter n'importe quel code.
- apporter des modifications aux objets "requête" et "réponse".
- mettre fin au cycle requête-réponse.
- appeler la fonction middleware suivante dans la pile.
- si la fonction middleware actuelle ne met pas fin au cycle requête-réponse, elle doit appeler
next()
pour passer le contrôle à la fonction middleware suivante. Dans le cas contraire, la requête sera laissée en suspens.
Vous implémentez un middleware Nest personnalisé soit dans une fonction, soit dans une classe avec un décorateur @Injectable()
. La classe doit implémenter l'interface NestMiddleware
, tandis que la fonction n'a pas d'exigences particulières. Commençons par implémenter un middleware simple en utilisant une classe.
AttentionExpress
etfastify
gèrent les middlewares différemment et fournissent des signatures de méthodes différentes, apprenez-en plus ici.
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log('Request...');
next();
}
}
import { Injectable } from '@nestjs/common';
@Injectable()
export class LoggerMiddleware {
use(req, res, next) {
console.log('Request...');
next();
}
}
Injection de dépendances#
Le middleware Nest prend entièrement en charge l'injection de dépendances. Tout comme les fournisseurs et les contrôleurs, ils sont capables d'injecter des dépendances qui sont disponibles dans le même module. Comme d'habitude, cela se fait à travers le constructeur
.
Appliquer le middleware#
Il n'y a pas de place pour les middlewares dans le décorateur @Module()
. A la place, nous les configurons en utilisant la méthode configure()
de la classe du module. Les modules qui incluent un middleware doivent implémenter l'interface NestModule
. Configurons l'intergiciel LoggerMiddleware
au niveau de AppModule
.
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes('cats');
}
}
import { Module } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
})
export class AppModule {
configure(consumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes('cats');
}
}
Dans l'exemple ci-dessus, nous avons configuré le LoggerMiddleware
pour les gestionnaires de routes /cats
qui ont été précédemment définis dans le CatsController
. Nous pouvons également restreindre un middleware à une méthode de requête particulière en passant un objet contenant la route path
et la requête method
à la méthode forRoutes()
lors de la configuration du middleware. Dans l'exemple ci-dessous, nous avons importé l'enum RequestMethod
pour référencer le type de méthode de requête désiré.
import { Module, NestModule, RequestMethod, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes({ path: 'cats', method: RequestMethod.GET });
}
}
import { Module, RequestMethod } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
})
export class AppModule {
configure(consumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes({ path: 'cats', method: RequestMethod.GET });
}
}
Astuce La méthodeconfigure()
peut être rendue asynchrone en utilisantasync/await
(par exemple, vous pouvezawait
la fin d'une opération asynchrone dans le corps de la méthodeconfigure()
).
Attention Lorsque vous utilisez l'adaptateurexpress
, l'application NestJS va enregistrerjson
eturlencoded
à partir du paquetbody-parser
par défaut. Cela signifie que si vous voulez personnaliser ce middleware via leMiddlewareConsumer
, vous devez désactiver le middleware global en mettant le drapeaubodyParser
àfalse
lors de la création de l'application avecNestFactory.create()
.
Jokers de route#
Les routes basées sur des motifs sont également supportées dans le middleware NestJS. Par exemple, le joker nommé (*splat
) peut être utilisé comme joker pour correspondre à n'importe quelle combinaison de caractères dans une route. Dans l'exemple suivant, l'intergiciel sera exécuté pour toute route commençant par abcd/
, quel que soit le nombre de caractères qui suivent.
forRoutes({
path: 'abcd/*splat',
method: RequestMethod.ALL,
});
Astucesplat
est simplement le nom du paramètre joker et n'a pas de signification particulière. Vous pouvez le nommer comme vous le souhaitez, par exemple,*wildcard
.
Le chemin d'accès 'abcd/*'
correspondra à abcd/1
, abcd/123
, abcd/abc
, et ainsi de suite. Le trait d'union ( -
) et le point (.
) sont interprétés littéralement par les chemins d'accès basés sur des chaînes de caractères. Cependant, abcd/
sans caractères supplémentaires ne correspondra pas à l'itinéraire. Pour cela, vous devez mettre le joker entre accolades pour le rendre optionnel :
forRoutes({
path: 'abcd/{*splat}',
method: RequestMethod.ALL,
});
Consommateur de middleware#
Le MiddlewareConsumer
est une classe "helper". Elle fournit plusieurs méthodes intégrées pour gérer les middlewares. Toutes peuvent être simplement chaînées dans le style fluide. La méthode forRoutes()
peut prendre une seule chaîne, plusieurs chaînes, un objet RouteInfo
, une classe de contrôleur et même plusieurs classes de contrôleur. Dans la plupart des cas, vous passerez simplement une liste de contrôleurs séparés par des virgules. Voici un exemple avec un seul contrôleur :
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
import { CatsController } from './cats/cats.controller';
@Module({
imports: [CatsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes(CatsController);
}
}
import { Module } from '@nestjs/common';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { CatsModule } from './cats/cats.module';
import { CatsController } from './cats/cats.controller';
@Module({
imports: [CatsModule],
})
export class AppModule {
configure(consumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes(CatsController);
}
}
Astuce La méthode apply()
peut prendre un seul middleware, ou plusieurs arguments pour spécifier plusieurs middlewares.
Exclure des routes#
Parfois, nous voulons exclure certaines routes de l'application du middleware. Nous pouvons facilement exclure certaines routes avec la méthode exclude()
. Cette méthode peut prendre une seule chaîne de caractères, plusieurs chaînes de caractères, ou un objet RouteInfo
identifiant les routes à exclure, comme montré ci-dessous :
consumer
.apply(LoggerMiddleware)
.exclude(
{ path: 'cats', method: RequestMethod.GET },
{ path: 'cats', method: RequestMethod.POST },
'cats/{*splat}',
)
.forRoutes(CatsController);
Astuce La méthode exclude()
prend en charge les paramètres joker en utilisant le paquet path-to-regexp.
Dans l'exemple ci-dessus, LoggerMiddleware
sera lié à toutes les routes définies dans CatsController
excepté les trois passées à la méthode exclude()
.
Cette approche offre une certaine souplesse dans l'application ou l'exclusion d'un logiciel intermédiaire en fonction d'itinéraires ou de modèles d'itinéraires spécifiques.
Middleware fonctionnel#
La classe LoggerMiddleware
que nous avons utilisée est assez simple. Elle n'a pas de membres, pas de méthodes supplémentaires, et pas de dépendances. Pourquoi ne pas la définir dans une simple fonction au lieu d'une classe ? En fait, c'est possible. Ce type de middleware est appelé ** middleware fonctionnel**. Pour illustrer la différence, transformons le middleware du logger, basé sur une classe, en middleware fonctionnel :
import { Request, Response, NextFunction } from 'express';
export function logger(req: Request, res: Response, next: NextFunction) {
console.log(`Request...`);
next();
};
export function logger(req, res, next) {
console.log(`Request...`);
next();
};
Et utilisons-le dans le AppModule
:
consumer
.apply(logger)
.forRoutes(CatsController);
Astuce Envisagez d'utiliser l'alternative plus simple du middleware fonctionnel chaque fois que votre middleware n'a besoin d'aucune dépendance.
Middlewares multiples#
Comme mentionné ci-dessus, afin de lier plusieurs middleware qui sont exécutés séquentiellement, il suffit de fournir une liste séparée par des virgules dans la méthode apply()
:
consumer.apply(cors(), helmet(), logger).forRoutes(CatsController);
Middleware global#
Si nous voulons lier le middleware à chaque route enregistrée en une seule fois, nous pouvons utiliser la méthode use()
qui est fournie par l'instance INestApplication
:
const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(process.env.PORT ?? 3000);
Astuce L'accès au conteneur DI dans un middleware global n'est pas possible. Vous pouvez utiliser un middleware fonctionnel à la place en utilisantapp.use()
. Alternativement, vous pouvez utiliser un middleware de classe et le consommer avec.forRoutes('*')
dans leAppModule
(ou tout autre module).