Nest.js 是一个 Node.js 的后端框架,它对 express 等 http 平台做了一层封装,解决了架构问题。它提供了 express 没有的 MVC、IOC、AOP 等架构特性,使得代码更容易维护、扩展。

这里的 MVC、IOC、AOP 都是啥意思呢?我们分别看一下:

MVC、IOC

MVC 是 Model View Controller 的简写。MVC 架构下,请求会先发送给 Controller,由它调度 Model 层的 Service 来完成业务逻辑,然后返回对应的 View。

Nest.js 提供了 @Controller 装饰器用来声明 Controller:

而 Service 会用 @Injectable 装饰器来声明:

通过 @Controller、@Injectable 装饰器声明的 class 会被 Nest.js 扫描,创建对应的对象并加到一个容器里,这些所有的对象会根据构造器里声明的依赖自动注入,也就是 DI(dependency inject),这种思想叫做 IOC(Inverse Of Control)。

IOC 架构的好处是不需要手动创建对象和根据依赖关系传入不同对象的构造器中,一切都是自动扫描并创建、注入的。

此外,Nest.js 还提供了 AOP (Aspect Oriented Programming)的能力,也就是面向切面编程的能力:

AOP

AOP 是什么意思呢?什么是面向切面编程呢?

一个请求过来,可能会经过 Controller(控制器)、Service(服务)、Repository(数据库访问) 的逻辑:

如果想在这个调用链路里加入一些通用逻辑该怎么加呢?比如日志记录、权限控制、异常处理等。

容易想到的是直接改造 Controller 层代码,加入这段逻辑。这样可以,但是不优雅,因为这些通用的逻辑侵入到了业务逻辑里面。能不能透明的给这些业务逻辑加上日志、权限等处理呢?

那是不是可以在调用 Controller 之前和之后加入一个执行通用逻辑的阶段呢?

比如这样:

这样的横向扩展点就叫做切面,这种透明的加入一些切面逻辑的编程方式就叫做 AOP (面向切面编程)。

AOP 的好处是可以把一些通用逻辑分离到切面中,保持业务逻辑的存粹性,这样切面逻辑可以复用,还可以动态的增删

其实 Express 的中间件的洋葱模型也是一种 AOP 的实现,因为你可以透明的在外面包一层,加入一些逻辑,内层感知不到。

而 Nest.js 实现 AOP 的方式更多,一共有五种,包括 Middleware、Guard、Pipe、Inteceptor、ExceptionFilter:

Middleware

Nest.js 基于 Express 自然也可以使用中间件,但是做了进一步的细分,分为了全局中间件和路由中间件:

全局中间件就是 Express 的那种中间件,在请求之前和之后加入一些处理逻辑,每个请求都会走到这里:

路由中间件则是针对某个路由来说的,范围更小一些:

这个是直接继承了 Express 的概念,比较容易理解。

再来看一些 Nest.js 扩展的概念,比如 Guard:

Guard

Guard 是路由守卫的意思,可以用于在调用某个 Controller 之前判断权限,返回 true 或者 flase 来决定是否放行:

创建 Guard 的方式是这样的:

Guard 要实现 CanActivate 接口,实现 canActive 方法,可以从 context 拿到请求的信息,然后做一些权限验证等处理之后返回 true 或者 false。

通过 @Injectable 装饰器加到 IOC 容器中,然后就可以在某个 Controller 启用了:

Controller 本身不需要做啥修改,却透明的加上了权限判断的逻辑,这就是 AOP 架构的好处。

而且,就像 Middleware 支持全局级别和路由级别一样,Guard 也可以全局启用:

Guard 可以抽离路由的访问控制逻辑,但是不能对请求、响应做修改,这种逻辑可以使用 Interceptor:

Interceptor

Interceptor 是拦截器的意思,可以在目标 Controller 方法前后加入一些逻辑:

创建 Inteceptor 的方式是这样的:

Interceptor 要实现 NestInterceptor 接口,实现 intercept 方法,调用 next.handle() 就会调用目标 Controller,可以在之前和之后加入一些处理逻辑。

Controller 之前之后的处理逻辑可能是异步的。Nest.js 里通过 rxjs 来组织它们,所以可以使用 rxjs 的各种 operator。

Interceptor 支持每个路由单独启用,只作用于某个 controller,也同样支持全局启用,作用于全部 controller:

除了路由的权限控制、目标 Controller 之前之后的处理这些都是通用逻辑外,对参数的处理也是一个通用的逻辑,所以 Nest.js 也抽出了对应的切面,也就是 Pipe:

Pipe

Pipe 是管道的意思,用来对参数做一些验证和转换:

创建 Pipe 的方式是这样的:

Pipe 要实现 PipeTransform 接口,实现 transform 方法,里面可以对传入的参数值 value 做参数验证,比如格式、类型是否正确,不正确就抛出异常。也可以做转换,返回转换后的值。

