Angular 11/13:仅无显式错误处理时触发的4xx/5xx拦截器开发
解决方案:Angular 11/13 条件式错误处理HttpInterceptor
要实现仅当请求未显式指定错误处理函数时才展示错误提示的需求,核心思路是跟踪每个请求的订阅状态,在拦截器中判断是否需要触发全局错误提示。以下是具体实现步骤:
1. 定义上下文标记Token
创建一个InjectionToken,用来在请求上下文中存储该请求是否有自定义错误处理的标记:
import { InjectionToken } from '@angular/core'; export const HAS_ERROR_HANDLER = new InjectionToken<boolean>('HAS_ERROR_HANDLER');
2. 包装HttpClient,跟踪错误处理状态
创建自定义HttpService包装Angular原生HttpClient,在订阅时判断是否传入了错误处理回调,并将状态写入请求上下文:
import { Injectable } from '@angular/core'; import { HttpClient, HttpContext } from '@angular/common/http'; import { Observable } from 'rxjs'; import { HAS_ERROR_HANDLER } from './has-error-handler.token'; @Injectable({ providedIn: 'root' }) export class HttpService { constructor(private http: HttpClient) {} // 封装GET方法,同理可扩展POST/PUT/DELETE等 get<T>(url: string, options?: { context?: HttpContext }): Observable<T> { const request$ = this.http.get<T>(url, options); return this.wrapObservable(request$, options?.context); } private wrapObservable<T>(source$: Observable<T>, context?: HttpContext): Observable<T> { return new Observable<T>((subscriber) => { const subscription = source$.subscribe({ next: value => subscriber.next(value), error: err => { // 判断订阅者是否传入了错误处理函数 const hasErrorHandler = typeof subscriber.error === 'function'; // 将状态写入上下文(若存在) if (context) { context.set(HAS_ERROR_HANDLER, hasErrorHandler); } subscriber.error(err); }, complete: () => subscriber.complete() }); return subscription; }); } }
3. 实现HttpInterceptor
编写拦截器,拦截4xx/5xx错误,根据上下文标记决定是否展示全局错误提示:
import { Injectable } from '@angular/core'; import { HttpRequest, HttpHandler, HttpInterceptor, HttpErrorResponse, } from '@angular/common/http'; import { catchError } from 'rxjs/operators'; import { throwError } from 'rxjs'; import { HAS_ERROR_HANDLER } from './has-error-handler.token'; // 替换成你项目中使用的错误提示组件(比如Material SnackBar、自定义弹窗等) import { MatSnackBar } from '@angular/material/snack-bar'; @Injectable() export class ErrorHandlingInterceptor implements HttpInterceptor { constructor(private snackBar: MatSnackBar) {} intercept(request: HttpRequest<unknown>, next: HttpHandler) { // 初始化上下文标记为false const context = request.context.set(HAS_ERROR_HANDLER, false); const modifiedRequest = request.clone({ context }); return next.handle(modifiedRequest).pipe( catchError((error: HttpErrorResponse) => { // 仅处理4xx/5xx状态码错误 if (error.status >= 400 && error.status < 600) { // 获取上下文标记 const hasErrorHandler = modifiedRequest.context.get(HAS_ERROR_HANDLER); // 无自定义错误处理时,展示全局提示 if (!hasErrorHandler) { this.snackBar.open( `请求失败: ${error.message || `状态码 ${error.status}`}`, '关闭', { duration: 5000, panelClass: ['error-toast'] } ); } } return throwError(() => error); }) ); } }
4. 注册拦截器
在AppModule中注册该拦截器:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { ErrorHandlingInterceptor } from './error-handling.interceptor'; import { AppComponent } from './app.component'; @NgModule({ declarations: [AppComponent], imports: [BrowserModule, HttpClientModule], providers: [ { provide: HTTP_INTERCEPTORS, useClass: ErrorHandlingInterceptor, multi: true, }, ], bootstrap: [AppComponent], }) export class AppModule {}
使用示例
场景1:自定义错误处理(拦截器不生效)
// 传入了error回调,拦截器不会展示全局提示 this.httpService.get('/api/data').subscribe( data => console.log('请求成功:', data), err => console.error('自定义错误处理:', err) );
场景2:无自定义错误处理(拦截器自动展示提示)
// 未传入error回调,拦截器会弹出全局错误提示 this.httpService.get('/api/data').subscribe( data => console.log('请求成功:', data) );
原理说明
- 通过
HttpContext存储每个请求的错误处理状态,这是Angular 10+提供的安全传递请求元数据的方式,兼容11/13版本。 - 自定义
HttpService包装原生请求,在订阅阶段判断是否传入了错误处理函数,并更新上下文标记。 - 拦截器在捕获到4xx/5xx错误时,读取上下文标记,决定是否触发全局错误提示。
内容的提问来源于stack exchange,提问作者Elio
相关产品推荐
相关产品推荐

