Nest.js 名词概念介绍

目录

1. 初识 Nest.js

1.1 什么是 Nest.js

1.2 Nest.js 优点

2. Nest.js 核心概念

2.1 控制器 Controller

2.2 提供者 Provider

2.3 模块 Module

2.4 中间件 Middleware

2.5 异常过滤器 Filter

2.6 管道 Pipe

2.7 守卫 Guard

2.8 拦截器 Interceptor

2.9 装饰器

2.10 路由

2.11 AOP(Aspect Oriented Programming)

3. Nest.js 内部层级关系

4. 对象属性中的特性值 vs 装饰器


1. 初识 Nest.js

1.1 什么是 Nest.js

用于构建 高效 / 可伸缩的  服务端  应用程序的 渐进式 Node.js 框架

1.2 Nest.js 优点

  1. 支持 TypeScript,使语法更加规范
  2. 兼容 Express 中间件,Express 是最早出现的轻量级的 node server端框架
  3. 层层处理,可以约束代码,比如何时使用中间件、使用 Guards守卫 等
  4. 依赖注入及模块化的思想,提供了完整的 MVC 链路,使代码结构清晰,便于维护

2. Nest.js 核心概念

2.1 控制器 Controller

客户端的请求,最终交给 哪个函数 / 模块 ,都需要通过预先处理,直接处理客户端请求(路由、方法等)的模块,称之为 控制器

控制器原理:

  1. 控制器 —— 接收应用的特定请求
  2. 路由机制 —— 控制哪个控制器接收哪些请求
  3. 控制器及路由的关系 —— 一个控制器有多个路由,不同路由 执行 不同操作

 

2.2 提供者 Provider

几乎所有的东西都可以被认为是提供者(例如:service, repository, factory, helper...),提供者的本质:用 @Injectable() 装饰器 注解的简单类

提供者 Provider 可以通过 constructor 注入依赖关系(即创建各种关系)

【控制反转】是面向对象编程中的一种 设计原则,用来降低代码耦合度

通过控制反转,对象在被创建的时候,有一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它

也可以说,通过控制反转,依赖被注入到对象

【依赖注入】把有依赖关系的类放到容器中,解析出这些类的实例,进而实现类的解耦

举个栗子:Class A 中用到了 Class B 的对象 b,一般情况下,需要在 A 的代码中显式的 new 一个 B 的对象。采用依赖注入技术之后,A 的代码只需要定义一个私有的 B 对象,不需要直接 new 来获得这个对象,而是通过相关的容器控制程序来将 B 对象在外部 new 出来并注入到 A 类里的引用中

【控制反转和依赖注入的关系】

  • 依赖注入(Dependency Injection,简称 DI)
  • 控制反转(Inversion of Control,简称 IoC)

DI 是实现 IoC 的一种常见方式

举个栗子(摘抄):假设你是一个想开公司的富二代,开公司首先需要一间办公室。那么你不用自己去买,你只需要在你的清单列表上写上办公室这么一项,那么,你老爸已经派人给你安排好了办公室,这间办公室长什么样?多大?在哪里?是租的?还是买的?你根本不知道,你也不需要知道。 现在你又在清单上写了需要 80 台办公电脑,你老爸又给你安排好了 80 台, 你自己并不需要关心这些电脑是什么配置,买什么样的 CPU 性价比更高,只要他们是能办公的电脑就行了。

这里你的老爸就是所谓的 IoC 容器,你在编写 Company 这个 Class 的时候,你内部用到的 Office、Computers 对象不需要你自己导入和实例化,你只需要在 Company 这个类的 Constructor (构造函数) 中声明你需要的对象,IoC 容器会帮你把所依赖的对象实例注入进去。

Nest 就是建立在 依赖注入 这种设计模式之上的

它在框架内部封装了一个 IoC 容器来管理所有的依赖关系

2.3 模块 Module

模块是用 @Module() 装饰器 注解的简单类

@Module() 装饰器提供了 元数据,Nest 用它来 组织应用程序结构

每个 Nest 应用程序至少有一个模块 —— 根模块 

  • 当应用很小时,根模块可能是应用程序中唯一的模块
  • 当应用很大时,应用将会拥有多个模块,每个模块都有一组紧密相关的功能

模块的好处:业务低耦合、边界清晰、便于排查错误、便于维护

