使用 Angular 依赖注入定义 Error Handler 的一个实际例子

举报
汪子熙 发表于 2025/03/01 10:54:18 2025/03/01
【摘要】 代码内容:export const PROPAGATE_ERROR_TO_SERVER = new InjectionToken< (error: unknown) => void>('PROPAGATE_ERROR_RESPONSE');这段代码很简单,但却蕴含了不少设计思路和技术要点,特别是在 Angular 依赖注入、类型系统、安全性及系统健壮性方面。PROPAGATE_ERROR_...

代码内容:

export const PROPAGATE_ERROR_TO_SERVER = new InjectionToken<
  (error: unknown) => void
>('PROPAGATE_ERROR_RESPONSE');

这段代码很简单,但却蕴含了不少设计思路和技术要点,特别是在 Angular 依赖注入、类型系统、安全性及系统健壮性方面。PROPAGATE_ERROR_TO_SERVER 是一个 Angular 中的 InjectionToken,用于在运行时通过依赖注入来提供特定类型的服务或对象。

部分概念

InjectionToken

InjectionToken 是 Angular 提供的一种机制,用于确保在依赖注入时能够正确地匹配到所需的对象或服务。使用 InjectionToken 的好处是,它可以为非类类型的依赖项创建注入标识符。这个标识符可以在 DI 系统中唯一地标识要注入的服务。

签名:

class InjectionToken<T> {
  constructor(desc: string, options?: {
    providedIn?: Type<any> | 'root' | 'platform' | 'any' | null, factory?: () => T})
}

参数:

  • desc: 一个描述性的字符串,唯一标识这个 token。
  • options: 可选参数,用于配置这个 token 的一些属性,比如提供范围 (providedIn) 或工厂方法 (factory)。

在这段代码中,new InjectionToken<(error: unknown) => void> 创建了一个新的类型安全的 InjectionToken,这个 Token 可用于提供一个处理错误的方法。

详解代码各部分含义

export const PROPAGATE_ERROR_TO_SERVER = new InjectionToken<
  (error: unknown) => void
>('PROPAGATE_ERROR_RESPONSE');
  • export const PROPAGATE_ERROR_TO_SERVER: export 关键字意味着这个 Token 可以被模块外部引用。const 确保它是一个不可变的常量,PROPAGATE_ERROR_TO_SERVER 是这个常量的名称。
  • = new InjectionToken<(error: unknown) => void>: 创建一个新的 InjectionToken,这个 Token 的类型是 (error: unknown) => void。换句话说,这个 Token 对应的值是一个接受一个 unknown 类型参数并返回 void 的函数。
  • ('PROPAGATE_ERROR_RESPONSE'): 这是这个 InjectionToken 的描述字符串,通常是这个 Token 的语义化描述,以帮助开发者了解这个 Token 的用途。

应用示例

为了更好地理解它的用途,我们来看一个具体的应用示例。假设我们有这样一个场景,我们希望在 Angular 应用中,捕获到所有的错误,并通过某种机制(例如 HTTP 请求)将这些错误传输到服务器进行记录和存储。

首先,我们定义一个服务,这个服务将被注入并处理这些错误。

创建一个处理错误的服务类:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { PROPAGATE_ERROR_TO_SERVER } from './error-token';

@Injectable({
  providedIn: 'root'
})
export class ErrorHandlingService {
  constructor(private http: HttpClient) {}

  propagateErrorToServer(error: unknown): void {
    this.http.post('/api/log-error', { error }).subscribe();
  }
}

在这个服务中,我们使用了 Angular 的 HttpClient 模块建立 HTTP 请求,将传入的错误对象发送到一个 /api/log-error 的端点。这个服务将被注入到其他地方,具体处理错误并将错误传输到服务器。

然后,在模块层面我们进行提供设置:

创建模块文件 app.module.ts

