2

i need to catch response body produced by my REST API, which is throwing http error 406 with custom response body, but in my angular, i only can catch the header status which is 'Not Acceptable', i can't get the body, how can i get the body instead of the 'Not Acceptable' string? and i got this error too TypeError: You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable. which is i dunno which part that trigger this error. Here is how i do it :

first i create global service class, which is looked like this :

  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());
  }

i tried to console.log the error it just appear string 'Not Acceptable', and it continued by console.log('here i am'); it print out properly, and i can't find where the cause of TypeError

here is how i use function above :

  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;
          }));
  }

when i trying to console.log the response, it didn't appear. I think my function stop working in my post function at my global service class, what did i do wrong?

------------------ UPDATE OF MY CODE --------------------

i create HttpRequestInterceptor class to handle the error, here is my class :

@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) /* && 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());
  }

until there i still can catch the error properly when i console.log(err) but when i consume the post function, i can't catch the error, and i got error in my browser console. Here is my function :

  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;
      }));
  }

i can't log my error, it doesn't event print anything, i think my function crashed and throw this error instead :

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

what did i still missed here?

2 Answers 2

2

You can write an interceptor to handle errors in 1 place. It would be more convenient, because you will not have to handle simular scenarios in different services

@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
            }
          }
        }
      )
    );
  }
}

And add RequestInterceptor into array of providers of AppModule

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

    bootstrap: [AppComponent]
})
export class AppModule {}
Sign up to request clarification or add additional context in comments.

5 Comments

i have tried your answer, but i still can't catch the error properly
Have you added RequestInterceptor into the array of providers of AppModule? See updated answer
I tried your answer but It's get wrong error this error message something like "An unexpected server error occurred." But I want to get real error message
this does not working
which part doesn't work @FaiZalDong?
1

As agreed with Timothy's answer, HttpInterceptor is convenient to handle error from a single place which provides a way to intercept HTTP requests and responses to transform or handle them before passing them along.

There are two use cases that can be implemented in the interceptor.

First, retry the HTTP call once or multiple times before throwing the error. In some cases, for example, if there is a timeout, better to continue without throwing the exception.

For this, use the retry operator from RxJS to resubscribe to the observable.

Then check the status of the exception and see if it is a 401 unauthorized error. Then check the status of the exception and see if it is a 406 not acceptable error. With token-based security, try to refresh the token, if required. If this does not work, redirect the user to the login page or else.

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);
        }
      })
    );    
  }
}

Here you may retry once before you check the error status and rethrow the error. Refreshing security tokens is up to you, if required.

1 Comment

i have tried your solution, but i still can't catch the error when it throwed, i will update my question

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.