NestJS中能否同时使用Interceptor与@Response设置响应状态码?
解决NestJS拦截器与自定义响应状态码共存的问题
问题原因
当你在控制器中使用@Response()(或@Res())注入FastifyReply对象并直接调用send()时,NestJS会判定你已手动完成响应处理,控制器方法的返回值会被忽略,导致拦截器的next.handle()只能拿到undefined,最终无法执行响应包装逻辑。
解决方案
方案1:使用passthrough: true透传响应对象
通过在@Res()中添加passthrough: true配置,既可以手动设置响应状态码,又能让NestJS继续处理控制器的返回值,确保拦截器能拿到数据并完成包装。
控制器代码示例:
@UseInterceptors(TransformInterceptor) @Post('some-path') async someFunction( @Res({ passthrough: true }) reply: Fastify.FastifyReply, ) { // 根据逻辑设置状态码 reply.status(isATeapot ? HttpStatus.I_AM_A_TEAPOT : HttpStatus.OK); // 直接返回数据,交给拦截器包装 return someData; }
拦截器无需修改,保持原有的包装逻辑即可。
方案2:统一返回格式,由拦截器处理状态码
控制器返回包含status和data的自定义对象,拦截器中读取状态码并设置到响应对象,同时完成数据包装。
控制器代码示例:
@UseInterceptors(TransformInterceptor) @Post('some-path') async someFunction() { const status = isATeapot ? HttpStatus.I_AM_A_TEAPOT : HttpStatus.OK; return { status, data: someData }; }
修改后的拦截器代码:
@Injectable() export class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> { intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> { const httpContext = context.switchToHttp(); const response = httpContext.getResponse<Fastify.FastifyReply>(); return next.handle().pipe( map((result) => { // 处理包含状态码的自定义返回格式 if (typeof result === 'object' && result.status !== undefined && result.data !== undefined) { response.status(result.status); return { data: result.data }; } // 默认情况直接包装数据 return { data: result }; }), ); } }
方案3:跳过特定接口的拦截器(不推荐)
如果部分接口需要完全手动控制响应且不需要拦截器包装,可以使用@SkipInterceptor()装饰器跳过该接口的拦截器,但这会破坏响应格式的统一性,仅适合特殊场景。
@SkipInterceptor(TransformInterceptor) @Post('some-path') async someFunction( @Response() reply: Fastify.FastifyReply, ) { return reply .status(isATeapot ? HttpStatus.I_AM_A_TEAPOT : HttpStatus.OK) .send({ data: someData }); }
推荐方案
优先选择方案1,它既保留了拦截器的统一包装逻辑,又能灵活设置响应状态码,代码简洁且符合NestJS的设计理念。
内容的提问来源于stack exchange,提问作者okhobb
相关产品推荐
相关产品推荐

