一篇文章学会 NestJS 中间件的使用,让你的应用更灵活和可扩展

前言

当我们基于NestJS框架构建和管理应用程序时,常常需要在特定的阶段执行一些额外的操作,例如在接收请求时进行身份验证,或在发送响应之前添加一些自定义的HTTP头。这就是我们需要理解和使用NestJS中间件的原因 ——它就像一道门,所有的请求必须要过这道门,而我们可以在门上挂上各种“魔法”,使得我们的应用程序更强大、更容易管理。

什么是NestJS中间件

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

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

使用案例

一、日志记录中间件

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

typescript
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(); } }

然后在应用模块中使用这个中间件:

typescript
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('*'); } }

现在,每个到达我们服务器的请求都会在控制台输出它的HTTP方法和URL。

二、身份认证中间件

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

typescript
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(); } }

然后在应用模块中使用这个中间件:

typescript
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common'; import { AuthMiddleware } from './auth.middleware'; @Module({}) export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(AuthMiddleware).forRoutes('*'); } }

这样一来,只有经过有效验证的用户才能访问我们服务器上的资源。

三、请求解析中间件

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

typescript
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();

现在,当我们接收到带有JSON主体的POST请求时,我们可以在路由处理函数中直接使用这些数据。为了演示这个,假设我们有一个只接受JSON数据的路由:

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

四、错误处理中间件

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

typescript
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, }); } } }

然后我们需要在模块中将此中间件应用到环路:

typescript
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common'; import { ErrorHandlerMiddleware } from './error-handler.middleware'; @Module({}) export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer.apply(ErrorHandlerMiddleware).forRoutes('*'); } }

总结

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