Начало работы с Nestjs (1)

Node.js
Начало работы с Nestjs (1)

NestjsЭто прогрессивный фреймворк для Node.Нижний уровень по умолчанию использует экспресс (который можно преобразовать в fastify через адаптер), может использовать все промежуточное ПО экспресс или fastify и отлично поддерживает TypeScript. Студенты, знакомые со Spring и Angular, могут быстро начать работу с Nestjs, который заимствует множество дизайнерских идей из Spring и Angular.

в начале написанияhello worldПрежде давайте рассмотрим наиболее важные дизайнерские идеи и концепции в Nestjs.

внедрение зависимости

Injection Dependency Injection (внедрение зависимостей, для краткости)DI) находится в объектно-ориентированномИнверсия контроля(Инверсия управления, именуемаяIoC) является наиболее распространенной реализацией, в основном используемой для уменьшения связанности кода. Мы используем пример, чтобы проиллюстрировать, что такое инверсия управления.

Предположим, вы хотите построить машину, и вам нужен двигатель и колеса:

import { Engine } from './engine'
import { Tire } from './tire'

class Car {
  private engine;
  private wheel;
  
  constructor() {
    this.engine = new Engine();
    this.tire = new Tire();
  }
}

В этот моментCarЭтот класс зависит отEngineиTire, конструктор должен не только присвоить зависимость внутренним свойствам текущего класса, но и создать экземпляр зависимости. Допустим, существует множество видовCarвсе использованоEngine, то необходимоEngineзаменитьElectricEngine, впадет в смущение, дергая за один волос и двигая всем телом.

Затем используйте IoC для его преобразования:

import { Engine } from './engine'
import { Tire } from './tire'

class Container {
  private constructorPool;

  constructor() {
    this.constructorPool = new Map();
  }

  register(name, constructor) {
    this.constructorPool.set(name, constructor);
  }

  get(name) {
    const target = this.constructorPool.get(name);
    return new target();
  }
}

const container = new Container();
container.bind('engine', Engine);
container.bind('tire', Tire);

class Car {
  private engine;
  private tire;
  
  constructor() {
    this.engine = container.get('engine');
    this.tire = container.get('tire');
  }
}

В настоящее время,containerэквивалентноCarиEngine,Tireпересадочная станция междуCarНе нужно создавать экземпляр самостоятельноEngineилиTire,CarиEngine,TireМежду ними нет сильной связи.

Как видно из приведенного выше примера, перед использованием IoC,CarнеобходимостьEngineилиTireВы должны взять на себя инициативу по созданиюEngineилиTire, в это время правильноEngineилиTireконтроль за созданием и использованиемCarпод рукой.

После использования IoC,CarиEngineилиTireСвязь междуCarнеобходимостьEngineилиTireчас,IoC Containerбудет активно создавать этот объект дляCarиспользовать, в это времяCarПолучатьEngineилиTireПоведение активного сбора данных становится пассивным, и управление меняется на противоположное. когдаEngineилиTireлюбые изменения,CarЭто не будет затронуто, и развязка между ними завершена.

когда нам нужно проверитьCar, нам не нужноEngineилиTireвсеnewпостроить один разCar, просто поместите макетEngineилиTire, просто введите его в контейнер IoC.

Существует множество реализаций IoC, таких как Spring для Java, Laravel для PHP, Angular2+ для внешнего интерфейса и Nestjs для Node.

В Nestjs через@InjectableДекоратор регистрируется в контейнере IoC:

import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface';

@Injectable()
export class CatsService {
  private readonly cats: Cat[] = [];

  create(cat: Cat) {
    this.cats.push(cat);
  }

  findAll(): Cat[] {
    return this.cats;
  }
}

Внедрить в конструкторCatsServiceПример:

import { Controller, Get, Post, Body } from '@nestjs/common';
import { CreateCatDto } from './dto/create-cat.dto';
import { CatsService } from './cats.service';
import { Cat } from './interfaces/cat.interface';

@Controller('cats')
export class CatsController {
  constructor(private readonly catsService: CatsService) {}

  @Post()
  async create(@Body() createCatDto: CreateCatDto) {
    this.catsService.create(createCatDto);
  }

