如何仅对幂等方法重试失败的 http 请求?

How to retry failed http requests for idempotent methods only?

我正在编写一个 Angular 10 应用程序。我需要创建一个拦截器,它将重试所有失败的 idempotent HTTP 请求。在阅读了一些在线教程之后,我编写了一个我认为可以解决问题的拦截器。然后我写了一个测试来验证它是否正常工作。不幸的是,我的测试不会失败;即使我修改拦截器使测试失败,它也不会失败。

这是拦截器:

export class RetryInterceptor implements HttpInterceptor {

  // private IDEMPOTENT_METHODS = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'PUT', 'TRACE']; // this is the real prod code.
  private IDEMPOTENT_METHODS = ['POST']; // this should cause my test to fail, but it doesn't.

  constructor(private router: Router, private logger: NGXLogger) {}

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    console.log('here'); // I've confirmed that this is being printed to the console.
    if (this.IDEMPOTENT_METHODS.includes(request.method)){
      console.log('there'); // I've confirmed that this is being printed to the console.
      return next.handle(request)
        .pipe(
          retry(1));
    }
    else{
      return next.handle(request);
    }
  }
}

这是测试:

describe('RetryInterceptor', () => {

  let httpTestingController: HttpTestingController;
  let http: HttpClient;
  let router: Router;
  let location: Location;
  let fixture;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        HttpClientTestingModule,
        LoggerTestingModule,
        RouterTestingModule,
        TranslateModule.forRoot({
          loader: {
            provide: TranslateLoader,
            useFactory: httpTranslateLoader,
            deps: [HttpClient]
          }
        })
      ],
      providers: [
        {
          provide: HTTP_INTERCEPTORS,
          useClass: RetryInterceptor,
          multi: true,
        },
      ],
    });

    httpTestingController = TestBed.inject(HttpTestingController);
    http = TestBed.inject(HttpClient);
    router = TestBed.inject(Router);
    location = TestBed.inject(Location);
    fixture = TestBed.createComponent(AppRootComponent);
    router.initialNavigation();
  });

  it('should not retry POSTs', fakeAsync( // I am expecting this to fail. 
    () => {
      http.post('/api/resource', {}).subscribe(response => expect(response).toBeTruthy());
      httpTestingController.expectOne({method: 'POST', url: '/api/resource'}).error(
        new ErrorEvent('network error', { message: 'bad request' }), { status: 400 });

      tick(); // the first tick is intended to make a call to the httpTestingController, which will return an error, which will be caught by my Interceptor.
      tick(); // on the second tick, my Interceptor should retry the failed call. this should, in turn, fail the test, because I have not set up a second expectation.
    })
  );
});

谁能发现我做错了什么?我的拦截器坏了吗?还是我的测试失败了?

拦截器没问题。您需要在测试结束时验证您的 http 测试控制器以捕获意外请求:

// insert this at end of test or in an afterEach() block
httpTestingController.verify()

一般来说,任何与 http 请求相关的单元测试都应该以验证结束。

文档:

https://angular.io/guide/http#testing-http-requests https://angular.io/api/common/http/testing/HttpTestingController#verify