如何在NestJS拦截器的catchError中生成自定义响应?
NestJS拦截器自定义错误响应方案优化
问题背景
在NestJS中需通过拦截器捕获所有错误并生成自定义响应,无法使用全局异常过滤器——因存在记录请求/响应的日志拦截器,且错误处理必须先于日志拦截器执行(需为日志附加错误数据)。
当前错误处理拦截器代码
@Injectable() export class UnknownExceptionsResponder implements NestInterceptor { constructor(private readonly httpAdapter: HttpServer) {} intercept(context: ExecutionContext, next: CallHandler): Observable<any> { return next .handle() .pipe( catchError((error) => { const ctx = context.switchToHttp() const req = ctx.getRequest() req.info = Object.assign({ error }, req.info) let status = 500 const response: any = { code: 'unknown_error', description: 'Internal server error' } if (error.exposeCustom_) { status = error.status || 500 response.code = error.message || 'unknown_error' if (error.description) { response.description = error.description } if (error.exposeMeta) { response.meta = error.exposeMeta } } this.httpAdapter.reply(ctx.getResponse(), response, status); return EMPTY }), ); } }
现有方案的问题
- 使用
this.httpAdapter.reply后返回EMPTY:能得到正确响应,但触发ERROR [ExceptionsHandler] no elements in sequence错误 - 返回
scheduled([{error: error}], asyncScheduler):可触发日志拦截器,但出现ERROR [ExceptionsHandler] Cannot set headers after they are sent to the client错误 - 移除
reply调用后返回scheduled([response], asyncScheduler):响应体正确但状态码始终为200,不符合需求
解决方案
核心思路是手动设置响应状态码,返回包含自定义响应体的Observable,让NestJS完成响应发送流程,避免重复操作。
修改后的拦截器代码
@Injectable() export class UnknownExceptionsResponder implements NestInterceptor { intercept(context: ExecutionContext, next: CallHandler): Observable<any> { return next.handle().pipe( catchError((error) => { const ctx = context.switchToHttp(); const req = ctx.getRequest(); const res = ctx.getResponse(); req.info = Object.assign({ error }, req.info); let status = 500; const response: any = { code: 'unknown_error', description: 'Internal server error', }; if (error.exposeCustom_) { status = error.status || 500; response.code = error.message || 'unknown_error'; if (error.description) { response.description = error.description; } if (error.exposeMeta) { response.meta = error.exposeMeta; } } // 手动设置响应状态码 res.status(status); // 返回自定义响应体,让NestJS正常完成响应流程 return of(response); }), ); } }
关键修改说明
- 移除
httpAdapter.reply调用:避免手动发送响应导致后续流程重复设置响应头 - 手动设置状态码:通过
res.status(status)直接修改响应对象的状态码 - 返回
of(response):- 发出自定义响应体,让NestJS的响应流程正常完成,不会触发
no elements in sequence错误 - 确保日志拦截器能捕获到响应数据,完成日志记录
- 发出自定义响应体,让NestJS的响应流程正常完成,不会触发
- 保证拦截器顺序:注册时将
UnknownExceptionsResponder放在日志拦截器之前,确保错误处理先执行
拦截器注册示例
// 全局注册示例 async function bootstrap() { const app = await NestFactory.create(AppModule); // 先注册错误处理拦截器,再注册日志拦截器 app.useGlobalInterceptors( new UnknownExceptionsResponder(), new LoggingInterceptor(), ); await app.listen(3000); } bootstrap();
修改后既能生成带正确状态码的自定义响应,又能让日志拦截器正常工作,同时避免所有之前的错误。
内容的提问来源于stack exchange,提问作者twilker
相关产品推荐
相关产品推荐

