NestJS核心概念之Middleware中间件

12 阅读3分钟

NestJS中间件实际上就是一个函数,这个函数可以对请求或响应对象进行一些操作,或者在传递给下一个处理程序之前进行一些预处理。具体来说,一般使用中间件处理以下几个场景:

  • 认证: 验证用户的身份,只有经过验证的用户才能对某些资源进行访问。
  • 日志记录: 记录请求的详细信息,如请求时间、来源IP、目标URL等,这对于审计和错误追踪非常有用。
  • 请求解析: 对请求参数进行解析,并进行一些必要的格式化和验证操作。
  • 错误处理: 对请求处理过程中产生的错误进行统一的处理。

类中间件

使用@Injectable()装饰器,并且需要实现NestMiddleware接口(use方法)

// Logger.middleware.ts
import { Injectable, NestMiddleware } from "@nestjs/common";
import { Request, Response } from "express";
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
    use(req: Request, res: Response, next: () => void) {
        console.log('logger middleware', `url: ${req.url}`);
        next();
    }
}

使用类中间件

类中间创建完之后,需要在模块中进行挂载,但@Module装饰器并没有中间件的相关配置,我们需要让module类实现NestModule接口,实现里面configure方法来进行挂载

// user.module.ts
import { Module, NestModule } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { LoggerMiddleware } from '../middleware/Logger.middleware';
@Module({
  controllers: [UserController],
  providers: [UserService]
})
export class UserModule implements NestModule {
  configure(consumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes(UserController);
  }
}

//forRootes方法还能做更详细的配置,比如可以针对特定的请求方法、请求路径可以使用正则匹配
export class UserModule implements NestModule {
  configure(consumer) {
    consumer
      .apply(LoggerMiddleware)
      .forRoutes({ path: 'user', method: RequestMethod.GET});
  }
}

//exclude可以用来排除不使用中间件的路径
export class UserModule implements NestModule {
  configure(consumer) {
    consumer
      .apply(LoggerMiddleware)
      .exclude({ path: '/user/a', method: RequestMethod.GET})
      .forRoutes(UserController);
  }
}

函数中间件

这种方式较为简单,使用起来与类中间件一致

export function LoggerMiddleware(req: Request, res: Response, next: () => void) {
    console.log('logger middleware', `url: ${req.url}`);
    next();
}

//使用函数中间件 /////////////////////////
export class UserModule implements NestModule {
  configure(consumer) {
    consumer
      .apply(LoggerMiddleware)
      .exclude({ path: '/user/a', method: RequestMethod.GET})
      .forRoutes(UserController);
  }
}

全局中间件

可以直接在入口文件main.ts中使用app.use来挂载中间件,这样挂载的中间件将全局生效

app.use(LoggerMiddleware) // 日志中间件

使用案例

日志记录中间件

假设我们需要对每个到服务器的请求进行日志记录,我们可以定义一个日志中间件,如下所示:

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('Received request:', req.method, req.originalUrl);
    next();
  }
}

//模块调用///////////////////////////////////////////////
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { LoggerMiddleware } from './logger.middleware';

@Module({})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(LoggerMiddleware).forRoutes('*');
  }
}

身份认证中间件

在这个例子中,我们定义一个中间件,它会检查每个请求的授权头部,如果没有这个头部,它会返回一个错误信息;如果有这个头部,它会进行验证,验证成功后才会将请求传递给下一个处理程序。

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

@Injectable()
export class AuthMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: Function) {
    if (!req.headers.authorization) {
      return res.status(403).json({ message: 'No Authorization Header' });
    }
    // 验证授权头部...
    next();
  }
}

请求解析中间件

NestJS框架提供了一个内置的请求解析中间件,可以将请求主体解析成我们所想要的格式,例如JSON。这样,我们可以在路由处理函数中直接处理这些数据。使用这个内置的中间件非常简单,只需要在主模块中添加以下代码:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as express from 'express';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.use(express.json());
  await app.listen(3000);
}
bootstrap();

//使用//////////////////////////////////////
import { Controller, Post, Body } from '@nestjs/common';

@Controller('route')
export class MyController {
  @Post()
  postData(@Body() data: any) {
    // 我们可以直接处理JSON数据
    console.log(data);
  }
}

错误处理中间件

错误处理是Web开发中常见的一个需求,NestJS同样可以通过中间件的方式处理错误。例如,我们可以创建一个错误处理中间件来捕获抛出的异常,然后将它们格式化为JSON响应:

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

@Injectable()
export class ErrorHandlerMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    try {
      next();
    } catch (err) {
      res.status(500).json({
         message: 'Internal server error',
         ...err,
      });
    }
  }
}

最后

NestJS中间件就像是一个可定制的、灵活性极高的管道,它可以让我们预处理请求、扩展响应或者组织我们的业务逻辑。它的威力不仅在于能够处理各种边缘情况,更在于它的扩展性和可维护性,这两个强大的特性让我们可以根据项目的需要自定义适合的中间件,从而开发出高效、健壮的后端服务。