You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular中RxJS订阅的error方法完全不执行问题排查

问题描述

我有一个向Node.js后端发起HTTP请求的Angular组件,未使用NgRx,通过服务处理请求。遇到的问题是:收到后端错误响应时,订阅中的error方法完全不执行,仅能在开发者工具看到状态码和消息,但next回调运行正常。

相关代码

组件代码

this.httpCF
  .getPercentagesByName(this.name)
  .subscribe({
    next: (response) => {
      console.log('response get by name');
      console.log(response); // 正常工作
    },
    error: (error) => {
      console.log('error'); // 错误响应时完全不执行...
    },
  });

服务代码

getPercentagesByName(name: string) {
  return this.http.post(
    environment.ApiGetByName,
    { name: name },
    { headers: this.headers }
  );
} 

Node.js后端响应代码

尝试过两种写法:

res.status(404).send({ msg: 'Element not found' })
res.status(404).json({ msg: 'Element not found' })

补充:HttpInterceptor代码

export class TokenInterceptor implements HttpInterceptor {
  constructor(
    private auth: AngularFireAuth,
    private router: Router,
    private _snackBar: MatSnackBar
  ) {}

  intercept(
    request: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {

    return next.handle(request).pipe(
      catchError((error: HttpEvent<any>) => {
        if (error instanceof HttpErrorResponse) {
          if (!(error.error instanceof ErrorEvent)) {

            if (error.status === 401) {
              // token过期时,登出并跳转到登录页
              this.auth.signOut().then(() => {
                if (this.router.url !== '/auth/login') {
                  this._snackBar.open(
                    'Session expired, please login...',
                    'Close',
                    {
                      duration: 5000,
                      panelClass: 'expiredtoken',
                    }
                  );
                  this.router.navigate(['/auth/login']);
                }
              });
            }
          }
        }
        return of(error);
      })
    );
  }
}
问题原因与解决方法

问题出在HttpInterceptor的catchError处理逻辑上:

拦截器捕获到错误后,使用return of(error)将错误包装成了一个正常的Observable流,导致订阅者的next回调接收到错误对象,而非触发error回调。

修正后的拦截器代码

import { throwError } from 'rxjs'; // 需导入throwError操作符

export class TokenInterceptor implements HttpInterceptor {
  constructor(
    private auth: AngularFireAuth,
    private router: Router,
    private _snackBar: MatSnackBar
  ) {}

  intercept(
    request: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {

    return next.handle(request).pipe(
      catchError((error: HttpErrorResponse) => {
        if (error instanceof HttpErrorResponse) {
          if (!(error.error instanceof ErrorEvent)) {
            if (error.status === 401) {
              // token过期时,执行登出与跳转逻辑
              this.auth.signOut().then(() => {
                if (this.router.url !== '/auth/login') {
                  this._snackBar.open(
                    '会话已过期,请重新登录...',
                    '关闭',
                    {
                      duration: 5000,
                      panelClass: 'expiredtoken',
                    }
                  );
                  this.router.navigate(['/auth/login']);
                }
              });
            }
          }
        }
        // 重新抛出错误,触发订阅者的error回调
        return throwError(() => error);
      })
    );
  }
}

关键说明

  • of(error)会创建一个发射错误对象的正常Observable,订阅者的next回调会接收该对象,不会触发error回调。
  • throwError(() => error)会创建一个发射错误的Observable,确保订阅者的error回调被正确触发。
  • 必须从rxjs包中导入throwError操作符才能使用。

内容的提问来源于stack exchange,提问作者Federico Rojas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 17:22:35