Угловая обработка ошибок HttpClient ConcatMap 7 - PullRequest
1 голос
/ 29 марта 2019

У меня есть код, который выполняет загрузку большого файла небольшими порциями. Вот что у меня есть:

upload(project: Project): Observable<any> {

    var chunks = this.chunkFiles(project);

    var totalPercent = project.file
        .map(file => Math.ceil(file.size / this.chunkSize))
        .reduce((sum, curr) => sum + curr) * 100;

    var finishedPercent = 0;
    var currentPercent = 0;

    return from(chunks)
        .pipe(
            concatMap(request =>
            {
                return this.http.request(request)
                    .pipe(
                        retryWhen(error =>
                        {
                            return interval(1000)
                                .pipe(
                                    flatMap(count =>
                                    {
                                        if (count == 2)
                                        {
                                            return throwError(error);
                                        }
                                        return of(count);
                                    })
                                );
                        }));
            }),
            map(event =>
            {
                if (event.type === HttpEventType.UploadProgress)
                {
                    var progress = event as HttpProgressEvent;
                    currentPercent = Math.round(100 * progress.loaded / progress.total);
                    return {
                        type: event.type,
                        loaded: finishedPercent + currentPercent,
                        total: totalPercent
                    };
                }
                else if (event instanceof HttpResponse) 
                {
                    finishedPercent += 100;
                    if (finishedPercent == totalPercent)
                    {
                        return event;
                    }
                }
            }),
            filter(response => response !== undefined)
        );
}

Предполагается, что он начинает загрузку фрагментов файла, и после сбоя одной из загрузок фрагмента он должен остановиться, и ошибка должна распространиться до кода, вызывающего мою функцию загрузки. Этот конкретный код выглядит следующим образом:

onStartUpload = this.actions$
    .ofType(forRoot.START_UPLOAD).pipe(
    switchMap((action: forRoot.StartUpload) =>
        this.uploadService
            .upload(action.project).pipe(
            map((event) => {
                if (event.type === HttpEventType.UploadProgress) {
                    const progress = event as HttpProgressEvent;
                    const percentDone = Math.round(100 * progress.loaded / progress.total);
                    return new forRoot.UploadProgress(percentDone);
                } else if (event instanceof HttpResponse) {
                    return new forRoot.UploadSucceeded();
                } else {
                    console.log(event);
                }
            }),
            filter(a => a !== undefined),
            catchError(error => {
                if (error instanceof HttpErrorResponse && error.status === 400) {
                    const message = this.getMessage(error);
                    return of(new forRoot.UploadFailed(message || "Bad request"));
                } else {
                    return of(new forRoot.UploadFailed("Network error"));
                }
            })))
        );

Проблема в том, что я получаю только сообщение «Ошибка сети», в то время как я ожидаю что-то со статусом ошибки 400 (поэтому я должен получать либо конкретное сообщение, либо «Плохой запрос».

Я предполагаю, что неправильно обрабатываю ошибку в retryWhen, но я пробовал несколько разных идей, и ни одна из них, похоже, не работает. Чего мне не хватает?

Изменить, чтобы показать рабочий код без повторных попыток:

Вот код с корректной обработкой сообщений об ошибках, но без повторных попыток:

return from(chunks)
    .pipe(
        concatMap(request =>
        {
            return this.http.request(request)
        }),
        map(event =>
        {
            if (event.type === HttpEventType.UploadProgress)
            {
                var progress = event as HttpProgressEvent;
                currentPercent = Math.round(100 * progress.loaded / progress.total);
                return {
                    type: event.type,
                    loaded: finishedPercent + currentPercent,
                    total: totalPercent
                };
            }
            else if (event instanceof HttpResponse) 
            {
                finishedPercent += 100;
                if (finishedPercent == totalPercent)
                {
                    return event;
                }
            }
        }),
        filter(response => response !== undefined)
    );

Я предполагал, что есть что-то, что я могу добавить к вызову http.request, чтобы повторить указанное количество раз с задержкой между попытками, а затем, если все они завершатся неудачно, вывести ошибку, как это делает http.request. , Кажется, что происходит то, что он проходит после кода concatMap к коду карты. Я могу добавить вызов console.log (событие) и посмотреть сообщение об ошибке, возвращаемое с сервера. Я все еще довольно новичок в rxjs, так что, может быть, я не понимаю, но я ожидал, что как только ошибка произойдет, она не выполнит код карты.

1 Ответ

0 голосов
/ 30 марта 2019

Проблема здесь:

retryWhen(error => {     // << error is a stream
  return interval(1000)
    .pipe(
        flatMap(count => {
          if (count == 2){
              return throwError(error); // so here a stream is thrown
          }

          return of(count);
      })
    );
})

error аргумент - это не единственная ошибка, а фактически наблюдаемый из всех ошибок.

Чтобы исправить это, вы должны вернуть наблюдаемое производноеиз error, например:

retryWhen(errors$ =>
  errors$.pipe(
    delay(1000)
  )
)

Обновленный пример ограниченной обработки ошибок

retryWhen(errors =>
  errors.pipe(
    // switchMap to add limiting logic
    switchMap((error, index) =>
      index == 3                      // if it is a 4th error
      ? throwError(error)             //  -- we rethrow it upstream
      : of(error).pipe( delay(1000) ) //  -- delay a retry otherwise
    )
  )
)

Здесь retryWhen пример с экспоненциальным откатом .

И статья об обработке ошибок в rxjs (с примечанием о retryWhen досрочном завершении).

Надеюсь, это поможет

...