Nest.js系列——Nest.js的生命周期与编程范式

2,478 阅读11分钟

大家好,我是water,欢迎关注我的公众号:前端进阶说,最近在看Nestjs相关内容就做个学习记录,希望对你有帮助。

前言

nest服务中,其实和前端开发中类似,也是拥有生命周期的。服务在一个生命周期中结束,中间会涉及到中间件、守卫等,最后经过控制器和服务处理然后返回到前端,这样一个请求的服务生命周期结束。而且在nest中还有一些开发的核心概念和范式,这些对于一些不是后端的小伙伴非常重要,这里本着学习的态度对这些温习一下。

image.png

nest中涉及的编程范式

OOP

面向对象编程(OOP)是一种编程范式,它将数据和操作数据的方法组合在一起,以便将对象视为单个实体。对象的设计包括属性和方法,属性是对象的特征,而方法是对象的行为。OOP的主要优点是代码重用性、灵活性和可维护性。

OOP的四个基本概念是:

  1. 封装(Encapsulation):将数据和方法组合在一起,以便将对象视为单个实体,并保护其内部状态。封装可以防止外部代码直接访问对象的数据,从而提高了代码的安全性和可维护性。
  2. 继承(Inheritance):允许创建一个新类,该类从现有类继承属性和方法。继承可以减少代码重复,并使代码更易于维护。
  3. 多态(Polymorphism):允许使用相同的方法来处理不同类型的对象。多态可以提高代码的灵活性和可扩展性。
  4. 抽象(Abstraction):将复杂的现实世界建模为类和对象,以便更好地理解和管理代码。抽象可以隐藏对象的复杂性,并使代码更易于理解和维护。

js中所有东西都是对象,包括数字、字符串、函数和类。js支持OOP,因此可以使用类和对象来编写面向对象的代码。

FP

函数式编程(FP)是一种编程范式,它将计算视为数学函数的求值,并避免使用可变状态和可变数据。FP的主要优点是代码可读性、可维护性和可扩展性。

FP的基本概念是:

  1. 纯函数(Pure Function):不依赖于外部状态或数据的函数,其输出仅由输入决定。纯函数可以避免副作用和不可预测性,并提高代码的可读性和可维护性。
  2. 不可变性(Immutability):避免使用可变状态和可变数据,以便更好地控制代码的行为和状态。不可变性可以提高代码的可读性、可维护性和可扩展性。
  3. 高阶函数(Higher-Order Function):接受一个或多个函数作为参数或返回一个函数的函数。高阶函数可以提高代码的抽象级别和可复用性。

js中,函数是一等公民,可以像其他对象一样传递和操作。js支持FP,因此可以使用函数和高阶函数来编写函数式的代码。

FRP

函数响应式编程(Functional Reactive Programming,FRP)是一种编程范式,它结合了函数式编程和响应式编程的概念。FRP的主要思想是将数据流看作是连续的时间变化的信号,并使用函数来处理这些信号。FRP的主要优点是代码可读性、可维护性和可扩展性。在js中,可以使用第三方库Rxjs来实现FRP

AOP

AOP(Aspect-Oriented Programming)是一种编程范式,它的主要思想是将程序的功能分解成不同的关注点(Aspect),然后通过横向切割(Cross-Cutting Concerns)的方式将这些关注点分离出来。AOP的主要优点是代码可读性、可维护性和可扩展性。使用AOP的好处是:

  1. 在业务外增加新功能,解耦

  2. 扩展功能方便,不影响业务之间的逻辑

  3. 逻辑集中管理

  4. 更有利于代码的复用

  5. AOP能在不破坏封装功能的前提下,额外增加功能

IOC

控制反转(Inversion of Control,IoC)是一种编程思想,它的主要思想是将程序的控制权从程序代码中转移到外部容器中,由容器来管理程序的生命周期和依赖关系。IoC的主要优点是代码可读性、可维护性和可扩展性。

IOC是一种思想&设计模式

DI

依赖注入(Dependency Injection,DI)是一种编程思想,它的主要思想是将程序的依赖关系从程序代码中移除,由外部容器来管理程序的依赖关系。DI的主要优点是代码可读性、可维护性和可扩展性。

DIIOC的具体实现

控制反转是一种面向对象编程中的一种设计原则,用来减低计算机代码之间的耦合度,其基本思想是:借助于“第三方”实现具有依赖关系的对象之间的解耦

依赖注入是一种用于实现ioc的设计模式,它允许类外创建依赖对象,并通过不同的方式将这些对象提供给类。

nest生命周期

image.png

nest核心概念

中间件

NestJS中的中间件是一种用于处理HTTP请求的函数,它可以在请求到达控制器之前或之后执行一些操作。中间件可以用于实现身份验证、日志记录、错误处理等功能。在NestJS中,中间件可以是全局的,也可以是局部的。以下是一个中间件的示例代码:

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response } from 'express';
​
​
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: Function) {
    console.log('Request...');
    next();
  }
}

在上面的代码中,我们定义了一个名为LoggerMiddleware的中间件类,它实现了NestMiddleware接口。在use()方法中,我们打印了一条日志,并调用了next()函数,表示请求可以继续向下执行。要在应用程序中使用中间件,我们需要将其添加到模块或控制器中。以下是一个将中间件添加到模块中的示例代码:

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from './logger.middleware';
​
​
@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes('*');
  }
}

