Учебный тур Nestjs (9) - Interceptor

Node.js

Добро пожаловать, чтобы продолжать обращать внимание *NestJs之旅*Серия статей

img

Перехватчик — это реализация, котораяNestInterceptorинтерфейс и есть@InjectableКласс, украшенный декоратором.

img

Interceptor — это приложение, основанное на идеях АОП-программирования.Следующие часто используемые функции:

  • Выполняется до или после выполнениядополнительная логика, эта логика вообще не является частью бизнеса
  • конвертироватьрезультат выполнения функции
  • конвертироватьИсключение возникло во время выполнения функции
  • Базовое поведение функций расширения
  • Полностью переписать поведение функции в конкретном сценарии (например, перехватчик кеша, который возвращается сразу после того, как есть доступный кеш, без выполнения реальной бизнес-логики, то есть было переписано поведение функции обработки бизнес-логики)

Интерфейс перехватчика

Каждый перехватчик должен реализоватьNestInterceptorМетод **intercept()** интерфейса, который принимает два параметра. Прототип метода выглядит следующим образом:

function intercept(context: ExecutionContext, next: CallHandler): Observable<any>

CallHandler

Этот интерфейс является абстракцией функции обработки маршрутизации.Интерфейс определяется следующим образом:

export interface CallHandler<T = any> {
    handle(): Observable<T>;
}

Возвращаемое значение функции handle() также является возвращаемым значением соответствующей функции маршрутизации.

В качестве примера возьмем список пользователей приобретения:

// user.controller.ts
@Controller('user')
export class UserController {
  @Get()
  list() {
    return [];
  }
}

При доступе к /user/list функция обработки маршрута возвращает **[]** Если перехватчик применен, вызов метода handle() интерфейса CallHandler также получит Observable (объект-оболочку RxJs).

Следовательно, если в перехватчике будет вызван метод next.handle(), то соответствующая функция обработки маршрута будет выполнена, а если не вызвана, то не будет выполнена.

Перехватчик регистрации ссылок запросов

С появлением микросервисов исходный единый проект разбивается на несколько более мелких подмодулей.Эти подмодули можно независимо разрабатывать, развертывать и запускать независимо, что значительно повышает эффективность разработки и выполнения, но также приводит к проблемам.Чаще всего , часто встречающаяся проблема заключается в том, что трудно найти журнал при сбое вызова интерфейса.

Крайне нецелесообразно жестко кодировать такой журнал вызовов ссылок в бизнес-логике, что серьезно нарушает принцип единой ответственности.Эта логика может быть реализована полностью через перехватчики.

// app.interceptor.ts
import { CallHandler, ExecutionContext, Injectable, Logger, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { Request } from 'express';
import { format } from 'util';

@Injectable()
export class AppInterceptor implements NestInterceptor {
  private readonly logger = new Logger(); // 实例化日志记录器

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const start = Date.now(); // 请求开始时间

    return next.handle().pipe(tap((response) => {
      // 调用完handle()后得到RxJs响应对象,使用tap可以得到路由函数的返回值
      const host = context.switchToHttp();
      const request = host.getRequest<Request>();

      // 打印请求方法,请求链接,处理时间和响应数据
      this.logger.log(format(
        '%s %s %dms %s',
        request.method,
        request.url,
        Date.now() - start,
        JSON.stringify(response),
      ));
    }));
  }
}
// user.controller.ts
@UseInterceptors(AppInterceptor)
export class UserController {
  @Get()
  list() {
    return [];
  }
}

Консоль хочет выводить при доступе к /user

[Nest] 96310   - 09/10/2019, 2:44 PM   GET /user 1ms []

Объем перехватчика

Перехватчик может привязать следующую область:

  • глобальный перехватчик
  • перехватчик контроллера
  • Перехватчик метода маршрутизации

глобальный перехватчик

Просто используйте следующий код в main.ts:

const app = await NestFactory.create(AppModule);
app.useGlobalInterceptors(new AppInterceptor());

перехватчик контроллера

будет принадлежать контроллерумаршрутизацияМетод вступает в силу:

@Controller('user')
@UseInterceptors(AppInterceptor)
export class UserController {
}

Перехватчик метода маршрутизации

Перехватывать только текущий декорированный метод маршрутизации:

@Controller('user')
export class UserController {
  @UseInterceptors(AppInterceptor)
  @Get()
  list() {
    return [];
  }
}

Обработка ответа

Возвращаемое значение handle() интерфейса CallHandler на самом деле является наблюдаемым объектом RxJs. Объектом можно управлять с помощью оператора RxJs. Например, существует интерфейс API. Ранее возвращаемая структура данных выглядит следующим образом. ответ нормальный, тело ответа это данные.Структура упаковки:

{
  "id":1,
  "name":"xialei"
}

Новое требование состоит в том, чтобы обернуть предыдущий ответ с чистыми данными в атрибут данных со следующей структурой:

{
  "data": {
    "id": 1,
    "name":"xialei"
  }
}

При получении этого запроса некоторые мелкие партнеры, возможно, уже разбирали количество интерфейсов ответа, а затем оценивали человеко-часы для подготовки к разработке.Используя перехватчик NestJ, это требование может быть реализовано менее чем за палочку благовоний.

import { CallHandler, ExecutionContext, Injectable, Logger, NestInterceptor } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
export class AppInterceptor implements NestInterceptor {

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {

    return next.handle().
      pipe(map(data => ({ data }))); // map操作符与Array.prototype.map类似
  }
}

После применения вышеуказанного перехватчика данные ответа будут обернуты слоем атрибута данных.

отображение исключений

Еще одним интересным примером является использование catchError RxJs для переопределения исключений, генерируемых обработчиками маршрутов.

import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  BadGatewayException,
  CallHandler,
} from '@nestjs/common';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable()
export class ErrorsInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next
      .handle()
      .pipe(
        catchError(err => throwError(new BadGatewayException())) // catchError用来捕获异常
      );
  }
}

Переписывание функции логики маршрутизации

В начале статьи упоминалось, что перехватчик может переопределить логику обработчика маршрутизации. Ниже приведен пример перехватчика буфера.

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable, of } from 'rxjs';

@Injectable()
export class CacheInterceptor implements NestInterceptor {
  constructor(private readonly cacheService: CacheService) {}
  
   async intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
  	const host = context.switchToHttp();
    const request = host.getRequest();
    if(request.method !== 'GET') {  
      // 非GET请求放行
      return next.handle();
    }
    const cachedData = await this.cacheService.get(request.url);
    if(cachedData) { 
      // 命中缓存,直接放行
      return of(cachedData);
    }
    return next.handle().pipe(tap(response) => { 
      // 响应数据写入缓存,此处可以等待缓存写入完成,也可以不等待
      this.cacheService.set(request.method, response);
    });
  }
}

конец

Эта статья является последней из основ NestJ и будет обновляться для конкретных модулей, таких как база данных, загрузка, аутентификация и т. д.

Из-за прямого выпуска группового QR-кода порог для вступления в группу крайне низок.В последнее время некоторые бездельники сканировали код для входа в группу для отправки рекламы/вредоносной информации, серьезно беспокоя участников группы, и QR канал ввода кодовой группы был закрыт. Нуждающиеся партнеры могут подписаться на официальный аккаунт, чтобы получить квалификацию для присоединения к группе.