Skip to main content

Прамежкавае праграмнае забеспячэнне

Прамежкавае праграмнае забеспячэнне - гэта функцыя, якая выклікаецца перад апрацоўшчыкам маршруту. Функцыі прамежкавага праграмнага забеспячэння маюць доступ да аб'ектаў запыту і адказу , а таксама функцыі прамежкавага праграмнага забеспячэння next() у цыкле запыт-адказ прыкладання. Наступная функцыя прамежкавага праграмнага забеспячэння звычайна пазначаецца зменнай з імем next .

Прамежкавае праграмнае забеспячэнне Nest па змаўчанні эквівалентна экспрэс - прамежкавым ПЗ. Наступнае апісанне з афіцыйнай экспрэс-дакументацыі апісвае магчымасці прамежкавага праграмнага забеспячэння:

Функцыі прамежкавага праграмнага забеспячэння могуць выконваць наступныя задачы:

  • выканаць любы код.
  • уносіць змены ў аб'екты запыту і адказу.
  • завяршыць цыкл запыт-адказ.
  • выклікаць наступную функцыю прамежкавага праграмнага забеспячэння ў стэку.
  • калі бягучая функцыя прамежкавага праграмнага забеспячэння не завяршае цыкл запыт-адказ, яна павінна выклікаць next() каб перадаць кіраванне наступнай функцыі прамежкавага праграмнага забеспячэння. У адваротным выпадку запыт застанецца вісіць.

Вы рэалізуеце карыстальніцкае прамежкавае праграмнае забеспячэнне Nest альбо ў функцыі, альбо ў класе з дэкаратарам @Injectable() . Клас павінен рэалізаваць інтэрфейс NestMiddleware , пры гэтым функцыя не мае асаблівых патрабаванняў. Давайце пачнем з рэалізацыі простай функцыі прамежкавага праграмнага забеспячэння з выкарыстаннем метаду класа.

папярэджанне Папярэджанне Express і fastify па-рознаму апрацоўваюць прамежкавае праграмнае забеспячэнне і забяспечваюць розныя сігнатуры метадаў, чытайце больш тут .

@@filename(logger.middleware)
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();
}
}
@@switch
import { Injectable } from '@nestjs/common';

@Injectable()
export class LoggerMiddleware {
use(req, res, next) {
console.log('Request...');
next();
}
}

Ін'екцыя залежнасці

Прамежкавае праграмнае забеспячэнне Nest цалкам падтрымлівае Dependency Injection. Гэтак жа, як у выпадку з пастаўшчыкамі і кантролерам, яны могуць уводзіць залежнасці , якія даступныя ў адным модулі. Як звычайна, гэта робіцца праз constructor .

Прымяненне прамежкавага праграмнага забеспячэння

У дэкаратары @Module() няма месца для прамежкавага праграмнага забеспячэння. Замест гэтага мы наладжваем іх з дапамогай метаду configure() класа модуля. Модулі, якія ўключаюць прамежкавае праграмнае забеспячэнне, павінны рэалізаваць інтэрфейс NestModule . Давайце наладзім LoggerMiddleware на ўзроўні AppModule .

@@filename(app.module)
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');
}
}
@@switch
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');
}
}

У прыведзеным вышэй прыкладзе мы наладзілі LoggerMiddleware для апрацоўшчыкаў маршрутаў /cats , якія раней былі вызначаны ўнутры CatsController . Мы таксама можам дадаткова абмежаваць прамежкавае праграмнае забеспячэнне пэўным метадам запыту, перадаўшы аб'ект, які змяшчае path маршруту і method запыту, у метад forRoutes() пры наладжванні прамежкавага праграмнага забеспячэння. Звярніце ўвагу на тое, што ў прыведзеным ніжэй прыкладзе мы імпартуем пералік RequestMethod для спасылкі на патрэбны тып метаду запыту.

@@filename(app.module)
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 });
}
}
@@switch
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 });
}
}
Падказка Метад configure() можна зрабіць асінхронным з дапамогай async/await (напрыклад, вы можаце await завяршэння асінхроннай аперацыі ўнутры цела метаду configure() ). :::

:::папярэджанне Папярэджанне Пры выкарыстанні express адаптара праграма NestJS па змаўчанні зарэгіструе json і urlencoded з body-parser пакета. Гэта азначае, што калі вы хочаце наладзіць прамежкавае праграмнае забеспячэнне праз MiddlewareConsumer , вам трэба адключыць глабальнае прамежкавае праграмнае забеспячэнне, усталяваўшы для флага bodyParser значэнне false пры стварэнні прыкладання з дапамогай NestFactory.create() . :::

Падстаноўныя знакі маршруту

Таксама падтрымліваюцца маршруты на аснове шаблонаў. Напрыклад, зорачка выкарыстоўваецца як сімвал падстаноўкі і будзе адпавядаць любой камбінацыі знакаў:

forRoutes({ path: 'ab*cd', method: RequestMethod.ALL });