  @Get()
  async findAll(): Promise<Cat[]> {
    return this.catsService.findAll();
  }
}

CatsServiceкакprivider, должен быть вmoduleзарегистрирован вmoduleПри запуске он будет анализироватьmoduleво всех зависимостях, когдаmoduleПри уничтожении,providerтакже будут уничтожены вместе.

import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';
import { CatsService } from './cats/cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class ApplicationModule {}

модульный

Nestjs предоставляет модульную структуру для организации кода внутри одного домена в отдельные модули. Роль модульности заключается в четкой организации вашего приложения и расширении его с помощью внешних библиотек.

ModuleПучокcontroller,serviceиpipeи т. д. упакованы в связанные функциональные блоки, каждый из которых ориентирован на функциональную область, бизнес-сферу, рабочий процесс или общий инструмент.

Прошел в Nestjs@ModuleДекоратор объявляет модуль,@ModuleПринимает объект, описывающий свойства модуля:

import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
import { CoreModule } from './core/core.module';

@Module({
  imports: [CoreModule],
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService]
})
export class CatsModule {}

каждый принадлежащий этому модулюcontroller,serviceи т. д. должны быть зарегистрированы в этом модуле, если вам нужно ввести другие модули или сторонние модули, вам необходимо зарегистрировать его вimports,пройти черезexportsсоответствующийservice,moduleПодождите, чтобы поделиться.

Аспектно-ориентированное программирование

Аспектно-ориентированное программирование (сокращенно АОП) предназначено в основном для извлечения аспектов из процесса бизнес-обработки и выполнения некоторых операций на определенном этапе и этапе для достижения цели DRY (не повторяйтесь). АОП является дополнением к ООП. Например, он может обрабатывать глобальные журналы и ошибки в определенном аспекте. Такой универсальный подход означает, что метод обработки АОП является относительно крупнозернистым.

В Nestjs АОП разделен на следующие части (по порядку):

  • Middlewares
  • Guards
  • Перехватчики (до обработки потока)
  • Pipes
  • Перехватчики (после обработки потока)
  • Фильтры исключений (если найдены какие-либо исключения)

Middlewares

Промежуточное ПО — это то же самое, что и экспресс-промежуточное ПО, вы можете напрямую использовать экспресс-промежуточное ПО:

import * as helmet from 'helmet'

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule, {
    cors: true,
    logger: false,
  })

  app.use(helmet())

  await app.listen(config.port, config.hostName, () => {
    Logger.log(
      `Flash API server has been started on http://${config.hostName}:${config.port}`,
    )
  })
}

Guards

Охранники, как и охранники маршрутов во внешних маршрутах, в первую очередь определяют, должен ли запрос обрабатываться обработчиком маршрута. Guard может знать контекстную информацию, которую нужно выполнить, поэтому по сравнению с промежуточным программным обеспечением Guard может точно знать, что выполнять.

Охранники выполняются после каждого промежуточного программного обеспечения, но перед перехватчиками и каналами.

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);			// validateRequest 函数实现 Request 的验证
  }
}

Interceptors

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

import {
  Injectable,
  NestInterceptor,
  ExecutionContext,
  CallHandler,
} from '@nestjs/common'
import { Observable } from 'rxjs'
import { map } from 'rxjs/operators'
import { getFormatResponse } from '../../shared/utils/response'

export interface Response<T> {
  data: T
}

@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> {
  intercept(
    context: ExecutionContext,
    next: CallHandler,
  ): Observable<Response<T>> {
    return next.handle().pipe(map(getFormatResponse))
  }
}

Pipes

Труба с@Injectable()Класс декоратора и реализуетPipeTransformинтерфейс. Обычно канал используется для передачи входных данныхконвертироватьдля желаемого вывода или обработкипроверять.

Ниже приведенValidationPipe,сотрудничатьclass-validatorиclass-transformer, параметры могут быть проверены более легко.

import {
  PipeTransform,
  ArgumentMetadata,
  BadRequestException,
  Injectable,
} from '@nestjs/common'
import { validate } from 'class-validator'
import { plainToClass } from 'class-transformer'

