WEBKT

NestJS 中间件错误处理:从入门到精通,构建更健壮的应用

88 0 0 0

NestJS 中间件错误处理:从入门到精通,构建更健壮的应用

为什么要重视中间件的错误处理?

NestJS 中间件错误处理的基础

同步错误处理

异步错误处理

设计错误响应

进阶:自定义错误类

错误日志

总结

NestJS 中间件错误处理:从入门到精通,构建更健壮的应用

嗨,各位开发者!今天咱们来聊聊 NestJS 中间件的错误处理。相信你一定遇到过这样的情况:在中间件中抛出了一个错误,结果整个应用都崩溃了,或者错误信息直接暴露给了用户,既不安全也不友好。别担心,今天我就带你深入了解 NestJS 中间件的错误处理机制,让你轻松应对各种错误场景,构建更健壮、更安全的应用程序。

为什么要重视中间件的错误处理?

在 NestJS 应用中,中间件扮演着至关重要的角色,它们像一道道关卡,在请求到达控制器之前对其进行拦截和处理。常见的应用场景包括:

  • 身份验证和授权:检查用户是否登录、是否有权限访问特定资源。
  • 日志记录:记录请求信息、响应信息、错误信息等。
  • 请求参数校验:验证请求参数是否符合预期。
  • 数据转换:将请求数据转换为控制器需要的格式。
  • 跨域资源共享 (CORS):处理跨域请求。

如果中间件中出现错误而没有得到妥善处理,可能会导致:

  • 应用程序崩溃:未捕获的异常会导致整个应用程序停止运行。
  • 安全漏洞:错误信息可能包含敏感信息,泄露给用户会导致安全风险。
  • 用户体验差:用户看到的是一堆错误信息,而不是友好的提示。

因此,掌握中间件的错误处理技巧至关重要。

NestJS 中间件错误处理的基础

NestJS 的中间件本质上就是一个函数,它接收三个参数:req (请求对象)、res (响应对象) 和 next (下一个中间件函数)。

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log('Request...');
next();
}
}

如果在中间件中发生错误,我们可以通过以下两种方式处理:

  1. 调用 next(err):将错误传递给下一个中间件或错误处理程序。这是最常用的方式。
  2. 直接使用 res.status().send():直接向客户端发送错误响应。这种方式通常用于处理一些简单的错误,例如参数校验失败。

同步错误处理

如果在中间件中同步抛出错误,NestJS 会自动捕获并处理。你可以通过调用 next(err) 将错误传递给下一个中间件或错误处理程序。

import { Injectable, NestMiddleware, BadRequestException } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class ValidateParamsMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
if (!req.body.name) {
// 抛出 BadRequestException
throw new BadRequestException('Name is required');
}
next();
}
}

在上面的例子中,如果请求体中没有 name 属性,就会抛出一个 BadRequestException。NestJS 会自动将这个异常转换为一个 HTTP 400 响应。

你也可以通过 next(err) 传递自定义错误:

// ...
use(req: Request, res: Response, next: NextFunction) {
if (!req.body.name) {
next(new Error('Name is required')); // 使用next传递错误
}
next();
}
// ...

异步错误处理

如果中间件中包含异步操作(例如数据库查询、调用外部 API),错误处理会稍微复杂一些。你需要确保在异步操作中捕获所有可能的错误,并使用 next(err) 将其传递给下一个中间件或错误处理程序。

import { Injectable, NestMiddleware, HttpException, HttpStatus } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { promises as fs } from 'fs';
@Injectable()
export class ReadFileMiddleware implements NestMiddleware {
async use(req: Request, res: Response, next: NextFunction) {
try {
const data = await fs.readFile('somefile.txt', 'utf8');
req.fileData = data; // 将读取的文件数据添加到请求对象中
next();
} catch (err) {
// 捕获异步操作中的错误
next(new HttpException('Failed to read file', HttpStatus.INTERNAL_SERVER_ERROR));
}
}
}

在这个例子中,我们使用 try...catch 块来捕获 fs.readFile 可能抛出的错误。如果读取文件失败,我们会创建一个 HttpException,并将其传递给 next() 函数。NestJS 会自动将 HttpException 转换为相应的 HTTP 响应。

