NestJS拦截器执行redirect报Cannot set headers after they are sent错误
问题场景
请求路径为 /login/login.html,需要重定向至 /login,因此在自定义UricheckInterceptor拦截器中编写重定向判断逻辑,拦截器代码如下:
export class UricheckInterceptor implements NestInterceptor { constructor(private uri: string[]) {} intercept(context: ExecutionContext, next: CallHandler): Observable<any> { const response = context.switchToHttp().getResponse(); const request = context.switchToHttp().getRequest(); const { pathname } = parseurl(request); const pathLength = pathname.length; const pathSlash = pathname.lastIndexOf('/'); const pathLast = pathname.substring(pathSlash + 1, pathLength); const queryString = request.query; if (pathLast === this.uri[1]) { if (Object.keys(queryString).length > 0) { if (queryString.code !== undefined && queryString.bst !== undefined) { response.redirect(301, `./${this.uri[0]}?code=${queryString.code}&bst=${queryString.bst}`); next.handle(); } } else { return next.handle(); } } }
问题现象
重定向逻辑触发后,服务端抛出未捕获的Promise错误,错误信息如下:
This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client....
问题诉求
经多次尝试无法定位问题根源,需要确认代码中存在的错误点。
错误点定位
代码存在3个核心问题,直接导致报头重复发送的错误:
- 重定向后未终止请求流程:执行
response.redirect(301, xxx)时,服务端已经向客户端发送了完整的301响应报头,后续再调用next.handle()会让请求继续流转到后续的拦截器、控制器逻辑,尝试再次向客户端写入响应报头,直接触发ERR_HTTP_HEADERS_SENT错误。 - 判断分支存在逻辑漏洞:当前if嵌套逻辑存在覆盖缺口,当
pathLast === this.uri[1]但query参数为空、或者缺少code/bst参数时,拦截器既没有返回响应,也没有放行请求,会导致请求悬挂或执行逻辑异常。 - 重定向路径存在隐患:使用相对路径
./拼接重定向地址,在多层路径场景下容易出现路径拼接错误,直接使用绝对路径更稳定。
修复后参考代码
export class UricheckInterceptor implements NestInterceptor { constructor(private uri: string[]) {} intercept(context: ExecutionContext, next: CallHandler): Observable<any> { const response = context.switchToHttp().getResponse(); const request = context.switchToHttp().getRequest(); const { pathname } = parseurl(request); const pathSlash = pathname.lastIndexOf('/'); const pathLast = pathname.substring(pathSlash + 1, pathname.length); const queryString = request.query; if (pathLast === this.uri[1]) { // 直接校验必填参数,无需先判断query对象长度 if (queryString.code !== undefined && queryString.bst !== undefined) { // 发送重定向响应后直接return,不再执行后续逻辑 response.redirect(301, `/${this.uri[0]}?code=${queryString.code}&bst=${queryString.bst}`); return; } } // 所有不满足重定向条件的请求统一放行 return next.handle(); } }
内容的提问来源于stack exchange,提问作者Curtis
相关产品推荐
相关产品推荐

