nestjs入门学习总结(二):中间件、异常过滤器、守卫、管道、拦截器

nestjs,入门,学习,总结,中间件,异常,过滤器,守卫,管道,拦截器 · 浏览次数 : 22

小编点评

**中间件 Nest 中间件** **定义** 中间件是一个在请求处理之前或之后运行的逻辑组件。它可以是函数或带有 `@Injectable()` 装饰器的类。 **使用** 1. 在模块类中实现 `NestModule` 接口。 2. 在 `configure()` 方法中使用 `apply()` 方法将中间件注册到路由处理器。 3. 可以在控制器层抛出 HttpException 对象以拦截请求。 **示例** ```typescript import { LoggerMiddleware } from './common/middlewares/logger.middleware'; @Module({ imports: [CatsModule], }) export class ApplicationModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer.apply(LoggerMiddleware) .forRoutes('cats'); } } ``` **函数中间件** 函数中间件可以定义在 `LoggerMiddleware` 中。 ```typescript import { LoggerMiddleware } from './common/middlewares/logger.middleware'; @Injectable() export class LoggingInterceptor implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable { console.log('Before...'); const now = Date.now(); return next.handle() .pipe( tap(() => console.log(`After... ${Date.now() - now}ms`)), ); } } ``` **守卫** 守卫是拦截器的一个类型。它根据运行时条件(例如权限,角色等)来决定给定的请求是否由路由处理程序处理。守卫在每个中间件之后执行,但在任何拦截器或管道之前执行。 **管道** 管道是数据处理和转换的中间组件。它可以用于将输入数据转换为所需的数据输出验证。管道有两个类型:转换和验证。

正文

中间件

Nest 中间件可以是一个函数,也可以是一个带有 @Injectable() 装饰器的类,且该类应该实现 NestMiddleware 接口,而函数没有任何特殊要求。

如下是一个日志中间件的简单示例:

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response } from 'express';

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: Function) {
    console.log('Request...');
    next();
  }
}
  1. 中间件中的依赖注入

与提供者(Provider)和控制器(Controller)一样,他能够通过构造函数注入属于同一模块的依赖项:

import { Injectable, Inject, NestMiddleware } from '@nestjs/common';
import { Request, Response } from 'express';

@Injectable()
export class SomeMiddleware implements NestMiddleware {
  constructor(@Inject(SomeService) private readonly someService: SomeService) {}

  use(req: Request, res: Response, next: Function) {
    // do some logic...
    this.someService.method();

    console.log('Request...');
    next();
  }
}
  1. 使用中间件

中间件是请求发出者和路由处理器之间的桥梁,我们只需要在模块类中实现 NestModule 接口:

import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './common/middlewares/logger.middleware';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],
})
export class ApplicationModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes('cats');
  }
}

在上面的例子中,我们为 /cats 路由处理器(@CatsController('/cats'))设置了日志中间件。
如果只需要给 /cats 路由中的某几个请求方法设置这个中间件,那只需要改变一下 forRoutes() 方法中的参数即可:forRoutes({ path: 'cats', method: RequestMethod.GET }),此时,只有 GET 请求才会被中间件拦截。

Nest 提供了路由通配符的功能(与 Controller 中的路由通配符一样)

forRoutes({ path: 'ab*cd', method: RequestMethod.ALL })

forRoutes() 方法中还可以传入一个控制器类,如:forRoutes(CatsController),他会将 CatsController 中的所有路由拦截并使用中间件。如果需要传入多个控制器类,只需要使用 , 分割,如: forRoutes(CatsController, UserController)。

不仅如此,apply() 方法同样可以传入一个或多个(用 , 分割)中间件,如:apply(LoggerMiddleware, OtherMiddleware)。

  1. 函数中间件

Nest 中的中间件可以是类,也可以是一个函数,上述都在讲关于类的中间件,这里使用函数来声明一个中间件:

export function logger(req, res, next) {
  console.log(`Request...`);
  next();
};
  1. 全局中间件

为了将中间件一次绑定到每个注册的路由,我们可以使用 INestApplication 实例中的 use() 方法:

const app = await NestFactory.create(ApplicationModule);
// 这里必须使用函数中间件
app.use(logger);
await app.listen(3000);

异常过滤器

Nest 内置异常层负责处理整个应用程序中抛出的所有异常。当捕获到未处理的异常时,用户最终将收到适当的友好响应。

每个出现的异常都由全局异常过滤器处理,当无法识别时(既不是HttpException,也不是从HttpException继承的类),用户会收到以下JSON响应:

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

当程序抛出一个 HttpException 对象时,它会被异常处理程序捕获,然后转换成相关的 JSON 响应。

  1. 基础异常

在讲异常过滤器前,我们先熟悉框架的基础异常类:HttpException,HttpException 类来自于 @nestjs/common。正如你所想的,当程序抛出一个 HttpException 对象时,它会被异常处理程序捕获,然后转换成相关的 JSON 响应。

如何在控制器层抛出一个 HttpException 呢?

@Post()
async create(@Body() createCatDto: CreateCatDto) {
  throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
}

此时,客户端会接收到如下 JSON 相应:

{
  "statusCode": 403,
  "message": "Forbidden"
}

HttpException 类的构造器中第一个参数可以是 string 或 object,当你将异常改写成如下时:

@Post()
async create(@Body() createCatDto: CreateCatDto) {
  throw new HttpException({
    status: HttpStatus.FORBIDDEN,
    error: 'This is a custom message',
  }, 403);
}

