Как правильно уловить реакцию на ошибку в Angular?

#angular #typescript #observable

Вопрос:

мне нужно поймать тело ответа, созданное моим REST API, которое выдает ошибку http 406 с пользовательским телом ответа, но в моем angular я могу поймать только статус заголовка «Неприемлемо», я не могу получить тело, как я могу получить тело вместо строки «Неприемлемо»? и я тоже получил эту ошибку TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable. , которая заключается в том, что я не знаю, какая часть вызывает эту ошибку. Вот как я это делаю :

сначала я создаю глобальный класс обслуживания, который выглядит следующим образом :

   constructor(
    private http: HttpClient,
    private alertx: AlertService) { }

    post<T>(url: string, body?: any, header?: HttpHeaders, param?: HttpParams): Observable<HttpResponse<T>>{
      return this.http.post<T>(url, body,
        {headers : header ? header : this.headers,
        observe: 'response',
        responseType: 'json',
        params: param})
        .pipe(catchError(err => {
          const error = err.error?.error_description || err.error?.message || err.statusText;
          console.log(err);
          console.log('here i am');
          return throwError(error);
        }))
        .pipe(timeout(10000), catchError(err => {
          if (err instanceof TimeoutError) {
            this.alertx.error('Timeout Exception');
            return throwError('Timeout Exception');
          }
        }))
        .pipe(shareReplay());
  }
 

я попытался утешить его.зарегистрируйте ошибку, в ней просто появляется строка «Неприемлемо», и она продолжается с помощью console.log («вот я»); она распечатана правильно, и я не могу найти причину TypeError

вот как я использую функцию выше :

   requestCancel(data: SelectItem, reasonCancel: string): Observable<any> {
      const request: RequestResponse<SelectItem> = new RequestResponse<SelectItem>(data);
      request.message = reasonCancel;
      return this.rest.post<any>(`${environment.apiUrl}${endpoint.sales_order.request_cancel}`, request).pipe(map(
          response => {
              return response.body.data;
          }));
  }
 

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

—————— ОБНОВЛЕНИЕ МОЕГО КОДА ———————

я создаю класс HttpRequestInterceptor для обработки ошибки, вот мой класс :

 @Injectable()
export class HttpRequestInterceptor implements HttpInterceptor {

  user: UserModel;
  prevUrl: string = '';
  constructor(
    private loginSvc: LoginService, 
    private alertx: AlertService,
    private sharedService: SharedService) {
    this.sharedService.currentUserSubject.subscribe(user => this.user = user);
  }

  intercept(httpRequest: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    this.sharedService.setLoading(true, httpRequest.url);
    return next.handle(httpRequest).pipe(catchError(err => {
      if ([401, 403].includes(err.status) /* amp;amp; this.user */) {
        // auto logout if 401 or 403 response returned from api
        this.sharedService.setLoading(false, httpRequest.url);
        this.loginSvc.removeLocal();
      }
      this.sharedService.setLoading(false, httpRequest.url);
      const error = err.statusText || err.statusText   ' '   err.error?.message || err.statusText   ' '   err.error?.error_description;
      this.alertx.error(error, {autoClose: true});
      return throwError(err.error?.message);
    }))
      .pipe(map<HttpEvent<any>, any>((evt: HttpEvent<any>) => {
        if (evt instanceof HttpResponse) {
          this.sharedService.setLoading(false, httpRequest.url);
        }
        return evt;
      }));
  }
}
 

when i console.log my err.error?.message i can catch the error properly, then i the value passed to here :

     post<T>(url: string, body?: any, header?: HttpHeaders, param?: HttpParams): Observable<HttpResponse<T>>{
      return this.http.post<T>(url, body,
        {headers : header ? header : this.headers,
        observe: 'response',
        responseType: 'json',
        params: param})
        .pipe(catchError(err => {
          console.log(err);
          return throwError(err);
        }))
        .pipe(timeout(10000), catchError(err => {
          if (err instanceof TimeoutError) {
            this.alertx.error('Timeout Exception');
            return throwError('Timeout Exception');
          }
        }))
        .pipe(shareReplay());
  }
 

