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适合更通用的处理逻辑。

总结

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

相关推荐
天宇&嘘月2 小时前
web第三次作业
前端·javascript·css
小王不会写code2 小时前
axios
前端·javascript·axios
发呆的薇薇°4 小时前
vue3 配置@根路径
前端·vue.js
luckyext4 小时前
HBuilderX中,VUE生成随机数字,vue调用随机数函数
前端·javascript·vue.js·微信小程序·小程序
小小码农(找工作版)4 小时前
JavaScript 前端面试 4(作用域链、this)
前端·javascript·面试
前端没钱4 小时前
前端需要学习 Docker 吗?
前端·学习·docker
前端郭德纲4 小时前
前端自动化部署的极简方案
运维·前端·自动化
海绵宝宝_4 小时前
【HarmonyOS NEXT】获取正式应用签名证书的签名信息
android·前端·华为·harmonyos·鸿蒙·鸿蒙应用开发
码农土豆5 小时前
chrome V3插件开发,调用 chrome.action.setIcon,提示路径找不到
前端·chrome
鱼樱前端5 小时前
深入JavaScript引擎与模块加载机制:从V8原理到模块化实战
前端·javascript