客户端会收到:

{
  "status": 403,
  "error": "This is a custom message"
}

这种通常用来做自定义的异常消息返回

守卫

守卫是一个使用 @Injectable() 装饰器的类。 守卫应该实现 CanActivate 接口。

守卫有一个单独的责任。它们根据运行时出现的某些条件(例如权限,角色,访问控制列表等)来确定给定的请求是否由路由处理程序处理。

守卫在每个中间件之后执行,但在任何拦截器或管道之前执行。

管道

管道是具有 @Injectable() 装饰器的类。管道应实现 PipeTransform 接口。

管道有两个类型:

转换:管道将输入数据转换为所需的数据输出
验证:对输入数据进行验证,如果验证成功继续传递; 验证失败则抛出异常;

拦截器

拦截器是使用 @Injectable() 装饰器注解的类。拦截器应该实现 NestInterceptor 接口。

拦截器具有一系列有用的功能,这些功能受面向切面编程(AOP)技术的启发。它们可以:

在函数执行之前/之后绑定额外的逻辑
转换从函数返回的结果
转换从函数抛出的异常
扩展基本函数行为
根据所选条件完全重写函数 (例如, 缓存目的)

每个拦截器都有 intercept() 方法,它接收2个参数。 第一个是 ExecutionContext 实例(与守卫完全相同的对象),第二个参数是 CallHandler。

当我们要记录与应用程序的交互时,它很有用,例如 存储用户调用,异步调度事件或计算时间戳。

// logging.interceptor.ts

import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    console.log('Before...');

    const now = Date.now();
    return next
      .handle()
      .pipe(
        tap(() => console.log(`After... ${Date.now() - now}ms`)),
      );
  }
}
  1. 绑定拦截器

为了设置拦截器, 我们使用从 @nestjs/common 包导入的 @UseInterceptors() 装饰器。与守卫一样, 拦截器可以是控制器范围内的, 方法范围内的或者全局范围内的。

// cats.controller.ts

@UseInterceptors(LoggingInterceptor)
export class CatsController {}

总结

管道:数据处理与转换,数据验证
守卫:验证用户登陆,保护路由
拦截器:统一响应内容
过滤器:异常捕获
中间件:日志打印

参考阅读

项目源码

代码已经上传到github中,欢迎大家star,持续更新,如有任何问题可以联系我v:sky201208(注明来意)

https://github.com/fozero/cloud-collect-nestjs

参考阅读

关于我&前端&node进阶交流学习群

大家好,我是阿健Kerry,一个有趣且乐于分享的人,前小鹏汽车、货拉拉高级前端工程师,长期专注前端开发,如果你对前端&Node.js 学习进阶感兴趣的话(后续有计划也可以),可以关注我,加我微信【sky201208】,拉你进交流群一起交流、学习共同进步,群内氛围特别好,定期会组织技术分享~

与nestjs入门学习总结(二):中间件、异常过滤器、守卫、管道、拦截器相似的内容:

nestjs入门学习总结(二):中间件、异常过滤器、守卫、管道、拦截器

### 中间件 Nest 中间件可以是一个函数,也可以是一个带有 @Injectable() 装饰器的类,且该类应该实现 NestMiddleware 接口,而函数没有任何特殊要求。 如下是一个日志中间件的简单示例: ``` import { Injectable, NestMiddleware }

nestjs入门学习总结(一):控制器、服务、模块

- 为什么要用nestjs,和egg区别对比 - nest项目初始化,了解目录结构 - nest cli命令了解 - nest基础知识点学习:控制器、服务、模块 ### 为什么要用nestjs,和egg区别对比 #### 官网介绍 1. Nest提供了一种开箱即用的应用程序架构,允许开发人员和团队创

nestjs入门学习总结(三):集成typeorm并实现一个curd操作

### typeorm熟悉 TypeORM 是一个ORM框架,它可以运行在 NodeJS、Browser、Cordova、PhoneGap、Ionic、React Native、Expo 和 Electron 平台上,可以与 TypeScript 和 JavaScript (ES5,ES6,ES7,

nestjs入门学习总结(四):实现接口统一格式及请求参数验证

### 接口格式统一 - 请求成功返回 ``` { "code": 0, "message": "OK", "data": [] } ``` - 请求失败返回 ``` { "code": -1, "message": "error reason", "data": null } ``` #### 请

nestjs入门学习总结(五):实现用户登录注册功能

### 实现用户注册 1. 我们先使用命令创建两个模块,分别是用户模块和授权模块 ``` nest g resource auth nest g resource user ``` 2. 编写用户实体 ``` import { Entity, Column, PrimaryGeneratedColu

next.js app目录 i18n国际化简单实现

最近在用next写一个多语言的项目,找了好久没找到简单实现的教程,实践起来感觉都比较复杂,最后终于是在官方文档找到了,结合网上找到的代码demo,终于实现了,在这里简单总结一下。 此教程适用于比较简单的项目实现,如果你是刚入门next,并且不想用太复杂的方式去实现一个多语言项目,那么这个教程就挺适合

我在前端写Java SpringBoot项目

本篇文章主要是使用 NestJs + Sequelize + MySQL 完成基础运行, 带大家了解 Node 服务端的基础搭建,也可以顺便看看 Java SpringBoot 项目的基础结构,它俩真的非常相似,不信你去问服务端开发同学。