Шлях маршруту 'ab*cd' будзе адпавядаць abcd , ab_cd , abecd і гэтак далей. Персанажы ? , + , * і () могуць выкарыстоўвацца ў шляху маршруту і з'яўляюцца падмноствамі сваіх аналагаў рэгулярных выразаў. Злучок ( - ) і кропка ( . ) інтэрпрэтуюцца літаральна шляхамі на аснове радкоў.

:::папярэджанне Папярэджанне Пакет fastify выкарыстоўвае апошнюю версію пакета path-to-regexp , які больш не падтрымлівае падстаноўныя зорачкі * . Замест гэтага вы павінны выкарыстоўваць параметры (напрыклад, (.*) , :splat* ). :::

Спажывец прамежкавага ПЗ

MiddlewareConsumer - гэта дапаможны клас. Ён забяспечвае некалькі ўбудаваных метадаў кіравання прамежкавым праграмным забеспячэннем. Усе яны могуць быць проста звязаны ў ланцужок у беглым стылі . Метад forRoutes() можа прымаць адзін радок, некалькі радкоў, аб'ект RouteInfo , клас кантролера і нават некалькі класаў кантролера. У большасці выпадкаў вы, верагодна, проста перадасце спіс кантролераў, падзеленых коскамі. Ніжэй прыведзены прыклад з адным кантролерам:

@@filename(app.module)
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);
}
}
@@switch
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);
}
}
Падказка Метад apply() можа прымаць адно прамежкавае праграмнае забеспячэнне або некалькі аргументаў для ўказання некалькіх прамежкавых праграм . :::

Без уліку маршрутаў

Часам мы хочам выключыць некаторыя маршруты з прымянення прамежкавага праграмнага забеспячэння. Мы можам лёгка выключыць пэўныя маршруты з дапамогай метаду exclude() . Гэты метад можа выключаць адзін радок, некалькі радкоў або аб'ект RouteInfo , які вызначае маршруты, як паказана ніжэй:

consumer
.apply(LoggerMiddleware)
.exclude(
{ path: 'cats', method: RequestMethod.GET },
{ path: 'cats', method: RequestMethod.POST },
'cats/(.*)',
)
.forRoutes(CatsController);
Падказка Метад exclude() падтрымлівае параметры падстаноўкі з выкарыстаннем пакета шлях да рэгулярных выразаў . :::

У прыведзеным вышэй прыкладзе LoggerMiddleware будзе прывязаны да ўсіх маршрутаў, вызначаных у CatsController акрамя трох, перададзеных у метад exclude() .

Функцыянальнае прамежкавае праграмнае забеспячэнне

Клас LoggerMiddleware які мы выкарыстоўваем, даволі просты. Ён не мае членаў, дадатковых метадаў і залежнасцей. Чаму мы не можам проста вызначыць яго ў простай функцыі замест класа? На самай справе, мы можам. Гэты тып прамежкавага праграмнага забеспячэння называецца функцыянальным прамежкавым праграмным забеспячэннем . Давайце пераўтворым прамежкавае праграмнае забеспячэнне рэгістратара з класавага ў функцыянальнае прамежкавае праграмнае забеспячэнне, каб праілюстраваць розніцу:

@@filename(logger.middleware)
import { Request, Response, NextFunction } from 'express';

export function logger(req: Request, res: Response, next: NextFunction) {
console.log(`Request...`);
next();
};
@@switch
export function logger(req, res, next) {
console.log(`Request...`);
next();
};

І выкарыстоўвайце яго ў AppModule :

@@filename(app.module)
consumer
.apply(logger)
.forRoutes(CatsController);
Падказка Разгледзьце магчымасць выкарыстання больш простай функцыянальнай альтэрнатывы прамежкавага праграмнага забеспячэння кожны раз, калі вашаму прамежкаваму праграмнаму забеспячэнню не патрэбныя ніякія залежнасці. :::

Некалькі прамежкавага праграмнага забеспячэння

Як згадвалася вышэй, каб прывязаць некалькі прамежкавага праграмнага забеспячэння, якое выконваецца паслядоўна, проста ўвядзіце спіс праз коскі ўнутры метаду apply() :

consumer.apply(cors(), helmet(), logger).forRoutes(CatsController);

Глабальнае прамежкавае праграмнае забеспячэнне

Калі мы хочам прывязаць прамежкавае праграмнае забеспячэнне да кожнага зарэгістраванага маршруту адначасова, мы можам выкарыстоўваць метад use() , які пастаўляецца асобнікам INestApplication :

@@filename(main)
const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);
Падказка Доступ да кантэйнера DI у глабальным прамежкавым ПЗ немагчымы. Замест гэтага вы можаце выкарыстоўваць функцыянальнае прамежкавае праграмнае забеспячэнне пры выкарыстанні app.use() . Акрамя таго, вы можаце выкарыстоўваць прамежкавае праграмнае забеспячэнне класа і выкарыстоўваць яго з дапамогай .forRoutes('*') у AppModule (ці любым іншым модулі). :::