主要讲下 NestJS 中 IOC 和 五种 AOP 的相关知识点。

Provider

可以先创建下项目

nest new xxx

providers 是可以注入的对象,我们可以把带有 @Injectable() 的 class 放到 Module 的 providers 里声明,因为 Nest 实现了 IOC,这样就会被它给识别到,从而实现依赖注入。

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';@Module({imports: [],controllers: [AppController],providers: [AppService],
})
export class AppModule {}

当然这是一种简写,原本是这样:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';@Module({imports: [],controllers: [AppController],providers: [{provide: AppService,useClass: AppService,},],
})
export class AppModule {}

这样就实现了依赖注入,我们就可以通过 @Inject() 在其他地方使用了,比如在 Controller 里使用

import { Controller, Get, Inject } from '@nestjs/common';
import { AppService } from './app.service';@Controller()
export class AppController {constructor(@Inject(AppService) private readonly appService: AppService) {}//或者直接写,不用构造器// @Inject(AppService) private readonly appService: AppService@Get()getHello(): string {return this.appService.getHello();}
}

但因为我们的 provide 和 useClass 是相同的,所以可以省略,所以可以简写成如下:

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();}
}

我们可以实验一下,把 provide 的值改为一个字符串

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';@Module({imports: [],controllers: [AppController],providers: [{provide: 'suemor',useClass: AppService,},],
})
export class AppModule {}

这样就没办法省略了,就得老老实实写 @Inject()

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

除了在 providers 里指定 class,我们也可以指定值或动态的对象,分别对应 useValueuseFactory,如下为 useFactory写法

import { Inject, Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';@Module({imports: [],controllers: [AppController],providers: [{provide: 'suemor',useClass: AppService,},{provide: 'suemor2',useFactory(appService: AppService) {return {age: 19,gender: 'male',say: appService.getHello(),};},inject: ['suemor'],},],
})
export class AppModule {}

修改 controller

import { Controller, Get, Inject } from '@nestjs/common';
import { AppService } from './app.service';@Controller()
export class AppController {constructor(@Inject('suemor') private readonly appService: AppService,@Inject('suemor2')private readonly user: { age: number; gender: string; say: string },) {}@Get()getHello() {return this.user;}
}

五种 AOP

