Nest.js 系列——自定义中间件

前言

在前面的文章中简单介绍了一下中间件是什么,并简单使用了下中间件,这里就看下如何自定义一个中间件以及需要注意的地方。

先用 cli 新建一个中间件文件

shell 复制代码
nest g middleware aaa --no-spec --flat

执行这个命令会生成一个中间件文件aaa.middleware.ts

ts 复制代码
import { Injectable, NestMiddleware } from '@nestjs/common'

@Injectable()
export class AaaMiddleware implements NestMiddleware {
  use(req: any, res: any, next: () => void) {
    next()
  }
}

因为不知道你的底层框架使用的是 express 还是 fastify,所以这里的参数类型是 any,你可以根据你使用的框架手动去改。一般来说使用的都是 express

ts 复制代码
import { Injectable, NestMiddleware } from '@nestjs/common'
import { Request, Response } from 'express'

@Injectable()
export class AaaMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: () => void) {
    next()
  }
}

中间件中使用了@Injectable装饰器,由此看出中间件也类似提供一个服务的提供者。可以在 next()方法的前后加一些处理逻辑,这个会在中间件的开始和结束时候触发。next 方法会把控制权交给下一个中间件。

ts 复制代码
import { Injectable, NestMiddleware } from '@nestjs/common'
import { Request, Response } from 'express'

@Injectable()
export class AaaMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: () => void) {
    console.log('brefore')
    next()
    console.log('after')
  }
}

使用中间件

上面简单定义了一个中间件, 接下来我们在app.module.ts中使用它。

ts 复制代码
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'
import { AppController } from './app.controller'
import { AppService } from './app.service'
import { AaaMiddleware } from './aaa.middleware'

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService]
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(AaaMiddleware).forRoutes('*')
  }
}

启动项目看下执行效果

这里是对所有路由启用,你也可以指定精确一点的路由。先定义几个路由作为区分

ts 复制代码
import { Controller, Get } from '@nestjs/common'
import { AppService } from './app.service'

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get('hello')
  getHello(): string {
    console.log('hello')
    return this.appService.getHello()
  }
  @Get('hello2')
  getHello2(): string {
    console.log('hello2')

    return this.appService.getHello()
  }

  @Get('world1')
  getWorld1(): string {
    console.log('world1')

    return this.appService.getHello()
  }

  @Get('world2')
  getWorld2(): string {
    console.log('world2')

    return this.appService.getHello()
  }
}

app.module.ts 中指定路由

ts 复制代码
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'
import { AppController } from './app.controller'
import { AppService } from './app.service'
import { AaaMiddleware } from './aaa.middleware'

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService]
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(AaaMiddleware).forRoutes({
      path: 'hello*',
      method: RequestMethod.GET
    })
    consumer.apply(AaaMiddleware).forRoutes({
      path: 'world*',
      method: RequestMethod.GET
    })
  }
}

浏览器访问对应的路由,看下执行结果。

这是中间件的简单使用,但是这样的使用方式和 express 差不多,但是 nest 中装饰器可以是类 class,因为如果是类的话,就可以使用依赖注入来注入服务进行一些逻辑使用。

ts 复制代码
import { AppService } from './app.service'
import { Inject, Injectable, NestMiddleware } from '@nestjs/common'
import { Request, Response } from 'express'

@Injectable()
export class AaaMiddleware implements NestMiddleware {
  @Inject(AppService)
  private readonly appService: AppService

  use(req: Request, res: Response, next: () => void) {
    console.log('brefore')
    console.log('-------' + this.appService.getHello())
    next()
    console.log('after')
  }
}

这里也可以使用构造函数注入,前面有提到过,这里的提供者注入有很多方式,忘记的可以去看看

ts 复制代码
import { AppService } from './app.service'
import { Inject, Injectable, NestMiddleware } from '@nestjs/common'
import { Request, Response } from 'express'

@Injectable()
export class AaaMiddleware implements NestMiddleware {
  constructor(private readonly appService: AppService) {}

  use(req: Request, res: Response, next: () => void) {
    console.log('brefore')
    console.log('-------' + this.appService.getHello())
    next()
    console.log('after')
  }
}

访问下路由看下控制台输出

如果全局使用中间件可以使用app.use()方法,这个就和 express 差不多了。

中间件和拦截器

从表现上看,感觉中间件和拦截器的处理时机是差不多的。但是它俩还有一些细微的区别的。interceptor 是能从 ExecutionContext 里拿到目标 classhandler,进而通过 reflector 拿到它的 metadata 等信息的,这些 middleware 就不可以。

再就是 interceptor 里是可以用 rxjs 的操作符来组织响应处理流程的。middleware 里也不可以。

它们都是 Nest AOP 思想的实现,但是 interceptor 更适合处理与具体业务相关的逻辑,而 middleware适合更通用的处理逻辑。

总结

本文简单实现了自定义中间件的编写和使用,当然在业务中不会有这么简单的中间件。而且也对比了和拦截器的区别,希望对你有帮助。

相关推荐
无理 Java26 分钟前
【技术详解】SpringMVC框架全面解析:从入门到精通(SpringMVC)
java·后端·spring·面试·mvc·框架·springmvc
f8979070701 小时前
layui动态表格出现 横竖间隔线
前端·javascript·layui
鱼跃鹰飞1 小时前
Leecode热题100-295.数据流中的中位数
java·服务器·开发语言·前端·算法·leetcode·面试
cyz1410011 小时前
vue3+vite@4+ts+elementplus创建项目详解
开发语言·后端·rust
二十雨辰1 小时前
[uni-app]小兔鲜-04推荐+分类+详情
前端·javascript·uni-app
liuxin334455662 小时前
大学生就业招聘:Spring Boot系统的高效实现
spring boot·后端·mfc
向上的车轮2 小时前
ASP.NET Zero 多租户介绍
后端·asp.net·saas·多租户
霸王蟹2 小时前
Vue3 项目中为啥不需要根标签了?
前端·javascript·vue.js·笔记·学习
小白求学12 小时前
CSS计数器
前端·css
Anita_Sun2 小时前
🌈 Git 全攻略 - Git 的初始设置 ✨
前端