最佳实践: 使用 async/awaittry...catch 来处理异步操作中的错误。

设计错误响应

当中间件发生错误时,我们需要向客户端返回一个清晰、友好的错误响应。NestJS 提供了 HttpException 类来帮助我们构建错误响应。

HttpException 构造函数接收两个参数:

  • response:错误消息或错误对象。
  • status:HTTP 状态码。
throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
// 或者使用更详细的错误对象
throw new HttpException({
status: HttpStatus.FORBIDDEN,
error: 'This is a custom message',
}, HttpStatus.FORBIDDEN);

NestJS 会自动将 HttpException 转换为如下的 JSON 响应:

{
"statusCode": 403,
"message": "Forbidden"
}
// 或者
{
"statusCode": 403,
"error": "This is a custom message"
}

你可以根据自己的需要自定义错误响应的格式。例如,你可以创建一个自定义的异常过滤器来拦截所有 HttpException,并将其转换为统一的错误响应格式。

import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message: exception.message,
});
}
}

然后在你的 main.ts 文件中全局注册这个过滤器:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { HttpExceptionFilter } from './http-exception.filter';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new HttpExceptionFilter());
await app.listen(3000);
}
bootstrap();

这样,所有 HttpException 都会被 HttpExceptionFilter 拦截,并转换为统一的 JSON 响应格式。

进阶:自定义错误类

对于更复杂的应用,你可能需要创建自定义的错误类来表示不同类型的错误。这可以帮助你更好地组织代码,并提供更详细的错误信息。

export class ValidationError extends HttpException {
constructor(errors: string[]) {
super({ message: 'Validation failed', errors }, HttpStatus.BAD_REQUEST);
}
}

然后你可以在中间件中使用这个自定义错误类:

// ...
use(req: Request, res: Response, next: NextFunction) {
const errors = [];
if (!req.body.name) {
errors.push('Name is required');
}
if (!req.body.email) {
errors.push('Email is required');
}
if (errors.length > 0) {
next(new ValidationError(errors));
}
next();
}
// ...

错误日志

除了向客户端返回错误响应,记录错误日志也是非常重要的。这可以帮助你追踪问题、分析错误原因,并改进你的应用程序。

NestJS 内置了日志模块 (Logger),你可以直接使用它来记录错误日志。

import { Injectable, NestMiddleware, HttpException, HttpStatus, Logger } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class ErrorLoggerMiddleware implements NestMiddleware {
private readonly logger = new Logger(ErrorLoggerMiddleware.name);
use(req: Request, res: Response, next: NextFunction) {
try {
next();
} catch (error) {
this.logger.error(`Error: ${error.message}, Path: ${req.originalUrl}`, error.stack);
next(error);
}
}
}

在上面的代码中,catch 块是无效的。因为NestJs中间件的错误需要通过next()传递,直接throw无法被NestJs默认的错误处理器处理,因此需要通过next(error)传递错误。

更推荐的方式是在异常过滤器中记录日志。

@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
private readonly logger = new Logger(AllExceptionsFilter.name);
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
const message =
exception instanceof HttpException
? exception.getResponse()
: 'Internal server error';
this.logger.error(
`HTTP Status: ${status} Error Message: ${JSON.stringify(message)}`,
exception instanceof Error ? exception.stack : '',
);
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message,
});
}
}

总结

NestJS 中间件的错误处理是构建健壮、安全应用程序的关键。通过本文的学习,你应该掌握了:

  • 中间件错误处理的重要性。
  • 同步和异步错误处理的方法。
  • 如何设计友好的错误响应。
  • 如何使用自定义错误类。
  • 如何记录错误日志。

希望这些知识能帮助你更好地处理 NestJS 中间件中的错误,构建出更出色的应用程序!如果你有任何问题或想法,欢迎在评论区留言。

一些额外的思考题:

  1. 如何在 NestJS 中实现全局的错误处理?
  2. 如何将错误信息发送到第三方监控平台(例如 Sentry)?
  3. 如何在单元测试中测试中间件的错误处理逻辑?
技术老司机 NestJS中间件错误处理

评论点评

打赏赞助
sponsor

感谢您的支持让我们更好的前行

分享

QRcode

https://www.webkt.com/article/7889