Наиболее полное введение в инфраструктуру Node Nest.JS с использованием передового шаблона MVC.

NestJS

ПО промежуточного слоя

Для чего используется промежуточное ПО?

  • выполнить любой код
  • Изменить объект запроса/ответа
  • Завершить цикл запрос-ответ
  • Вызвать следующую промежуточную функцию в стеке
  • Если текущая функция промежуточного программного обеспечения не завершает цикл запрос-ответ, то необходимо вызвать метод next(), чтобы передать управление следующей функции промежуточного программного обеспечения. В противном случае запрос останется в состоянии ожидания.

Использовать промежуточное ПО

Nest не реализует декораторы промежуточного программного обеспечения, мы используем класс модуляconfigureметод. Следовательно, модули, которые должны использовать промежуточное программное обеспечение, должныimplements NestModule.

export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes('cats');
  }
}

инструкции

  1. название маршрутаforRoutes('cats')
  2. контроллерforRoutes(CatsController)
  3. конкретный тип запросаforRoutes({ path: 'cats', method: RequestMethod.GET })
  4. Подстановочные знаки маршрутизацииforRoutes({ path: 'ab*cd', method: RequestMethod.ALL })
  5. Отбросить определенные запросы
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
  1. использоватьuseGlobalFilters
const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new HttpExceptionFilter());

глобальный перехватчикuseGlobalInterceptorsиспользуется во всем приложении для каждогоcontrollerи каждыйroutes. Что касается внедрения зависимостей, глобальные перехватчики, зарегистрированные вне любого модуля (например, с помощью useGlobalInterceptors ), не могут внедрять зависимости, поскольку это делается вне контекстов всех модулей. Чтобы решить эту проблему, мы можем установить его непосредственно в любом модуле следующим образом.interceptor.

  1. использовать любой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

    1. использоватьuseGlobalPipes
    const app = await NestFactory.create(AppModule);
    app.useGlobalPipes(new ValidationPipe());
    
    1. использовать любой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
    1. использоватьuseGlobalGuards
    const app = await NestFactory.create(AppModule);
    app.useGlobalGuards(new RolesGuard());
    
    1. использовать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
  1. использоватьuseGlobalInterceptors
const app = await NestFactory.create(AppModule);
app.useGlobalInterceptors(new LoggingInterceptor());
  1. Используйте любой модуль
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, как спроектировать переменные среды/регистрацию глобальных данных или о других интересных знаниях.

В старые времена на опушке леса Маодяньского общества внезапно появились дорога и мост. Приятно познакомиться, незнакомец.