NestJS 核心概念,IOC 和 AOP 的运用
主要讲下 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,我们也可以指定值或动态的对象,分别对应 useValue
和 useFactory
,如下为 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 的运用相关推荐
- Spring核心机制IoC与AoP梳理
Spring核心机制IoC与AoP梳理 文章目录 Spring核心机制IoC与AoP梳理 IoC介绍 IoC案例介绍 pom文件中IoC环境引入 自己new对象方法举例(正转) IoC创建对象 基于X ...
- spring框架两大核心:IOC和AOP的详解
目录 一.IOC和AOP的介绍 二.IOC 1.IOC初始化属性的方式 1.使用构造方法完成属性初始化 2.使用type数据类型完成属性初始化 3.使用p命名空间初始化对象 4.使用c命名空间初始化对 ...
- Spring 框架核心概念IoC 随笔
IoC = DI(Dependency Injection ) + 生命周期钩子事件 + 外部配置 ... IoC核心设计模式 Factory 模式和 Observer 模式,强制使用 依赖注入 模式 ...
- IOC和AOP的概念
Spring的两大核心:IOC和AOP 一.IOC的概念 IOC 全称为 Inversion of Control,翻译为 "控制反转",它还有一个别名为 DI(Dependenc ...
- Spring的IOC和AOP思想
Spring框架的两大核心(ioc和aop) 一.ioc:控制反转(Inversion of Control)思想 1.1.由spring来负责控制对象的生命周期和对象间的关系(SSM框架中的依赖关系 ...
- 谈谈Spring中的IOC和AOP概念 - 倪升武的博客 - CSDN博客
转载于https://blog.csdn.net/eson_15/article/details/51090040 IOC和AOP是Spring中的两个核心的概念,下面谈谈对这两个概念的理解. 1. ...
- java spring原理详解,spring原理详解,两大核心IOC和AOP
大家好,我是java梦之旅,一个被Bug耽误了才艺的程序员,专注于Java领域的知识分享和技术交流,每天会给大家带来Java学习的干货教程,喜欢我的同学可以关注我,一起学习,一起加油! 1.概念:sp ...
- 手写spring简单实现转账--体会核心ioc和aop
文章目录 前言 spring是全栈的轻量级的开源框架,本文就通过一个简单的案例帮助大家理解其ioc和aop大致是如何实现的. 一.转账案例 首先我们设定一个需求:简单转账功能比如A向B转账 步骤: 首 ...
- IOC和AOP概念理解
IOC和AOP概念理解总结: IOC:控制反转 是一种降低对象之间耦合关系的设计思想,面试的时候最好能说出来个例子,加深理解.例子:租房子,以前租房子需要一个房子一个房子找,费时费力,然后现在加入一个 ...
最新文章
- 穿上这件全球首款「隐形衣」,做这条街最「无脸」的仔;阿里给钱给资源,求解AI安全难题...
- 常见加密算法分类,用途,原理以及比较
- 在centos7中静默安装oracle11g
- 20169207《Linux内核原理与分析》第五周作业
- TCP/IP协议基本概括+ARP协议详解+DNS协议详解---Linux学习笔记
- use regular expression instead of ABAP function module to parse attachment
- excel中怎样用公式获取表单控件_老会计不愿教的工资表汇总公式,真是太好用了...
- PHP 正在干掉 Python
- dubbo 支持服务降级吗_关于dubbo的服务降级
- javascript this的意思
- was java heap_Websphere产生大量javacore与heapdump文件的分析
- 测试唯一ID支持多大的并发量
- 基础教程之Running Man奔跑
- win10/win7 usb转串口驱动下载
- 关键绩效指标法(KPI)初步概念
- python课程设计小结和体会_通用版课程设计心得体会
- python学习笔记(六):if语句之处理数据
- 阿里云服务器遭ddos攻击防御案例
- 前后端分离之Web前端架构设计
- apk逆向思路_逆向练手——从一个apk简单逆向看消息摘要算法
热门文章
- php百度人脸检测api测颜值评分(源码直接可用)
- A Practical Guide to Support Vector Classication
- 糖果Y8 Max手机刷机原厂线刷包msm8917附教程
- 渗透测试基础-文件上传漏洞 (下)
- 【迁移攻击笔记】数据集の变化→提高迁移率!Improving Transferability of Adversarial Examples with Input Diversity
- python字符串反码输出_python 反码
- 行测-图形推理题分析
- 手机网速稳定性测试软件,什么手机软件可以监测或测网速的稳定性?
- Excel常用时间函数
- Turtlebot4入门教程-产品特征