如何自定义NestJS 中间件
前言
NestJS 作为一个强大的 Node.js 框架,允许你通过中间件对请求和响应进行处理。中间件的概念在其他许多框架中也存在,它们在请求处理流程的早期执行,因此非常适合执行如日志记录、请求验证、设置响应头等任务。
在这篇教程中,我将通过一个简单的示例,展示如何在 NestJS 应用中创建和使用自定义中间件。
使用步骤
第一步:创建中间件
在 NestJS 中创建中间件很简单,你只需要创建一个实现了 NestMiddleware
接口的类。这个类将包含一个 use
方法,该方法接收 request
、response
对象和 next
函数。
typescriptimport { Injectable, NestMiddleware } from '@nestjs/common'; import { Request, Response, NextFunction } from 'express'; @Injectable() export class LoggingMiddleware implements NestMiddleware { use(req: Request, res: Response, next: NextFunction) { console.log(`[${new Date().toISOString()}] Request made to: ${req.path}`); next(); } }
在这个例子中,LoggingMiddleware
类会记录请求到达的时间和路径。
第二步:注册中间件
接下来,你需要在模块中注册这个中间件。这通常在模块的 configure
方法中完成,你需要实现 NestModule
接口并定义这个方法。
typescriptimport { Module, NestModule, MiddlewareConsumer } from '@nestjs/common'; @Module({}) export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer .apply(LoggingMiddleware) .forRoutes('*'); // 应用到所有路由 } }
在这里,我们使用 MiddlewareConsumer
的 apply
方法来注册中间件,并通过 forRoutes
指定中间件应用的路径。在这个例子中,中间件将应用于所有路由。
第三步:测试中间件
一旦你注册了中间件,它就会在每个请求到来时执行。要测试它,你可以启动你的 NestJS 应用并向任意端点发送请求。
shellnpm run start
然后,打开一个新的命令行窗口发送一个请求(使用 curl 或者任何 API 测试工具):
shellcurl <http://localhost:3000/your-endpoint>
你应该可以在 NestJS 应用的控制台输出中看到日志行,类似于:
shell[2024-01-01T12:00:00.000Z] Request made to: /your-endpoint
高级使用
中间件不仅仅限于记录日志。你可以利用中间件做很多有用的事情,比如:
- 请求验证: 确认请求是否包含所需的凭证或参数。
- 速率限制: 限制来自同一来源的请求频率,以预防滥用。
- CORS 设置: 在跨域请求中设置必要的头信息,以支持安全的跨域交互。
记住,中间件执行顺序很重要。如果你注册了多个中间件,它们会按顺序执行。这意味着,如果第一个中间件不调用 next()
,后续的中间件和路由处理器都不会被执行。
一、请求验证
假设我们有一个API端点 /secure
,它需要验证请求是否带有有效的API密钥。我们可以创建一个中间件来校验请求头中的 x-api-key
字段。
typescriptimport { Injectable, NestMiddleware, UnauthorizedException } from '@nestjs/common'; import { Request, Response, NextFunction } from 'express'; @Injectable() export class ApiKeyMiddleware implements NestMiddleware { private readonly VALID_API_KEY = 'secret'; use(req: Request, res: Response, next: NextFunction) { const apiKey = req.headers['x-api-key']; if (apiKey && apiKey === this.VALID_API_KEY) { next(); } else { throw new UnauthorizedException('Invalid API Key'); } } }
注册这个中间件:
typescriptconfigure(consumer: MiddlewareConsumer) { consumer .apply(ApiKeyMiddleware) .forRoutes('/secure'); }
任何请求 /secure
端点的客户端都需要提供有效的API密钥,否则请求会被拒绝。
二、速率限制
为了防止滥用,你可能需要限制客户端在一定时间内能够发送到某个端点的请求数量。这样的中间件可以利用第三方库如 express-rate-limit
实现。
typescriptimport rateLimit from 'express-rate-limit'; export function RateLimiterMiddleware() { return rateLimit({ windowMs: 15 * 60 * 1000, // 15分钟 max: 100, // 限制每个IP在15分钟内最多100个请求 }); }
注册中间件:
typescriptconfigure(consumer: MiddlewareConsumer) { consumer .apply(RateLimiterMiddleware()) .forRoutes('/*'); // 适用于所有路由 }
这将会对所有请求施加速率限制,每个IP地址每15分钟只允许最多100个请求。
三、CORS 设置
跨源资源共享(CORS)是一种安全特性,限制了网页上可以加载和使用哪些资源。如果你想允许来自特定来源的请求,你可以使用中间件来设置CORS相关的响应头。
虽然 NestJS 提供了内置的CORS支持,但如果你需要更高级的配置,可以自定义CORS中间件,例如:
typescriptimport { Injectable, NestMiddleware } from '@nestjs/common'; import { Request, Response, NextFunction } from 'express'; @Injectable() export class CorsMiddleware implements NestMiddleware { use(req: Request, res: Response, next: NextFunction) { res.header('Access-Control-Allow-Origin', '<https://example.com>'); res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization'); next(); } }
注册中间件:
typescriptconfigure(consumer: MiddlewareConsumer) { consumer .apply(CorsMiddleware) .forRoutes('/*'); // 适用于所有路由 }
这个简单的CORS中间件允许来自 https://example.com
的请求,并允许 GET
、POST
和 OPTIONS
方法,同时允许 Content-Type
和 Authorization
请求头。
结论
中间件是 NestJS 框架灵活性的关键组成部分,能够通过简单有效的方式实现请求的预处理。上面介绍的高级使用方式,包括请求验证、速率限制和CORS设置,都是实际应用中常见并且非常实用的场景。合理地使用中间件可以让你的应用更加安全和稳定,同时提高用户体验。
结论
中间件是 NestJS 框架灵活性的关键组成部分,能够通过简单有效的方式实现请求的预处理。通过创建 NestMiddleware
接口的实现类,你可以在请求处理链中插入自己的逻辑。注册中间件方法是直观而灵活的,让你可以精确控制中间件的应用范围。