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

Angular HTTP拦截器404请求测试报错:未捕获异常排查

Angular HTTP拦截器404请求测试报错排查

问题现象

编写用于追踪/取消追踪HTTP请求的Angular拦截器时,测试404状态码的请求会出现Uncaught [object Object]错误,尽管拦截器中已经通过catchError处理了错误。

代码片段

拦截器代码

@Injectable()
export class TrafficInterceptor implements HttpInterceptor {

    constructor(private trafficService: TrafficService) {
    }

    intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
        this.trafficService.trackRequest(request);
        return next.handle(request).pipe(
            catchError((error: HttpErrorResponse) => {
                this.trafficService.unTrackRequest(request);
                return throwError(error);
            }),
            finalize(() => {
                this.trafficService.unTrackRequest(request);
            })
        );
    }
}

测试代码

describe('TrafficInterceptor', () => {
    let interceptor: TrafficInterceptor;
    let trafficStateStore: TrafficStateStore;
    let httpTestingController: HttpTestingController;
    let http: HttpClient;

    const reqURL = '/somewebsite/foo';
    const httpMethods = [
        {method: 'get'},
        {method: 'delete'},
        {method: 'post'},
        {method: 'put'},
        {method: 'patch'}
    ];

    beforeEach(() => {
        TestBed.configureTestingModule({
            imports: [HttpClientTestingModule],
            providers: [
                {provide: HTTP_INTERCEPTORS, useClass: TrafficInterceptor, multi: true},
                TrafficInterceptor,
                TrafficStateStore,
                TrafficService
            ]
        });
        interceptor = TestBed.inject(TrafficInterceptor);
        trafficStateStore = TestBed.inject(TrafficStateStore);

        httpTestingController = TestBed.inject(HttpTestingController);
        http = TestBed.inject(HttpClient);
    });

    afterEach(() => {
        httpTestingController.verify();
    });

    it('should be created', () => {
        expect(interceptor).toBeTruthy();
    });

    httpMethods.forEach(({method}) => {
        it(`should track and untrack a successfull HTTP - ${method} metod`, () => {
            httpRequest(reqURL, method);
            const req = httpTestingController.expectOne(reqURL);

            expect(trafficStateStore.get('showPendingOverlay')).toBeTrue();
            expect(trafficStateStore.get('isRequestProcessing')).toBeTrue();

            req.flush({});

            expect(trafficStateStore.get('showPendingOverlay')).toBeFalse();
            expect(trafficStateStore.get('isRequestProcessing')).toBeFalse();
        });


        it(`should track and untrack a unsuccessfull HTTP - ${method} metod`, () => {
            httpRequest(reqURL, method);
            const req = httpTestingController.expectOne(reqURL);

            expect(trafficStateStore.get('showPendingOverlay')).toBeTrue();
            expect(trafficStateStore.get('isRequestProcessing')).toBeTrue();

            req.flush('', {status: 404, statusText: 'error'});

            expect(trafficStateStore.get('showPendingOverlay')).toBeFalse();
            expect(trafficStateStore.get('isRequestProcessing')).toBeFalse();
        });
    });

    function httpRequest(url: string, method: string, data?: Record<string, boolean>, params: Record<string, boolean> = {}): void {
        if (data) {
            http[method](url, data, {params}).subscribe();
        } else {
            http[method](url, {params}).subscribe();
        }
    }
});

问题原因

测试中的httpRequest函数调用subscribe()时,仅使用无参数的订阅方式,未处理错误回调。当请求返回404错误时,拦截器的catchError会重新抛出错误,而该错误未被订阅的错误处理器捕获,导致测试环境抛出未捕获的错误。

另外,拦截器中catchError和finalize都调用了unTrackRequest,会导致错误场景下该方法被调用两次,这是潜在逻辑问题,但不是当前报错的直接原因。

解决方案

1. 为订阅添加错误处理

修改httpRequest函数中的订阅逻辑,添加错误回调以捕获抛出的错误:

function httpRequest(url: string, method: string, data?: Record<string, boolean>, params: Record<string, boolean> = {}): void {
    if (data) {
        http[method](url, data, {params}).subscribe({
            next: () => {},
            error: () => {}
        });
    } else {
        http[method](url, {params}).subscribe({
            next: () => {},
            error: () => {}
        });
    }
}

2. 修复拦截器重复调用unTrackRequest的问题

finalize操作符无论请求成功还是失败都会执行,因此可以移除catchError中的unTrackRequest调用,只保留finalize中的调用,避免重复执行:

intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    this.trafficService.trackRequest(request);
    return next.handle(request).pipe(
        catchError((error: HttpErrorResponse) => {
            return throwError(error);
        }),
        finalize(() => {
            this.trafficService.unTrackRequest(request);
        })
    );
}

以上修改既解决了未捕获错误的问题,也优化了拦截器的逻辑一致性。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 16:27:50