@Injectable()
export class ValidationPipe implements PipeTransform<any> {
  async transform(value, metadata: ArgumentMetadata) {
    const { metatype } = metadata
    if (!metatype || !this.toValidate(metatype)) {
      return value
    }
    const object = plainToClass(metatype, value)
    const errors = await validate(object)
    if (errors.length > 0) {
      throw new BadRequestException('Validation failed')
    }
    return value
  }

  private toValidate(metatype): boolean {
    const types = [String, Boolean, Number, Array, Object]
    return !types.find(type => metatype === type)
  }
}

Exception filters

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

import { ExceptionFilter, Catch, ArgumentsHost } from '@nestjs/common';

@Catch()
export class AnyExceptionFilter implements ExceptionFilter {
  catch(exception: any, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const request = ctx.getRequest();

    response
      .status(status)
      .json({
        statusCode: exception.getStatus(),
        timestamp: new Date().toISOString(),
        path: request.url,
      });
  }
}

DTO

Объект доступа к данным (DTO) для краткости — это простой контейнер для группы агрегированных данных, которые необходимо передавать через границы процесса или сети. Он не должен содержать бизнес-логику и ограничивать свое поведение такими действиями, как внутренняя проверка согласованности и базовая проверка.

В Nestjs это можно сделать с помощью интерфейсов TypeScript или простых классов. Сотрудничатьclass-validatorиclass-transformerВы можете легко проверить параметры, переданные из внешнего интерфейса:

import { IsString, IsInt, MinLength, MaxLength } from "class-validator";
import { ApiModelProperty } from '@nestjs/swagger'

export class CreateCatDto {
  @ApiModelProperty()
  @IsString()
  @MinLength(10, {
    message: "Name is too short"
  })
  @MaxLength(50, {
    message: "Name is too long"
  })
  readonly name: string;
  
  @ApiModelProperty()
  @IsInt()
  readonly age: number;
  
  @ApiModelProperty()
  @IsString()
  readonly breed: string;
}

import { Controller, Post, Body } from '@nestjs/common';
import { CreateCatDto } from './dto';

@Controller('cats')
export class CatsController {
  @Post()
  create(@Body() createCatDto: CreateCatDto) {
    return 'This action adds a new cat';
  }
}

Если параметры в Body не соответствуют требованиям, об этом будет сообщено напрямуюValidation failedОшибка.

ORM

ORM — это аббревиатура от «Object/Relational Mapping», которая завершает работу реляционной базы данных с помощью синтаксиса объектов-экземпляров. Объектно-ориентированное программирование можно использовать для работы с реляционными базами данных через ORM.

В Java обычно есть слой DAO (Data Access Object, объект доступа к данным), который содержит различные методы работы с базой данных. С помощью его методов выполняются связанные операции с базой данных. Основная функция DAO — разделить бизнес-уровень и уровень данных, чтобы избежать связи между бизнес-уровнем и уровнем данных.

В Nestjs вы можете использовать TypeORM в качестве уровня DAO, который поддерживает MySQL/MariaDB/Postgres/CockroachDB/SQLite/Microsoft SQL Server/Oracle/MongoDB/NoSQL.

В typeORM таблица базы данных соответствует классу, а сущность создается путем определения класса. Entity — это класс, который сопоставляется с таблицей базы данных (или коллекцией при использовании MongoDB) через@Entity()помечать.

import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";

@Entity()
export class User {

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    firstName: string;

    @Column()
    lastName: string;

    @Column()
    age: number;

}

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

+-------------+--------------+----------------------------+
|                          user                           |
+-------------+--------------+----------------------------+
| id          | int(11)      | PRIMARY KEY AUTO_INCREMENT |
| firstName   | varchar(255) |                            |
| lastName    | varchar(255) |                            |
| isActive    | boolean      |                            |
+-------------+--------------+----------------------------+

использовать@InjectRepository()Декоратор вводит соответствующийRepository, вы можете использовать этоRepositoryВыполните некоторые операции с базой данных над объектом.

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
  ) {}

  async findAll(): Promise<User[]> {
    return await this.userRepository.find();
  }
}

Ссылаться на

Nestjs

Анализ инверсии управления

Понимание внедрения зависимостей и инверсии управления

От Express до Nestjs: расскажите об идеях дизайна и использовании Nestjs.