内置的有 8 个 Pipe,从名字就能看出它们的意思:

  • ValidationPipe

  • ParseIntPipe

  • ParseBoolPipe

  • ParseArrayPipe

  • ParseUUIDPipe

  • DefaultValuePipe

  • ParseEnumPipe

  • ParseFloatPipe

同样,Pipe 可以只对某个路由生效,也可以对每个路由都生效:

不管是 Pipe、Guard、Interceptor 还是最终调用的 Controller,过程中都可以抛出一些异常,如何对某种异常做出某种响应呢?

这种异常到响应的映射也是一种通用逻辑,Nest.js 提供了 ExceptionFilter 来支持:

ExceptionFilter

ExceptionFilter 可以对抛出的异常做处理,返回对应的响应:

创建 ExceptionFilter的形式是这样的:

首先要实现 ExceptionFilter 接口,实现 catch 方法,就可以拦截异常了,但是要拦截什么异常还需要用 @Catch 装饰器来声明,拦截了异常之后,可以异常对应的响应,给用户更友好的提示。

当然,也不是所有的异常都会处理,只有继承 HttpException 的异常才会被 ExceptionFilter 处理,Nest.js 内置了很多 HttpException 的子类:

  • BadRequestException

  • UnauthorizedException

  • NotFoundException

  • ForbiddenException

  • NotAcceptableException

  • RequestTimeoutException

  • ConflictException

  • GoneException

  • PayloadTooLargeException

  • UnsupportedMediaTypeException

  • UnprocessableException

  • InternalServerErrorException

  • NotImplementedException

  • BadGatewayException

  • ServiceUnavailableException

  • GatewayTimeoutException

当然,也可以自己扩展:

Nest.js 通过这样的方式实现了异常到响应的对应关系,代码里只要抛出不同的 HttpException,就会返回对应的响应,很方便。

同样,ExceptionFilter 也可以选择全局生效或者某个路由生效:

某个路由:全局:

我们了解了 Nest.js 提供的 AOP 的机制,但它们的顺序关系是怎样的呢?

几种 AOP 机制的顺序

Middleware、Guard、Pipe、Interceptor、ExceptionFilter 都可以透明的添加某种处理逻辑到某个路由或者全部路由,这就是 AOP 的好处。

但是它们之间的顺序关系是什么呢?

调用关系这个得看源码了。

对应的源码是这样的:

很明显,进入这个路由的时候,会先调用 Guard,判断是否有权限等,如果没有权限,这里就抛异常了:

抛出的 HttpException 会被 ExceptionFilter 处理。

如果有权限,就会调用到拦截器,拦截器组织了一个链条,一个个的调用,最后会调用的 controller 的方法:

调用 controller 方法之前,会使用 pipe 对参数做处理:

会对每个参数做转换:

ExceptionFilter 的调用时机很容易想到,就是在响应之前对异常做一次处理。

而 Middleware 是 express 中的概念,Nest.js 只是继承了下,那个是在最外层被调用。

这就是这几种 AOP 机制的调用顺序。把这些理清楚,就算是对 Nest.js 有很好的掌握了。

总结

Nest.js 基于 express 这种 http 平台做了一层封装,应用了 MVC、IOC、AOP 等架构思想。

MVC 就是 Model、View Controller 的划分,请求先经过 Controller,然后调用 Model 层的 Service、Repository 完成业务逻辑,最后返回对应的 View。

IOC 是指 Nest.js 会自动扫描带有 @Controller、@Injectable 装饰器的类,创建它们的对象,并根据依赖关系自动注入它依赖的对象,免去了手动创建和组装对象的麻烦。

AOP 则是把通用逻辑抽离出来,通过切面的方式添加到某个地方,可以复用和动态增删切面逻辑。

Nest.js 的 Middleware、Guard、Interceptor、Pipe、ExceptionFileter 都是 AOP 思想的实现,只不过是不同位置的切面,它们都可以灵活的作用在某个路由或者全部路由,这就是 AOP 的优势。

我们通过源码来看了它们的调用顺序,Middleware 是 Express 的概念,在最外层,到了某个路由之后,会先调用 Guard,Guard 用于判断路由有没有权限访问,然后会调用 Interceptor,对 Contoller 前后扩展一些逻辑,在到达目标 Controller 之前,还会调用 Pipe 来对参数做验证和转换。所有的 HttpException 的异常都会被 ExceptionFilter 处理,返回不同的响应。

Nest.js 就是通过这种 AOP 的架构方式,实现了松耦合、易于维护和扩展的架构。

AOP 架构的好处,你感受到了么?

- END -

关于奇舞团

奇舞团是 360 集团最大的大前端团队,代表集团参与 W3C 和 ECMA 会员(TC39)工作。奇舞团非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。