@module() 装饰器接受一个 描述模块属性的对象

@Module({
  providers: [UserService],
  controllers: [UserController],
  imports: [OrderModule],
  exports: [UserService],
})
export class UserModule {}
  • providers:服务提供者列表,本模块可用,会自动注入
  • controllers:控制器列表,本模块可用,会绑定路由访问
  • imports:本模块导入的模块,如需要使用其他模块的 provider,则必须导入其他模块
  • exports:本模块导出的服务提供者,在此处定义的 provider 才能被其他模块使用

2.4 中间件 Middleware

中间件 —— 客户端 和 路由处理 的中间,在路由处理程序之前,调用的 函数

中间件函数可以访问:请求对象、响应对象、请求响应周期中的 next() 中间件函数

next() 中间件函数,通常由名为 next 的变量表示,他决定了请求-响应的循环系统

Nest 中间件,可以是一个函数,也可以是一个带有 @Injectable() 装饰器 的类

【中间件函数的任务】

  1. 执行任何代码
  2. 对请求和响应对象进行更改
  3. 结束请求-响应周期
  4. 调用堆栈中的下一个中间件函数
  5. 如果当前中间件函数,没有结束 请求-响应周期,它必须调用 next() 将控制传递给下一个中间件函数,否则,请求将被挂起

2.5 异常过滤器 Filter

当项目中出现了异常,而代码中却没有处理,那么这个异常就会到 Nest.js 内建的异常处理层,它能将异常更友好地响应给前端

当异常无法识别时 (既不是 HttpException 也不是继承 HttpException 的类 ) , 将会收到以下 JSON 响应:

{
    "statusCode": 500,
    "message": "Internal server error"
}

2.6 管道 Pipe

管道本质:一个实现了 PipeTransform 接口,并用 @Injectable() 装饰器 修饰的类

管道作用:

  1. 转换:将输入数据转换为所需的格式输出
  2. 验证:验证输入的内容是否满足预先定义的规则,当数据不正确时可能会抛出异常

举个栗子:把参数转化成十进制的整型数字 的管道 ParseIntPipe

// 注意下方 使用的装饰器 @Injectable() 及 实现的接口 PipeTransform
@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;
  }
}

对于 get 请求中的参数 id,调用 new ParseIntPipe 将 参数 id 转化成十进制的整数

@Get(':id')
async findOne(@Param('id', new ParseIntPipe()) id) {
  return await this.catsService.findOne(id);
}

2.7 守卫 Guard

守卫:实现权限验证,比如验证请求是否包含 token 或者 token 是否过期

与 中间件 Middleware 相比,守卫 Guard 能获得更详细的关于请求的执行上下文信息

通常 Guards,位于 Middleware 之后,Pipe 之前(请求正式被处理函数处理之前)

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);
  }
}
export interface ArgumentsHost {
  getArgs<T extends Array<any> = any[]>(): T;
  getArgByIndex<T = any>(index: number): T;
  switchToRpc(): RpcArgumentsHost;
  switchToHttp(): HttpArgumentsHost;
  switchToWs(): WsArgumentsHost;
}
export interface ExecutionContext extends ArgumentsHost {
  getClass<T = any>(): Type<T>;
  getHandler(): Function;
}

2.8 拦截器 Interceptor

拦截器本质:一个实现了 NestInterceptor 接口,并用 @Injectable() 装饰器 修饰的类

拦截器作用:

  1. 在函数执行前/后,绑定额外逻辑
  2. 转换从函数返回的结果
  3. 转换从函数抛出的异常
  4. 重写函数

举个栗子:对所有接口返回的 数据结构 处理(应用拦截器的)

2.9 装饰器

装饰器 —— 一种特殊类型的声明,本质上就是一个 方法

装饰器可以注入到类、类的方法、类的属性、类的参数上,用于扩展功能

装饰器的分类:

  1. 类的装饰器
  2. 类方法的装饰器
  3. 类函数参数的装饰器
  4. 类的属性的装饰器

举个栗子:项目代码 main.ts 页面(应用装饰器)

2.10 路由

回顾:控制器 Controllar 用于接收应用程序的特定请求,基于 路由机制 来实现请求分发;每个控制器对应多个路由,不同的路由可以执行不同的动作

回顾:路由映射(将请求绑定到相应的控制器)

【路由指向、全局路由前缀】 