до тех пор, пока я все еще могу правильно поймать ошибку, когда я console.log(err) , но когда я использую post функцию, я не могу поймать ошибку, и я получил ошибку в консоли браузера. Вот моя функция :

   requestCancel(data: SelectItem, reasonCancel: string): Observable<any> {
      const request: RequestResponse<SelectItem> = new RequestResponse<SelectItem>(data);
      request.message = reasonCancel;
      return this.rest.post<any>(`${environment.apiUrl}${endpoint.sales_order.request_cancel}`, request).pipe(map(
          response => {
              return response.body.data;
          }))
          .pipe(catchError(err => {
              // i cannot get anything here
              console.log(err);
              return err;
      }));
  }
 

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

 TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.
 

что я все еще пропустил здесь?

Ответ №1:

Вы можете написать перехватчик для обработки ошибок в 1 месте. Это было бы удобнее, потому что вам не придется обрабатывать имитационные сценарии в разных службах

 @Injectable({
  providedIn: 'root',
})
export class RequestInterceptor implements HttpInterceptor {
  public intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req).pipe(
      tap( // or catchError operator
        (event: HttpEvent<any>) => event,
        (error: any) => {
          if (error instanceof HttpErrorResponse) {
            if (error.status === 406) {
              // do something here
            }
          }
        }
      )
    );
  }
}
 

И добавьте RequestInterceptor в массив поставщиков AppModule

 @NgModule({
    imports: [
        ...
    ],
    declarations: [
        ...
    ],
    providers: [
        { provide: HTTP_INTERCEPTORS, useClass: RequestInterceptor, multi: true }
    ],

    bootstrap: [AppComponent]
})
export class AppModule {}
 

Комментарии:

1. я попробовал ваш ответ, но все еще не могу правильно уловить ошибку

2. Вы добавили RequestInterceptor в массив поставщиков AppModule? См. Обновленный ответ

3. Я попробовал ваш ответ, но это ошибка «ошибиться», это сообщение об ошибке что-то вроде «Произошла неожиданная ошибка сервера». Но я хочу получить реальное сообщение об ошибке

Ответ №2:

Как согласовано с ответом Тимоти, HttpInterceptor удобно обрабатывать ошибки из одного места, что обеспечивает способ перехвата HTTP запросов и ответов для их преобразования или обработки перед передачей.

Существует два варианта использования, которые могут быть реализованы в перехватчике.

Во-первых, retry HTTP вызовите один или несколько раз, прежде чем выдавать ошибку. В некоторых случаях, например, если есть тайм-аут, лучше продолжить, не создавая исключения.

Для этого используйте retry оператор from RxJS для повторной подписки на наблюдаемое.

Затем проверьте состояние исключения и посмотрите, не является ли оно 401 несанкционированной ошибкой. Затем проверьте состояние исключения и посмотрите, является ли оно 406 недопустимой ошибкой. При обеспечении безопасности на основе токенов попробуйте обновить токен, если это необходимо. Если это не сработает, перенаправьте пользователя на страницу входа в систему или в другое место.

 import { Injectable } from '@angular/core';
import { 
  HttpEvent, HttpRequest, HttpHandler, 
  HttpInterceptor, HttpErrorResponse 
} from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { retry, catchError } from 'rxjs/operators';

@Injectable()
export class ServerErrorInterceptor implements HttpInterceptor {

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

    return next.handle(request).pipe(
      retry(1),
      catchError((error: HttpErrorResponse) => {
        if (error.status === 401) {
          // refresh token
        } 
        else if (error.status === 406) {
          // do something here
        } 
        else {
          return throwError(error);
        }
      })
    );    
  }
}
 

Здесь вы можете retry один раз, прежде чем проверять состояние ошибки, повторить ошибку. Обновление токенов безопасности зависит от вас, если это необходимо.

Комментарии:

1. я попробовал ваше решение, но я все еще не могу поймать ошибку, когда она возникла, я обновлю свой вопрос