ПО промежуточного слоя
Для чего используется промежуточное ПО?
- выполнить любой код
- Изменить объект запроса/ответа
- Завершить цикл запрос-ответ
- Вызвать следующую промежуточную функцию в стеке
- Если текущая функция промежуточного программного обеспечения не завершает цикл запрос-ответ, то необходимо вызвать метод next(), чтобы передать управление следующей функции промежуточного программного обеспечения. В противном случае запрос останется в состоянии ожидания.
Использовать промежуточное ПО
Nest не реализует декораторы промежуточного программного обеспечения, мы используем класс модуляconfigure
метод. Следовательно, модули, которые должны использовать промежуточное программное обеспечение, должныimplements NestModule
.
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes('cats');
}
}
инструкции
- название маршрута
forRoutes('cats')
- контроллер
forRoutes(CatsController)
- конкретный тип запроса
forRoutes({ path: 'cats', method: RequestMethod.GET })
- Подстановочные знаки маршрутизации
forRoutes({ path: 'ab*cd', method: RequestMethod.ALL })
- Отбросить определенные запросы
consumer
.apply(LoggerMiddleware)
.exclude(
{ path: 'cats', method: RequestMethod.GET },
{ path: 'cats', method: RequestMethod.POST },
'cats/(.*)',
)
.forRoutes(CatsController);
функциональное промежуточное ПО
Мы можем написать промежуточное ПО как функцию:
import { Request, Response } from 'express';
export function logger(req: Request, res: Response, next: Function) {
console.log(`Request...`);
next();
};
Как использовать функциональное промежуточное ПО
consumer
.apply(logger)
.forRoutes(CatsController);
глобальное использование
INestApplication
экземпляр имеетuse()
метод:
const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);
Фильтры исключений
Что такое фильтры
Nest поставляется со встроеннымуровень обработки исключений, этот уровень отвечает за обработку всех необработанных исключений во всем приложении. Если ваш код не обрабатывает исключение, слой перехватит исключение и отправит ответ, удобный для пользователя.
ВстроенныйHttpException
Механизм фильтров исключений предоставляется «из коробки», эту операцию выполняет встроенный глобальный фильтр исключений, который обрабатываетHttpException
Исключения для классов (и их подклассов). Если исключение не может быть идентифицировано (ниHttpException
ни отHttpException
унаследованный класс), встроенный фильтр исключений сгенерирует следующий ответ JSON по умолчанию:
{
"statusCode": 500,
"message": "Internal server error"
}
Мы можем свободно использовать встроенныйHttpException
захватывать:
@Get()
async findAll() {
throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
}
После этого клиент получит такой ответ:
{
"statusCode": 403,
"message": "Forbidden"
}
要仅覆盖JSON响应主体的消息部分,我们只需要在response参数中提供一个字符串。要覆盖整个JSON响应主体,我可以在response参数中传递一个对象。 Например:
@Get()
async findAll() {
throw new HttpException({
status: HttpStatus.FORBIDDEN,
error: 'This is a custom message',
}, HttpStatus.FORBIDDEN);
}
В этом случае ответ следующий:
{
"status": 403,
"error": "This is a custom message"
}
Встроенный подкласс HttpException удобен в использовании
Nest предоставляет нам набор наследства отHttpException
Фильтры исключений, такие как:
- BadRequestException
- UnauthorizedException
- NotFoundException
- ForbiddenException
- NotAcceptableException
- RequestTimeoutException
- ...
Таким образом, следующий код работает одинаково:
throw new HttpException('user not exist',HttpStatus.BAD_REQUEST)
throw new BadRequestException('User not exist');
Зачем настраивать фильтры
Хотя встроенныйException Filter
Многие случаи могут обрабатываться за вас автоматически, но вы можете захотеть иметь полный контроль над уровнем исключений. Например, вы можете захотеть добавить ведение журнала или использовать другиеJSON Schema
. Фильтры исключений предназначены именно для этой цели. Они позволяют вам уловить точный поток управления и отправить содержимое ответа обратно клиенту.
Как настроить фильтр
Например, мы настраиваемHttpExceptionFilter
:
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
response
.status(status)
.json({
statusCode: status,
message:exception.message,
error:exception.name,
timeStamp: new Date().toString(),
path: request.url
});
}
}
Требуется для всех фильтров обработки исключений
implements
общийExceptionFilter<T>
интерфейс, который требует от вас реализации метода catch(exception: T, host: ArgumentsHost). T представляет тип исключения.
Тогда обработка исключений, полученных клиентом, будет примерно такой:
{
"statusCode": 401,
"message": "Unauthorized",
"error": "Error",
"timeStamp": "Thu Jul 09 2020 11:38:07 GMT+0800 (中国标准时间)",
"path": "/user/aaa"
}
В приведенном выше пользовательском коде фильтров@Catch(HttpException)
Декоратор привязывает необходимые метаданные к фильтру исключений, его роль очень проста, он просто сообщает Nest, что этот фильтр ищетHttpException
Типа исключение, ничего более сложного.@Catch()
Декораторы могут принимать один параметр или список, разделенный запятыми. Это позволяет устанавливать фильтры для нескольких типов исключений одновременно.
Arguments Host
Давайте взглянемcatch()
параметры метода.exception
объект исключения, который обрабатывается в данный момент,host
параметрArgumentsHost
объект.ArgumentsHost
Является мощным объектом сущности. В этом примере кода мы просто используем его для получения значения, которое было передано исходному обработчику запроса (в контроллере, где произошло исключение).Request
а такжеResponse
ссылка на объект. мы вArgumentsHost
Воспользуйтесь вспомогательными методами для получения требуемогоRequest
а такжеResponse
объект. существуетглава контекста выполненияУзнать оArgumentsHost
Чтобы получить больше информации.
Привязать фильтры к тому месту, где они используются
- Method
@Post()
@UseFilters(new HttpExceptionFilter())
async create(@Body() createCatDto: CreateCatDto) {
throw new ForbiddenException();
}
- Controller
@UseFilters(new HttpExceptionFilter())
export class CatsController {}
- Global
- использовать
useGlobalFilters
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter());
глобальный перехватчик
useGlobalInterceptors
используется во всем приложении для каждогоcontroller
и каждыйroutes
. Что касается внедрения зависимостей, глобальные перехватчики, зарегистрированные вне любого модуля (например, с помощью useGlobalInterceptors ), не могут внедрять зависимости, поскольку это делается вне контекстов всех модулей. Чтобы решить эту проблему, мы можем установить его непосредственно в любом модуле следующим образом.interceptor
.
- использовать любой
Module
import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_FILTER,
useClass: HttpExceptionFilter,
},
],
})
export class AppModule {}
Трубы
трубы используются@Injectable()
Класс, аннотированный декоратором. Трубы должны быть реализованыPipeTransform
интерфейс.
Для чего нужны трубы?
- конвертировать: преобразование входных данных в требуемый формат (например, преобразование строки в целое число)
- проверять: проверьте входные данные. Если они верны, просто верните их. Если они неверны, создайте исключение (например, обычная проверка существования идентификатора пользователя)
В обоих случаях трубопроводcontroller route handler
обработанныйarguments
работать. Nest вставляет канал перед вызовом метода, и канал принимает аргументы и работает с ними. В этот момент любойпреобразование/проверкаоперация, а затем вызов с преобразованными аргументамиroute handler
.
Например,ParseIntPipe
Конвейер преобразует аргументы обработчика метода в целые числа JavaScript (выдает исключение, если преобразование завершается неудачно).
Встроенные трубы
Nest предоставляет нам 5 труб из коробки.
- ValidationPipe
- ParseIntPipe
- ParseBoolPipe
- ParseArrayPipe
- ParseUUIDPipe
- DefaultValuePipe
Как и где использовать встроенные трубы
Привяжите экземпляр класса конвейера к соответствующему контексту. в нашемParseIntPipe
В нашем примере мы хотим связать конвейер с определенным методом обработки маршрута и убедиться, что он запускается до вызова этого метода. Мы используем следующее, что можно назвать привязкой пайплайна на уровне параметров метода:
@Get(':id')
async findOne(@Param('id', ParseIntPipe) id: number) {
return this.catsService.findOne(id);
}
Если бы мы передали строку вместо числа, то получили бы такой ответ:
{
"statusCode": 400,
"message": "Validation failed (numeric string is expected)",
"error": "Bad Request"
}
Это исключение предотвратитfindOne
метод выполняется.
Передайте класс Pipe или объект экземпляра Pipe
В приведенном выше примере мы передали класс (ParseIntPipe
), вместо экземпляров мы оставляем задачу создания экземпляра фреймворку и разрешаем внедрение зависимостей. Pipe and Guard, мы можем передавать экземпляры объектов. Передача объекта мгновенного экземпляра полезна, если мы хотим обогатить поведение встроенного конвейера некоторыми параметрами конфигурации. Например:
@Get(':id')
async findOne(
@Param('id', new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }))
id: number,
) {
return this.catsService.findOne(id);
}
Таким образом, код ненормального состояния, который мы получаем, равен 406.Not Acceptable
:
{
"statusCode": 406,
"message": "Validation failed (numeric string is expected)",
"error": "Error",
"timeStamp": "Thu Jul 09 2020 13:38:07 GMT+0800 (中国标准时间)",
"path": "/user/aaa"
}
Таможенные трубы
Например, настройте канал, который проверяет, существует ли пользователь:
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException, HttpException, HttpStatus } from '@nestjs/common';
import { UsersService } from 'src/users/users.service';
@Injectable()
export class UserByIdPipe implements PipeTransform<string> {
constructor(private readonly usersService: UsersService) { }
async transform(value: string, metadata: ArgumentMetadata) {
const user = await this.usersService.findOne(value);
if (user) {
return user;
} else {
throw new BadRequestException('User not exist');
}
}
}
PipeTransform
— это универсальный интерфейс, который должен быть реализован любым конвейером. Общий интерфейс использует T для типа входного значения и R для возвращаемого типа метода transform().
Каждый конвейер должен реализовыватьtransform()
метод достиженияPipeTransform
интерфейс. Этот метод имеет два параметра:
- value
- metedata
value
Параметр — это текущий обрабатываемый параметр метода, то есть переданное значение. а такжеmetadata
метаданные для текущего обрабатываемого параметра метода. Объекты метаданных обладают следующими свойствами:
export interface ArgumentMetadata {
type: 'body' | 'query' | 'param' | 'custom';
metatype?: Type<unknown>;
data?: string;
}
Используйте пользовательские каналы
@Get(':id')
getProfile(@Request() req, @Param('id', ParseIntPipe, UserByIdPipe) user: User) {
return user;
}
Если пользователь существует, возвращается информация о пользователе. Если пользователь не существует, клиент получит этот ответ:
{
"statusCode": 400,
"message": "User not exist",
"error": "Error",
"timeStamp": "Thu Jul 09 2020 13:45:38 GMT+0800 (中国标准时间)",
"path": "/user/22"
}
Иногда мы не хотим возвращать информацию о пользователе напрямую, потому что мы можем выполнять проверку перед удалением/регистрацией пользователя. В процессе:
if (user) {
return value;
} else {
throw new BadRequestException('User not exist');
}
как использовать:
@Delete(':id')
remove(@Param('id', ParseIntPipe, UserByIdPipe) id: string): Promise<void> {
return this.usersService.remove(id);
}
Используйте сценарии для преобразования данных
проверятьНе единственный сценарий для пользовательской сантехники. Ранее мы упомянули, что трубопровод также может передавать входные данныеконвертироватьв нужный формат, так как изtransform
Значение, возвращаемое методом, полностью переопределит предыдущее значение параметра.
Когда использовать преобразование? Иногда данные, переданные от клиента, требуют некоторых изменений (например, преобразования строки в целое число), прежде чем они смогут быть правильно обработаны методом обработки маршрута. Кроме того, некоторые обязательные поля данных могут отсутствовать, и мы надеемся, что можно будет использовать значения по умолчанию. Конвейер преобразования может выполнять эти функции, вставляя логику обработки между запросом клиента и методом обработки запроса.
Следующий пример является одним из наших пользовательскихParseIntPipe
, который отвечает за преобразование строки в целочисленное значение. (Как упоминалось выше, Nest имеет встроенный ParseIntPipe, который является более сложным; здесь мы просто используем следующий конвейер в качестве простого примера пользовательского конвейера преобразования).
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
@Injectable()
export class ParseIntPipe implements PipeTransform<string, number> {
transform(value: string, metadata: ArgumentMetadata): number {
const val = parseInt(value, 10);
if (isNaN(val)) {
throw new BadRequestException('Validation failed');
}
return val;
}
}
Значение по умолчанию заполняет трубу
Конвейер по умолчаниюDefaultValuePipe
использование
@Get()
async findAll(
@Query('activeOnly', new DefaultValuePipe(false), ParseBoolPipe) activeOnly: boolean,
@Query('page', new DefaultValuePipe(0), ParseIntPipe) page: number,
) {
return this.catsService.findAll({ activeOnly, page });
}
Как использовать трубы
-
Parameter
Конвейеры с параметрами полезны, когда логика проверки заботится только о конкретном параметре.
@Get(':id') async findOne(@Param('id', ParseIntPipe) id: number) { return this.catsService.findOne(id); } @Post() async create( @Body(new ValidationPipe()) createCatDto: CreateCatDto) { this.catsService.create(createCatDto); }
-
Golbal
- использовать
useGlobalPipes
const app = await NestFactory.create(AppModule); app.useGlobalPipes(new ValidationPipe());
- использовать любой
Module
import { Module } from '@nestjs/common'; import { APP_PIPE } from '@nestjs/core'; @Module({ providers: [ { provide: APP_PIPE, useClass: ValidationPipe, }, ], }) export class AppModule {}
- использовать
Охранники маршруты гвардии
Что такое гвардии
Охранникиединственная ответственностьиз. Они определяют, должен ли данный запрос обрабатываться обработчиком маршрута, на основе определенных условий, присутствующих во время выполнения (таких как разрешения, роли, ACL и т. д.). Это часто называют авторизацией. Авторизация (и то, с чем она обычно работает — аутентификация) обычно выполняетсяпромежуточное ПОиметь дело с. Промежуточное ПО — хороший выбор для аутентификации, поскольку такие вещи, как проверка токена или добавление свойств к объекту запроса и т. д., не имеют сильной связи с маршрутизацией.
Однако есть и другие проблемы с промежуточным ПО. вызовnext()
После функции неизвестно, какой обработчик будет выполнен. а такжеGuardsможет получить доступExecutionContext
Например, чтобы вы точно знали, что произойдет дальше. Они разработаны так же, как фильтры исключений, конвейеры и перехватчики, что позволяет вам вставлять логику обработки в нужное место в цикле запрос/ответ.
Охранники выполняются после промежуточного программного обеспечения и до перехватчиков/каналов.
Как реализовать охрану
Как упоминалось ранее,AuthorizationдаGuards
Хороший корпус использования, поскольку конкретный маршрут доступен только в том случае, если вызывающий абонент (обычно конкретный аутентифицированный пользователь) имеет достаточные разрешения. Теперь мы построимAuthGuard
Предположим, что пользователь был аутентифицирован (токен был добавлен в заголовки запроса). Он будет извлекать и подтвердить токен и использовать извлеченную информацию, чтобы определить, может ли запрос продолжаться.
Что касается проверки подлинности токена JWT, я напишу статью о том, как это реализовать позже.
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
return validateRequest(request);
}
}
Каждая гвардия должна реализоватьcanActivate()
функция. Эта функция должна возвращать логическое значение, указывающее, разрешен ли текущий запрос. Он может возвращать ответы синхронно или асинхронно (через Promise или Observable). Nest использует возвращаемое значение для управления следующим действием:
- Если он возвращает true, запрос будет обработан.
- Если возвращается false, Nest отклонит запрос.
Execution context
canActivate()
Функция имеет только один параметр, которыйExecutionContext
пример.ExecutionContext
унаследовано отArgumentsHost
. Мы видели выше в фильтре исключенийArgumentsHost
. В приведенном выше примере мы просто используем тот жеArgumentsHost
Тот же метод, определенный выше, для получения парыRequest
ссылка на объект.
по наследствуArgumentsHost
,ExecutionContext
Также было добавлено несколько методов, предоставляющих более подробную информацию о текущем процессе выполнения. Эта информация может помочь нам создавать Guards, которые работают в контексте контроллеров, методов и выполнения. существуетздесьУзнать оExecutionContext
Чтобы получить больше информации.
Как использовать охрану
- Controller
@Controller('cats') @UseGuards(RolesGuard) export class CatsController {}
- Method
@UseGuards(RolesGuard) @Delete(':id') remove(@Param('id', ParseIntPipe, UserByIdPipe) id: string): Promise<void> { return this.usersService.remove(id); }
- Global
- использовать
useGlobalGuards
const app = await NestFactory.create(AppModule); app.useGlobalGuards(new RolesGuard());
- использовать
module
import { Module } from '@nestjs/common'; import { APP_GUARD } from '@nestjs/core'; @Module({ providers: [ { provide: APP_GUARD, useClass: RolesGuard, }, ], }) export class AppModule {}
- использовать
установить роль
нашRolesGuard
Это работает, но не использует наиболее важную функцию Guards — контекст выполнения. Он также не знает, к каким ролям разрешен доступ каждому обработчику. Например, для разных маршрутовCatsController
Могут иметь разные схемы разрешений. Некоторые из них могут быть доступны только пользователям с правами администратора, в то время как другие могут быть открыты для всех. Как мы сопоставляем роли с маршрутами?
Это примерно здесьпользовательские метаданные(понять больше) вверх. Nest提供了通过@SetMetadat()
Функция декоратора для прикрепления пользовательских метаданных к маршрутам. Эти метаданные предоставляют недостающие данные о символах. Давайте посмотрим на использование@SetMetadata()
Методы:
@Post()
@SetMetadata('roles', ['admin'])
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
Ставим метаданные роли (roles — ключ метаданных,['admin']
является значением) добавляется кcreate()
метод. использовать его прямо в маршруте@SetMetadata()
Это не лучший способ написать это, мы можем инкапсулировать это в@Role()
декоратор.
import { SetMetadata } from '@nestjs/common';
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
Затем внутри контроллера:
@Post()
@Roles('admin')
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
Внедрить Guard на основе ролей
По ролевой информации метаданных маршрута мы судим, имеет ли пользователь право заходить на маршрут в Guards:
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable } from 'rxjs';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean>{
const roles = this.reflector.get<string[]>('roles', context.getHandler());
if (!roles) {
return true;
}
const request = context.switchToHttp().getRequest();
const user = request.user;
const hasRole = () =>
user.roles.some(role => !!roles.find(item => item === role));
return user && user.roles && hasRole();
}
}
Получите информацию о пользователе через запрос. Прозеро, потому что мы предположили, что приложение, которое было выпущено аутентификацией токена в приложении, и контекст хранит информацию о пользователе, что по сути является вопросом каждого приложения.
Использовать в контексте-чувствительном способеReflector
Подробнее см.Execution contextизотражение и метаданныечасть.
В этом случае, если пользователь не в роли администратора, будет получен следующий ответ:
{
"statusCode": 403,
"message": "Forbidden resource",
"error": "Forbidden"
}
Фреймворк использует по умолчаниюForbiddenException
Генерация исключений, конечно, если вы не хотите использовать дефолтные, вы также можете генерировать свои собственные исключения в Guards, такие как:
throw new UnauthorizedException();
Перехватчики перехватчик
Для чего нужен перехватчик?
- Добавляем собственный метод до/после выполнениялогика
- Преобразовать/упаковатьрезультат, возвращаемый методом
- Преобразовать/упаковатьИсключение, вызванное методом
- расширятьСодержание основного метода
- В зависимости от некоторых особых обстоятельств (таких как цель кэширования) полностьюперезаписать/переписатьметод
Сфера использования
- Controller
@UseInterceptors(LoggingInterceptor)
export class CatsController {}
- Method
@UseInterceptors(LoggingInterceptor)
@Get()
async findAll(): Promise<Cat[]> {
return this.catsService.findAll();
}
- Global
- использовать
useGlobalInterceptors
const app = await NestFactory.create(AppModule);
app.useGlobalInterceptors(new LoggingInterceptor());
- Используйте любой модуль
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_INTERCEPTOR,
useClass: LoggingInterceptor,
},
],
})
export class AppModule {}
Custom DecoratorsПользовательские декораторы
что такое декоратор
Nest построен на языковой функции, называемой декораторами. Концепцию декораторов можно кратко резюмировать следующим образом:
Декоратор ES6 — это выражение, которое возвращает функцию, которая может принимать цель, имя и описание свойства в качестве аргументов. Вы можете сделать это, поставив перед декоратором префикс
@
персонаж, чтобы применить его и поместить его на самую вершину содержания, который будет оформлен. Содержание может быть классом, методом или свойством.
Как настроить декоратор
Когда поведение декоратора зависит от некоторого условия, вы можете использоватьdata
Аргументы передают аргументы фабричной функции декоратора. Распространенным сценарием является настраиваемый декоратор, который может извлекать свойства из объекта запроса по ключу. Например, ниже, передавusername
поле для получения значения, еслиUser
Если объект более сложный, данные могут быть получены легче, а читабельность лучше.
user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const User = createParamDecorator(
(data: string, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
const user = request.user;
return data ? user && user[data] : user;
},
);
user.controller.ts
@Get('username')
async findOne(@UserDecorator('username') username: string) {
return `Hello ${username}`;
}
использовать с трубами
Nest обрабатывает пользовательские декораторы так же, как и встроенные, и мы можем использовать конвейерную проверку и т. д. как встроенные.
@Get()
async findOne(@User(new ValidationPipe()) user: UserEntity) {
console.log(user);
}
Комбинированный декоратор
Nest предоставляет вспомогательный метод для создания нескольких декораторов. Например, допустим, вы хотите объединить все декораторы, связанные с аутентификацией, в один декоратор. Это может быть достигнуто за счет:
import { applyDecorators } from '@nestjs/common';
export function Auth(...roles: Role[]) {
return applyDecorators(
SetMetadata('roles', roles),
UseGuards(AuthGuard, RolesGuard),
ApiBearerAuth(),
ApiUnauthorizedResponse({ description: 'Unauthorized"' }),
);
}
Затем внутри контроллера:
@Get('users')
@Auth('admin')
findAllUsers() {}
findAllUsers
Теперь это приводит к применению четырех декораторов с одним объявлением.
На самом деле, после тщательной разработки автора фреймворка Nest Камиля Мысливца, Nest очень прост и удобен в использовании, а расширение довольно гибкое и мощное. Но мы должны понимать, для чего используются эти концепции Nest, чтобы мы знали, когда реализовать фильтр, а когда реализовать канал. Или узнайте разницу между промежуточным ПО и гвардией. Особенно Interceptor, который, кажется, доступен везде. Если вы плохо понимаете официальную документацию, ее легко использовать неправильно.
Позже я сообщу больше о навыках использования Nest, например, о том, как реализовать проверку токена JWT, как спроектировать переменные среды/регистрацию глобальных данных или о других интересных знаниях.
В старые времена на опушке леса Маодяньского общества внезапно появились дорога и мост. Приятно познакомиться, незнакомец.