import { NgModule, ErrorHandler } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
import { ErrorHandlingService } from './error-handling.service';
import { PROPAGATE_ERROR_TO_SERVER } from './error-token';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, HttpClientModule],
  providers: [
    {
      provide: PROPAGATE_ERROR_TO_SERVER,
      useFactory: (service: ErrorHandlingService) => service.propagateErrorToServer,
      deps: [ErrorHandlingService]
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

在这里,我们使用 providers 数组将 PROPAGATE_ERROR_TO_SERVER 这个 InjectionToken 提供到依赖注入系统中。

  • useFactory: 定义一个工厂方法,根据传入的 ErrorHandlingService 实例提供一个具体的处理错误的方法。
  • deps: 表示这个工厂方法需要依赖的服务列表,这里是 ErrorHandlingService

之后,我们创建一个全局错误处理器(ErrorHandler),并注入刚才定义的 Token:

创建自定义错误处理器类 global-error-handler.ts

import { ErrorHandler, Inject, Injectable } from '@angular/core';
import { PROPAGATE_ERROR_TO_SERVER } from './error-token';

@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
  constructor(
    @Inject(PROPAGATE_ERROR_TO_SERVER) private propagateError: (error: unknown) => void
  ) {}

  handleError(error: unknown): void {
    this.propagateError(error);
    console.error(error);
  }
}

在这个全局错误处理器中,我们使用 Angular 提供的 ErrorHandler 接口,并通过构造函数注入我们之前定义的 PROPAGATE_ERROR_TO_SERVER Token 对应的方法。

最后在 app.module.ts 中,提供这个全局错误处理器:

...
providers: [
  {
    provide: PROPAGATE_ERROR_TO_SERVER,
    useFactory: (service: ErrorHandlingService) => service.propagateErrorToServer,
    deps: [ErrorHandlingService]
  },
  {
    provide: ErrorHandler,
    useClass: GlobalErrorHandler
  }
],
...

通过这样的设置,当应用中任意地方发生错误时,都会被我们自定义的 GlobalErrorHandler 捕获,并传输到服务器。

RxJS 在错误处理中的应用

在实际应用中,错误处理很少是简单的一步完成的事情,尤其当多个异步操作组合在一起时,RxJS 提供了极大的便利。

例如,如果我们希望在进行 HTTP 请求或者其他异步操作时,捕获错误并采用某种机制处理,可以使用 RxJS 提供的 catchError 操作符。

以下是一个具体的示例,在一个服务中使用 RxJS 处理错误:

创建一个服务类 data.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { catchError } from 'rxjs/operators';
import { of } from 'rxjs';
import { PROPAGATE_ERROR_TO_SERVER } from './error-token';
import { Inject } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class DataService {
  constructor(
    private http: HttpClient,
    @Inject(PROPAGATE_ERROR_TO_SERVER) private propagateError: (error: unknown) => void
  ) {}

  getData() {
    return this.http.get('/api/data').pipe(
      catchError(error => {
        this.propagateError(error);
        return of(null);  // 使用一个空的可观察对象(Observable)作为回退
      })
    );
  }
}

在这个例子中,我们使用 HttpClient 发起一个 GET 请求,同时使用 catchError 操作符捕获任何可能发生的错误,并通过注入的 propagateError 方法处理这些错误。

由于 catchError 接受一个返回新的可观察对象(Observable)的函数作为参数,这样我们可以在错误发生时,返回一个替代的值,保证应用的健壮性。

综合解读

这段代码展示了 Angular 如何通过强类型系统和依赖注入机制,实现复杂的逻辑分离和功能复用。在这段示例中,我们实现了一个统一的错误处理机制,可以在应用中任意位置注入并使用,同时确保了代码的优雅性和易维护性。

通过自定义的 InjectionToken 和 DI 提供机制,能够很方便地改变错误处理策略,而不需要修改核心逻辑。当结合 rxjs 进行复杂异步操作时,通过 rxjs 的强大功能和灵活的操作符,能够进一步增强应用的健壮性和容错性。

这种设计不仅强调了代码的模块化和可测试性,同时也充分利用了 Angular 框架的依赖注入特性,提供了一种动态扩展、灵活替换的解决方案。在团队协作和大型项目中,这种模式能够显著提升代码质量和开发效率。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。