在上面的代码中,我们将LoggerMiddleware中间件添加到了AppModule模块中,并使用forRoutes('*')方法指定了该中间件适用于所有路由。这样,每次请求到达控制器之前,都会先执行LoggerMiddleware中间件中的代码。

守卫

NestJS中的守卫(Guard)是一种用于保护路由的机制,它可以在请求到达控制器之前或之后执行一些操作。守卫可以用于实现身份验证、权限控制、缓存等功能。在NestJS中,守卫可以是全局的,也可以是局部的。以下是一个守卫的示例代码:

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);
  }
}
​
​
function validateRequest(request: Request): boolean {
  // 验证请求是否合法
  return true;
}

在上面的代码中,我们定义了一个名为AuthGuard的守卫类,它实现了CanActivate接口。在canActivate()方法中,我们获取了请求对象,并调用了validateRequest()函数来验证请求是否合法。如果请求合法,就返回true,否则返回false。要在控制器中使用守卫,我们需要将守卫添加到路由上。以下是一个将守卫添加到路由上的示例代码:

import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from './auth.guard';
​
​
@Controller()
export class AppController {
  @Get()
  @UseGuards(AuthGuard)
  getHello(): string {
    return 'Hello World!';
  }
}

在上面的代码中,我们将AuthGuard守卫添加到了路由上,并使用@UseGuards()装饰器来指定守卫的类名。这样,每次请求到达控制器之前,都会先执行AuthGuard守卫中的代码,以确保请求的合法性。

拦截器

NestJS中的拦截器(Interceptor)是一种用于处理HTTP请求和响应的函数,它可以在请求到达控制器之前或之后执行一些操作。拦截器可以用于实现日志记录、错误处理、数据转换等功能。在NestJS中,拦截器可以是全局的,也可以是局部的。以下是一个拦截器的示例代码:

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
​
​
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    console.log('Before...');
    const now = Date.now();
    return next
      .handle()
      .pipe(
        tap(() => console.log(`After... ${Date.now() - now}ms`)),
      );
  }
}

在上面的代码中,我们定义了一个名为LoggingInterceptor的拦截器类,它实现了NestInterceptor接口。在intercept()方法中,我们打印了一条日志,并记录了当前时间。然后,我们调用了next.handle()函数,表示请求可以继续向下执行。在请求处理完成后,我们又打印了一条日志,并计算了请求处理的时间。要在应用程序中使用拦截器,我们需要将其添加到模块或控制器中。以下是一个将拦截器添加到模块中的示例代码:

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggingInterceptor } from './logging.interceptor';
​
​
@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes('*');
  }
}

controller

NestJS中的控制器(Controller)是用于处理HTTP请求的类,它们负责处理来自客户端的请求并返回响应。控制器可以使用装饰器(Decorator)来定义路由、请求方法、中间件等。在NestJS中,控制器是一个普通的类,它可以使用依赖注入(Dependency Injection)来获取其他服务(Service)的实例。以下是一个控制器的示例代码:

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
​
​
@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}
​
​
  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}

在上面的代码中,我们定义了一个名为AppController的控制器类,它使用了@Controller()装饰器来定义路由。在构造函数中,我们使用了依赖注入来获取AppService的实例。在getHello()方法中,我们调用了AppServicegetHello()方法并返回了它的返回值。

service

service层负责提供方法和操作,只包含业务逻辑

NestJS中的服务(Service)是用于处理业务逻辑的类,它们负责处理控制器(Controller)发来的请求并返回响应。服务可以使用依赖注入(Dependency Injection)来获取其他服务的实例。在NestJS中,服务是一个普通的类,它可以使用@Injectable()装饰器来标记自己是一个服务。以下是一个服务的示例代码:

import { Injectable } from '@nestjs/common';
​
​
@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}

在上面的代码中,我们定义了一个名为AppService的服务类,它使用了@Injectable()装饰器来标记自己是一个服务。在getHello()方法中,我们返回了一个字符串'Hello World!'。在控制器中,我们可以使用依赖注入来获取AppService的实例并调用它的方法。

过滤器

NestJS中的过滤器(Filter)是一种用于处理HTTP请求和响应的函数,它可以在请求到达控制器之前或之后执行一些操作。过滤器可以用于实现数据转换、错误处理、响应格式化等功能。在NestJS中,过滤器可以是全局的,也可以是局部的。以下是一个过滤器的示例代码:

import { ExceptionFilter, Catch, ArgumentsHost } 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,
        timestamp: new Date().toISOString(),
        path: request.url,
      });
  }
}

在上面的代码中,我们定义了一个名为HttpExceptionFilter的过滤器类,它实现了ExceptionFilter接口。在catch()方法中,我们获取了请求对象和响应对象,并将异常的状态码和请求的URL添加到响应中。要在应用程序中使用过滤器,我们需要将其添加到模块或控制器中。以下是一个将过滤器添加到模块中的示例代码:

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { HttpExceptionFilter } from './http-exception.filter';
​
​
@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes('*');
  }
}

在上面的代码中,我们将HttpExceptionFilter过滤器添加到了模块中,并使用@Catch()装饰器来指定过滤器要捕获的异常类型

小结

以上就是nestjs中涉及到的一些前端不太熟悉的概念,这里以学习的态度进行归纳整理,希望对你有帮助。