以 src/main.ts 为例:

  • await NestFactory.create(AppModule) —— 使用 Nest 的工厂函数创建 AppModule
  • await app.listen(3000) —— 监听 3000 端口,可自定义
  • http://localhost:3000/thsapp/
    思考:thsapp 由来?输出结果的由来?—— 全局路由前缀
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  // 使用 Nest 的工厂函数创建 AppModule
  const app = await NestFactory.create(AppModule);
  // 设置全局路由前缀
  app.setGlobalPrefix('thsapp'); // 全局路由前缀
  // 监听 3000 端口,可自定义
  await app.listen(3000);
}

bootstrap();

【局部路由前缀】 

http://localhost:3000/thsapp/user
思考:use 由来?输出结果的由来?

import {Controller, Get} from '@nestjs/common';

@Controller('user')
export class User2Controller {

  // http://localhost:3000/thsapp/user
  @Get()
  async getUserInfo() {
    return '我是用户信息';
  }
  // http://localhost:3000/thsapp/user/info
  // @Get('info')
  // async getUserInfo() {
  //   return '获取用户信息';
  // }
}
// src/app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.services';

@Controller()
export class AppController {
    constructor(private readonly appService: AppService) {}

    @Get()
    getHello(): string {
        return this.appService.getHello();
    }
}
// src/app.service.ts
import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
    getHello(): string {
        return 'Hello World!';
    }
}

2.11 AOP(Aspect Oriented Programming)

面向切面编程 —— 通过 预编译方式 和 运行期间动态代理 实现程序功能的统一维护的一种技术(在运行时,动态地将代码切入到类的指定方法、指定位置上)

切入点:指定类/指定方法 切入到的 代码片段 称为切面,切入到哪些类、哪些方法

AOP 可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为

AOP 优点:

  1. 降低业务逻辑各部分之间的耦合度
  2. 提高程序的可重用性
  3. 提高了开发的效率
  4. 提高代码的灵活性和可扩展性

将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来

通过对这些行为的分离,将它们独立到非指导业务逻辑的方法中,进而实现改变这些行为时,不影响业务逻辑的代码

3. Nest.js 内部层级关系

4. 对象属性中的特性值 vs 装饰器

ES5 中,对象中的每个属性都有 特性值 来描述这个属性的特点,他们分别是:

  • configurable:属性是否能被 delete 删除,当值为 false 时,其他特性值也不能被改变,默认值为 true
  • enumerable:属性是否能被枚举,也就是是否能被 for in 循环遍历,默认为 true
  • writable:属性值是否能修改,默认为 true
  • value:属性值具体是多少,默认为 undefined
  • get:当通过 person.name 访问 name 的属性值时,get() 将被调用,get() 可以自定义返回的具体值是多少,get 默认值为 undefined
  • set:当通过 person.name = 'Jake' 设置 name 的属性值时,set() 将被调用,set() 可以自定义设置值的具体方式,set 默认值为 undefined

注意:不能同时设置value、writeable 与 get、set

可以通过 Object.defineProperty(操作单个) Object.defineProperties(操作多个) 来修改这些特性值

上述方法的参数:

  • target(对象)
  • key(对象属性)
  • descriptor(对象属性的 特性值的 描述对象)
var person = {
  name: "Lily",
};

// 修改
Object.defineProperty(person, "name", {
  value: "Lucy",
});

// 新增
Object.defineProperty(person, "age", {
  value: 20,
});

下面写一个装饰器函数 nameDecorator()

函数 nameDecorator(),会重写被他装饰的属性,函数最后必须返回 descriptor

function nameDecorator(target, key, descriptor) {
  descriptor.value = () => {
    return "Tom";
  };
  return descriptor;
}

@nameDecorator,就是装饰器语法

自定义函数 nameDecorator 的参数中,target(被装饰的对象Person),key(被装饰的具体方法 getName())

getName() 方法的三个参数,与 Object.defineProperty 一一对应,分别指当前的对象Person,被作用的属性 getName(),以及属性特性值的描述对象 descriptor

函数 nameDecorator(),会重写被他装饰的属性 getName()

class Person {
  constructor() {
    this.name = "Lily";
  }
  @nameDecorator
  getName() {
    return this.name;
  }
}

let p1 = new Person();
console.log(p1.getName());

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lyrelion

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值