​ 因为 NestJS 使用的 MVC 架构,所以有 AOP 的能力,其中 Nest 的实现主要包括如下五种(按执行顺序排列

  • Middleware
  • Guard
  • Pipe
  • Interceptor
  • ExceptionFilte

Middleware

即中间件,但这并不是 Nest 独有的,你用 Express 或者 Fastify 当请求的库他们本身也都拥有 middleware,概念基本相同,我们一般会用它处理些通用逻辑(如日志)。

nest g middleware logger --no-spec --flat

应该会生成如下代码,这里的 req 和 res 都是 any 是因为 Nest 并不知道你用的是 Express 还是 Fastify 当请求库,所以类型得自行补全。

import { Injectable, NestMiddleware } from '@nestjs/common';@Injectable()
export class LoggerMiddleware implements NestMiddleware {use(req: any, res: any, next: () => void) {next();}
}

修改类型,并添加下 console.log,然后我们得去注册这个 middleware

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

app.module.ts里全局注册

import { MiddlewareConsumer, Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { NestModule } from '@nestjs/common';
import { LoggerMiddleware } from './logger.middleware';@Module({imports: [],controllers: [AppController],providers: [{provide: 'suemor',useClass: AppService,},{provide: 'suemor2',useFactory(appService: AppService) {return {age: 19,gender: 'male',say: appService.getHello(),};},inject: ['suemor'],},],
})
export class AppModule implements NestModule {configure(consumer: MiddlewareConsumer) {consumer.apply(LoggerMiddleware).forRoutes('*');}
}

然后去浏览器访问,我们可以看到控制台正确输出了

Guard

即路由守卫的意思,一般在 Controller 之前鉴权,返回 true 和 false

nest g guard roles --no-spec --flat
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';@Injectable()
export class RolesGuard implements CanActivate {canActivate(context: ExecutionContext,): boolean | Promise<boolean> | Observable<boolean> {return true;}
}

修改 Controller,UseGuards 和 SetMetadata。

import {Controller,Get,Inject,SetMetadata,UseGuards,
} from '@nestjs/common';
import { AppService } from './app.service';
import { RolesGuard } from './roles.guard';@Controller()
export class AppController {constructor(@Inject('suemor') private readonly appService: AppService,@Inject('suemor2')private readonly user: { age: number; gender: string; say: string },) {}@Get()@UseGuards(RolesGuard)@SetMetadata('roles', ['admin'])getHello() {return this.appService.getHello();}
}

修改 RolesGuard,这里我们会引 Reflector 的 Metadata 概念,这目前还没有被 ES 标准化,还处于草案阶段,Nest 应该是使用 reflect-metadata 这个 polyfill 包,这可以说是 Nest 的核心了,它的 IOC 基本都是靠这个实现的,这里大意就是从 ExecutationContext 取到 handle,然后注入 reflector,通过 reflector.get 取出 handler 上的 metadata,这样就可以获取到当前路由的权限了。

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable } from 'rxjs';@Injectable()
export class RolesGuard implements CanActivate {constructor(private reflector: Reflector) {}canActivate(context: ExecutionContext,): boolean | Promise<boolean> | Observable<boolean> {console.log(this.reflector.get<string[]>('roles', context.getHandler())); // [ 'admin' ]return true;}
}

我们现在可以获得当前路由的权限了,那接下来就要用这个与请求的权限进行比较了,关于解析请求的权限要用到 @nestjs/passport库,就是通过 token 获取到当前的 user,但这不是本文重点,我们假设已经在 req 赋上 user 字段了,就可以通过 context.switchToHttp().getRequest().user 获取到当前的请求的 user 了,然后进行 if 判断下就可以了。

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable } from 'rxjs';@Injectable()
export class RolesGuard implements CanActivate {constructor(private reflector: Reflector) {}canActivate(context: ExecutionContext,): boolean | Promise<boolean> | Observable<boolean> {const routerRole = this.reflector.get<string[]>('roles',context.getHandler(),);const ReqUser = context.switchToHttp().getRequest().user;console.log(routerRole, ReqUser);if (routerRole[0] == ReqUser.role) {return true;}return false;}
}

Interceptor

即拦截器,它可以在 Controller 方法前后加入一些逻辑,其实和 Middleware 挺像的,也可以用来记录日志等,不过它也可以在 Controller 之后进行拦截,比如统一返回结果之类的。

nest g interceptor logging --flat --no-spec

它还有些优势就是可以使用 rxjs 的各种 operator,且也可以使用 reflector 和 ExecutionContext。

import {CallHandler,ExecutionContext,Injectable,NestInterceptor,
} from '@nestjs/common';
import { Observable, tap } from 'rxjs';@Injectable()
export class LoggingInterceptor implements NestInterceptor {intercept(context: ExecutionContext, next: CallHandler): Observable<any> {console.log('before');return next.handle().pipe(tap(() => console.log('after')));}
}

然后去 app 注册

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { LoggingInterceptor } from './logging.interceptor';async function bootstrap() {const app = await NestFactory.create(AppModule);app.useGlobalInterceptors(new LoggingInterceptor());await app.listen(3000);
}
bootstrap();

Pipe

即管道,可以对参数进行校验,比如我们说 age 不能是字符串,就可以这样写。

import { Controller, Get, Inject, ParseIntPipe, Query } from '@nestjs/common';
import { AppService } from './app.service';@Controller()
export class AppController {constructor(@Inject('suemor') private readonly appService: AppService) {}@Get()getHello2(@Query('age', ParseIntPipe) age: number) {return age;}
}

但实际业务我们一般会用 class-validator 这个库,它就是利用的 Pipe。

ExceptionFilter

即异常过滤器,它是很强大的,除了 middleware 以外,只要出现异常,都可以被它给捕获到。

import { ArgumentsHost, ExceptionFilter } from '@nestjs/common';
import { Catch, HttpException } from '@nestjs/common';@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {catch(exception: HttpException, host: ArgumentsHost) {const ctx = host.switchToHttp();const response = ctx.getResponse();const request = ctx.getRequest();const status = exception.getStatus();response.status(status).json({statusCode: status,timestamp: new Date().toISOString(),path: request.url,});}
}

去 app 注册

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { HttpExceptionFilter } from './any-exception.filter';async function bootstrap() {const app = await NestFactory.create(AppModule);app.useGlobalFilters(new HttpExceptionFilter());await app.listen(3000);
}
bootstrap();

我们 throw 个异常

import { BadRequestException, Controller, Get, Inject } from '@nestjs/common';
import { AppService } from './app.service';@Controller()
export class AppController {constructor(@Inject('suemor') private readonly appService: AppService) {}@Get()getHello2() {throw new BadRequestException();return this.appService.getHello();}
}

被正确捕获

NestJS 核心概念,IOC 和 AOP 的运用相关推荐

  1. Spring核心机制IoC与AoP梳理

    Spring核心机制IoC与AoP梳理 文章目录 Spring核心机制IoC与AoP梳理 IoC介绍 IoC案例介绍 pom文件中IoC环境引入 自己new对象方法举例(正转) IoC创建对象 基于X ...

  2. spring框架两大核心:IOC和AOP的详解

    目录 一.IOC和AOP的介绍 二.IOC 1.IOC初始化属性的方式 1.使用构造方法完成属性初始化 2.使用type数据类型完成属性初始化 3.使用p命名空间初始化对象 4.使用c命名空间初始化对 ...

  3. Spring 框架核心概念IoC 随笔

    IoC = DI(Dependency Injection ) + 生命周期钩子事件 + 外部配置 ... IoC核心设计模式 Factory 模式和 Observer 模式,强制使用 依赖注入 模式 ...

  4. IOC和AOP的概念

    Spring的两大核心:IOC和AOP 一.IOC的概念 IOC 全称为 Inversion of Control,翻译为 "控制反转",它还有一个别名为 DI(Dependenc ...

  5. Spring的IOC和AOP思想

    Spring框架的两大核心(ioc和aop) 一.ioc:控制反转(Inversion of Control)思想 1.1.由spring来负责控制对象的生命周期和对象间的关系(SSM框架中的依赖关系 ...

  6. 谈谈Spring中的IOC和AOP概念 - 倪升武的博客 - CSDN博客

    转载于https://blog.csdn.net/eson_15/article/details/51090040 IOC和AOP是Spring中的两个核心的概念,下面谈谈对这两个概念的理解. 1. ...

  7. java spring原理详解,spring原理详解,两大核心IOC和AOP

    大家好,我是java梦之旅,一个被Bug耽误了才艺的程序员,专注于Java领域的知识分享和技术交流,每天会给大家带来Java学习的干货教程,喜欢我的同学可以关注我,一起学习,一起加油! 1.概念:sp ...

  8. 手写spring简单实现转账--体会核心ioc和aop

    文章目录 前言 spring是全栈的轻量级的开源框架,本文就通过一个简单的案例帮助大家理解其ioc和aop大致是如何实现的. 一.转账案例 首先我们设定一个需求:简单转账功能比如A向B转账 步骤: 首 ...

  9. IOC和AOP概念理解

    IOC和AOP概念理解总结: IOC:控制反转 是一种降低对象之间耦合关系的设计思想,面试的时候最好能说出来个例子,加深理解.例子:租房子,以前租房子需要一个房子一个房子找,费时费力,然后现在加入一个 ...

最新文章

  1. 穿上这件全球首款「隐形衣」,做这条街最「无脸」的仔;阿里给钱给资源,求解AI安全难题...
  2. 常见加密算法分类,用途,原理以及比较
  3. 在centos7中静默安装oracle11g
  4. 20169207《Linux内核原理与分析》第五周作业
  5. TCP/IP协议基本概括+ARP协议详解+DNS协议详解---Linux学习笔记
  6. use regular expression instead of ABAP function module to parse attachment
  7. excel中怎样用公式获取表单控件_老会计不愿教的工资表汇总公式,真是太好用了...
  8. PHP 正在干掉 Python
  9. dubbo 支持服务降级吗_关于dubbo的服务降级
  10. javascript this的意思
  11. was java heap_Websphere产生大量javacore与heapdump文件的分析
  12. 测试唯一ID支持多大的并发量
  13. 基础教程之Running Man奔跑
  14. win10/win7 usb转串口驱动下载
  15. 关键绩效指标法(KPI)初步概念
  16. python课程设计小结和体会_通用版课程设计心得体会
  17. python学习笔记(六):if语句之处理数据
  18. 阿里云服务器遭ddos攻击防御案例
  19. 前后端分离之Web前端架构设计
  20. apk逆向思路_逆向练手——从一个apk简单逆向看消息摘要算法

热门文章

  1. php百度人脸检测api测颜值评分(源码直接可用)
  2. A Practical Guide to Support Vector Classication
  3. 糖果Y8 Max手机刷机原厂线刷包msm8917附教程
  4. 渗透测试基础-文件上传漏洞 (下)
  5. 【迁移攻击笔记】数据集の变化→提高迁移率!Improving Transferability of Adversarial Examples with Input Diversity
  6. python字符串反码输出_python 反码
  7. 行测-图形推理题分析
  8. 手机网速稳定性测试软件,什么手机软件可以监测或测网速的稳定性?
  9. Excel常用时间函数
  10. Turtlebot4入门教程-产品特征