AOP架构的应用
前言
后端框架基本都是 MVC(Model View Controller) 架构,在这个架构中,请求会先发送给Controller,由它调度Model层的Service来完成业务逻辑,然后返回对应的View。
在这个流程中,Nest还提供了 AOP(Aspect Oriented Programming)的能力,也就是面向切面编程的能力。切面编程可以让我们在请求链路中加入一些通用逻辑,例如日志记录,权限控制,异常处理等,并且可以保证业务逻辑的纯粹性。切面逻辑还可以复用和动态增删。
Nest一共有五种实现AOP的方式,分别是:Middleware
、Guard
、Pipe
、Interceptor
和ExceptionFilter
Middleware
Nest底层是express,所以也可以使用中间件,但做了进一步细分,分为全局中间件和路由中间件
全局中间件就是 Express 的那种中间件,在请求之前和之后加入一些处理逻辑,每个请求都会走到这里:
1 | const app = await NestFactory.create(AppModule); |
路由中间件则是针对某个路由来说的,范围更小一些:
1 | export class AppModule implements NestModule { |
Grard
Guard 是路由守卫的意思,可以用于在调用某个 Controller 之前判断权限,返回 true 或者 false 来决定是否放行:
1 | () |
Guard 要实现 CanActivate 接口,实现 canActivate 方法,可以从 context 拿到请求的信息,然后做一些权限验证等处理之后返回 true 或者 false。
通过 @Injectable 装饰器加到 IOC 容器中,然后就可以在某个 Controller 启用了:
1 | 'cats') ( |
Controller 本身不需要做啥修改,却透明的加上了权限判断的逻辑,这就是 AOP 架构的好处。
而且,就像 Middleware 支持全局级别和路由级别一样,Guard 也可以全局启用:
1 | const app = await NestFactory.create(AppModule) |
Guard 可以抽离路由的访问控制逻辑,但是不能对请求、响应做修改,这种逻辑可以使用 Interceptor:
Interceptor
Interceptor 是拦截器的意思,可以在目标 Controller 方法前后加入一些逻辑:
1 | import { Observable } from 'rxjs'; |
Interceptor 要实现 NestInterceptor 接口,实现 intercept 方法,调用 next.handle() 就会调用目标 Controller,可以在之前和之后加入一些处理逻辑。
Controller 之前之后的处理逻辑可能是异步的。Nest 里通过 rxjs 来组织它们,所以可以使用 rxjs 的各种 operator。
Interceptor 支持每个路由单独启用,只作用于某个 controller,也同样支持全局启用,作用于全部 controller:
1 | new LoggingInterceptor()) ( |
1 | const app = await NestFactory.create(ApplicationModule); |
除了路由的权限控制、目标 Controller 之前之后的处理这些都是通用逻辑外,对参数的处理也是一个通用的逻辑,所以 Nest 也抽出了对应的切面,也就是 Pipe:
Pipe
Pipe 是管道的意思,用来对参数做一些检验和转换:
1 | () |
Pipe 要实现 PipeTransform 接口,实现 transform 方法,里面可以对传入的参数值 value 做参数验证,比如格式、类型是否正确,不正确就抛出异常。也可以做转换,返回转换后的值。
内置的有 9 个 Pipe,从名字就能看出它们的意思:
- ValidationPipe
- ParseIntPipe
- ParseBoolPipe
- ParseArrayPipe
- ParseUUIDPipe
- DefaultValuePipe
- ParseEnumPipe
- ParseFloatPipe
- ParseFilePipe
同样,Pipe 可以只对某个参数生效,某个路由生效,也可以对每个路由都生效:
1 | () |
1 | () |
1 | async function bootstrap() { |
不管是 Pipe、Guard、Interceptor还是最终调用的 Controller,过程中都可以抛出一些异常,如何对某种异常做出某种响应呢?
这种异常到响应的映射也是一种通用逻辑,Nest 提供了 ExceptionFilter 来支持:
ExceptionFilter
ExceptionFilter 可以对抛出的异常做处理,返回对应的响应:
创建 ExceptionFilter的形式是这样的:
1 | HttpException) ( |
首先要实现 ExceptionFilter 接口,实现 catch 方法,就可以拦截异常了,但是要拦截什么异常还需要用 @Catch 装饰器来声明,拦截了异常之后,可以返回对应的响应,给用户更友好的提示。
Nest 内置了很多 http 相关的异常,都是 HttpException 的子类:
- BadRequestException
- UnauthorizedException
- NotFoundException
- ForbiddenException
- NotAcceptableException
- RequestTimeoutException
- ConflictException
- GoneException
- PayloadTooLargeException
- UnsupportedMediaTypeException
- UnprocessableException
- InternalServerErrorException
- NotImplementedException
- BadGatewayException
- ServiceUnavailableException
- GatewayTimeoutException
同样,ExceptionFilter 也可以选择全局生效或者某个路由生效:
1 | () |
1 | async function bootstrap(){ |