Angular6.Правильный способ обработки ошибок в httpClient - PullRequest
0 голосов
/ 27 августа 2018

Мне интересно об обработке ошибок из наблюдаемых потоков.Я прочитал статью об использовании rxjs и асинхронного канала в Angular.Здесь было написано, что мы должны избегать использования оператора подписки и пытаться заменить его асинхронным каналом.Но теперь, когда я пытаюсь написать какой-либо запрос post / put, мы должны подписаться, поэтому я немного запутался, когда мне нужно изменить состояние моего компонента.

submitForm() {
  this.restService.add(this.user.id, this.form.value)
    .pipe(
      tap(() => { this.isAdded = true }),
      catchError((error: HttpErrorResponse) => {
          this.responseMsg = 'Something went wrong'
          return throwError('notFound')
        }
      )
    ).subscribe();
  }

Или я должен обработать это в подписке

submitForm() {
      this.restService.add(this.user.id, this.form.value)
        .pipe(
          tap(() => { this.isAdded = true }),
        ).subscribe( 
            () => {}, 
            () => {this.responseMsg = 'Something went wrong'}
       );
 }

Какое решение лучше?

Ответы [ 3 ]

0 голосов
/ 27 августа 2018

Вот как вы правильно используете асинхронную трубу.Когда вы используете асинхронные каналы, вам не нужно подписываться, потому что они делают жизненный цикл наблюдаемого для вас.Подписка и уничтожить подписку, чтобы избежать утечек памяти.Если вы не используете асинхронные каналы, вы должны сделать это вручную.

SERVICE.TS

import { EquipmentID } from './../../../shared/model/equipmentID.model';
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { PickupAvailability } from '../models/pickup-availability.model';
import { catchError } from 'rxjs/operators';
import { ErrorHandlingService } from '../../../shared/services/error-handling.service';
@Injectable()
export class PickupAvaibilityService {
  BASE_URL = '/sxrw-ship/api/v1';
  AMOUNT_DUE_URL = '/shipments/search';

  constructor(
    private http: HttpClient,
    private errorHandler: ErrorHandlingService
  ) {}

  getAmountDue(equipmentID: EquipmentID[]): Observable<PickupAvailability> {
    return this.http
      .post<PickupAvailability>(
        this.BASE_URL + this.AMOUNT_DUE_URL,
        equipmentID
      )
      .pipe(catchError(this.errorHandler.handleError));
  }
}

Тогда в моем пользовательском сервисе обработки ошибок у вас будет что-то вроде этого:

ERRORHANDLER.SERVICE.TS

import { Injectable } from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';
import { _throw } from 'rxjs/observable/throw';
@Injectable()
export class ErrorHandlingService {
  constructor() {}

  handleError(error: HttpErrorResponse) {
    //To know the version of RxJS npm list --depth=0 (I for this example im on version 5.5)
    if (error.error instanceof ErrorEvent) {
      // A client-side or network error occurred. Handle it accordingly.
      console.error('An error occurred: ', error.error.message);
    } else {
      // The backend returned an unsuccessful response code.
      // The response body may contain clues as to what went wrong,
      console.error(
        `Backend returned code ${error.status}` + ` body was: ${error.message}`
      );
    }
    // return an observable with a user-facing error message
    return _throw('Something bad happened; please try again later.');
  }
}

То, как я сделал свой обработчик ошибок, - это то, как это делается на официальном сайте Angular https://angular.io/guide/http

Теперь в компоненте, который будет использоватьслужба, которая вызывает нас http, вы объявляете переменную типа Observable (рекомендуется использовать $ sing)

COMPONENT.TS

pickupAvailability$: Observable<PickupAvailability>;

  getPickupDate(pa: PickupAvailability) {
    let id = pa.equipmentInitial + pa.equipmentNumber;
    this.equipmentSummary$ = this.pickupDateService
      .getPickupDate(id)
      .pipe(
        map(
          pd =>
            new EquipmentSummary(
              id,
              pd.pickupDate,
              'TODO',
              pa.totalPremiseCharges
            )
        )
      );
  }

HTML

<div *ngIf="(pickupAvailability$ | async) as pickupAvailability">
  <!-- TODO move to table -->
  <div *ngFor="let pa of pickupAvailability">
    <button pButton type="button" label="Pay ${{ pa.totalPremiseCharges }}" class="x-primary-gray-100" (click)='getPickupDate(pa)'>
    </button>
  </div>
  <div *ngIf="(equipmentSummary$ | async) as es">
    <app-pay-storage [equipmentSummary]="es"></app-pay-storage>
  </div>
</div>

Вы можете проверить это, выполнив следующее:

{{pickupAvailability$ | async | json}}

когда вы действительно собираетесь его использовать, удалите трубу json

0 голосов
/ 27 августа 2018

Вы можете рассмотреть возможность использования HttpClient interceptor .

@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
  constructor(public auth: AuthService) {}
  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

       return next.handle(request)
            .pipe(catchError(err => {
                 // handle your logic there
             }));
   }
}

. При этом все ваши запросы Http будут обрабатываться одинаково в одном и том же месте.Вам не нужно внедрять службы управления ошибками.

Больше информации там: Обработка ошибок углового перехватчика httpClient

0 голосов
/ 27 августа 2018

Если вы хотите обрабатывать только ошибки из внутреннего API (ошибка HTTP), вы можете просто обработать их в функции подписки.

Также вы можете использовать собственный обработчик ошибок для обработки всех ваших ошибок HTTP:

import {ErrorHandler} from "@angular/core";
import {UNAUTHORIZED, BAD_REQUEST, FORBIDDEN} from "http-status-codes";
import {Router} from "@angular/router";
import {ToastsManager, Toast, ToastOptions} from "ng2-toastr";

@Injectable()
export class myAppErrorHandler implements ErrorHandler {

  static readonly REFRESH_PAGE_ON_TOAST_CLICK_MESSAGE: string = "An error occurred: Please click this message to refresh";
  static readonly DEFAULT_ERROR_TITLE: string = "Something went wrong";

  constructor(private router: Router,private toastManager: ToastsManager){};


  public handleError(error: any) {
    console.error(error);
    let httpErrorCode = error.httpErrorCode;
    switch (httpErrorCode) {
      case UNAUTHORIZED:
          this.router.navigateByUrl("/login");
          break;
      case FORBIDDEN:
          this.router.navigateByUrl("/unauthorized");
          break;
      case BAD_REQUEST:
         this.showError(error.message);
          break;
      default:
         this.showError(REFRESH_PAGE_ON_TOAST_CLICK_MESSAGE);
    }
  }

  private showError(message:string){
    this.toastManager.error(message, DEFAULT_ERROR_TITLE, { dismiss: 'controlled'}).then((toast:Toast)=>{
            let currentToastId:number = toast.id;
            this.toastManager.onClickToast().subscribe(clickedToast => {
                if (clickedToast.id === currentToastId) {
                    this.toastManager.dismissToast(toast);
                    window.location.reload();
                }
            });
        });
  }
}

Затем скажите angular использовать его вместо обработчика ошибок по умолчанию, чтобы добавить его к вашим провайдерам.раздел в вашем app.module.ts следующий:

  providers: [
      {provide: ErrorHandler, useClass: myAppErrorHandler }
  ]
...