Nest.js 是如何实现 AOP 架构的?相关推荐

  1. Notadd —— 基于 nest.js 的微服务开发框架

    2019独角兽企业重金招聘Python工程师标准>>> 前言 随着 node.js 社区的不断发展,虽然有 express .koa .egg .... 这样的框架,或者更应该称之为 ...

  2. 1. Nest Js

    第一步 在这一组文章中, 您将了解 Nest 的核心基础知识.为了了解基本的 nest 应用程序构建模块,我们将构建一个基本的 CRUD 应用程序, 其中的涵盖了大量的基础功能. 语言 我们爱上了 T ...

  3. Nest.js 名词概念介绍

    目录 1. 初识 Nest.js 1.1 什么是 Nest.js 1.2 Nest.js 优点 2. Nest.js 核心概念 2.1 控制器 Controller 2.2 提供者 Provider ...

  4. 【好文翻译】Difference Between Next.js vs. Nuxt.js vs. Nest.js

    Difference Between Next.js vs. Nuxt.js vs. Nest.js 编程可以被认为是创造一些令人惊奇和信息事物的过程.如果你是一个程序员,你必须同每项新兴前沿技术保持 ...

  5. .NET面向上下文、AOP架构模式(实现)

    .NET面向上下文.AOP架构模式(实现) 1.上下文Context.面向切面编程AOP模型分析 在本人的.NET面向上下文.AOP架构模式(概述)一文中,我们大概了解了上下文如何辅助对象在运行时的管 ...

  6. js return 后 运行 另_新手入门Nest.js(六) 控制器Resources、路由通配符

    点击上方公众号,可快速关注 作者:鲲鹏友人原文链接:https://www.gowhich.com/blog/1067 Nest.js控制中的Resources 前面介绍了路由中如何通过GET方式访问 ...

  7. nestjs配置MySQL数据库,Nest.js 中的数据库操作

    安装 Typeorm 为了与 SQL 和 NoSQL 数据库集成,Nest.js 提供了@nestjs/typeorm 软件包.Nest.js 使用 TypeORM,因为它是 TypeScript 最 ...

  8. js 加入debug后可以进入controller_新手入门Nest.js(四) 控制器路由

    点击上方公众号,可快速关注 作者:鲲鹏友人原文链接:https://www.gowhich.com/blog/1065 一个控制器的目的是接收来自应用的一个请求 路由机制控制了控制器接收哪个请求 通常 ...

  9. (转载)NET面向上下文、AOP架构模式(实现)

    1.上下文Context.面向切面编程AOP模型分析 在本人的.NET面向上下文.AOP架构模式(概述)一文中,我们大概了解了上下文如何辅助对象在运行时的管理.在很多时候我们急需在运行时能把对象控制在 ...

最新文章

  1. iOS属性之assign,copy,retain的区别以及weak和strong的区别
  2. [公告]博客园新服务器照片
  3. 推荐:Visual Basic.NET Windows Forms 编程
  4. 向js中添加静态方法与属性方法
  5. P4424-[HNOI/AHOI2018]寻宝游戏【结论】
  6. 信安教程第二版-第21章网络设备安全
  7. java可重复的集合_Java中是否存在任何无序,可重复的Collection类?
  8. CentOS7下RabbitMq安装和开机自启动配置
  9. 开源服务器 Jenkins 曝漏洞,可用于发动 DDoS 攻击
  10. 超星尔雅python_超星尔雅Python金融数据分析答案公众号
  11. iis服务器mp4不显示,IIS浏览器无法读取mp4视频的解决方法
  12. HTTPS的原理与具体实现过程及HTTP服务的虚拟目录简介
  13. robots文件的作用
  14. MCS-51系列单片机存储器结构
  15. 再说《提督的决断4》
  16. Appium+python自动化(六)- 连接模拟器并启动淘宝APP(超详解)
  17. 怎么给图片加滤镜?这三个实用方法教会你
  18. Ubuntu 系统备份 恢复
  19. 上海配眼镜的闭坑分享,配30副眼镜的资深眼镜控聊聊心得
  20. 什么是网络上的Cookies,您如何使用它们?

热门文章

  1. 工作10年,悟出的50条职场道理
  2. mysql 时区的影响
  3. 月薪没有五万的西二旗人是怎么活的?
  4. 淘宝/天猫API:item_question_answer-淘宝评论问答列表接口
  5. OPPO Reno标准版工信部入网:搭载骁龙710处理器
  6. LTS 轻量级分布式任务调度框架(Light Task Scheduler)
  7. 推荐系统系列——推荐系统的分类
  8. 副业项目分享,操作简单,兼职赚点零花钱
  9. 演义江湖PC端意见汇总
  10. 315晚会报道的无人机是怎么被劫持的?