NestJS Logo
ads via Carbon Design and Development tips in your inbox. Every weekday. ads via Carbon

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 et fastify gèrent les middlewares différemment et fournissent des signatures de méthodes différentes, apprenez-en plus ici.
logger.middleware.ts
JS TS

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.

app.module.ts
JS TS

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

app.module.ts
JS TS

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éthode configure() peut être rendue asynchrone en utilisant async/await (par exemple, vous pouvez await la fin d'une opération asynchrone dans le corps de la méthode configure()).
Attention Lorsque vous utilisez l'adaptateur express, l'application NestJS va enregistrer json et urlencoded à partir du paquet body-parser par défaut. Cela signifie que si vous voulez personnaliser ce middleware via le MiddlewareConsumer, vous devez désactiver le middleware global en mettant le drapeau bodyParser à false lors de la création de l'application avec NestFactory.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 :

app.module.ts
JS TS

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 CatsControllerexcepté 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 :

logger.middleware.ts
JS TS

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 :

app.module.ts
JS TS

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 :

main.ts
JS TS

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 utilisant app.use(). Alternativement, vous pouvez utiliser un middleware de classe et le consommer avec .forRoutes('*') dans le AppModule (ou tout autre module).

Soutenez-nous

Nest est un projet open source sous licence MIT. Il peut se développer grâce au soutien de ces personnes formidables. Si vous souhaitez les rejoindre, apprenez-en plus ici.

Sponsors Principaux

Trilon LogoMarblism LogoMojam LogoAmplication Logo

Sponsors / Partenaires

Devenir un sponsor