Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

handling error with http global service angular 4.3, handleError , 401, 0, etc, interceptor , jwt, headers

Tags:

angular

jwt

I have a http globalservice that it's called for all services; so i can manage best by example; errors, alerts, variables,etc.

customers.service.ts

export class CustomersService {
  childUrl = environment.apiUrl + 'customers';

  constructor(
    private http: HttpClient,
    private globalService: GlobalService
   ) {


  }

  public get(childUrl)  {
    return this.globalService.get(this.childUrl)
      .catch((res: Response) => this.handleError(res));
  }
  ...
  private handleError(err) {
    return Observable.throw(err);
  }
}

global.service.ts

import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse, HttpHeaders  } from '@angular/common/http';
import { environment } from '../../environments/environment';

@Injectable()
export class GlobalService {

  url: string,

  constructor(private http: HttpClient) {

    this.headers = new HttpHeaders()
      .set('Content-Type', 'application/json; charset=utf-8')
      .set('Accept', 'application/json');


  }

  public prepare ( vars ) {
    this.url = environment.apiUrl + vars.childUrl;
  }
  public get( childUrl)  {

    this.prepare ({childUrl} );

    return this.http.get(this.url, { headers: this.headers, observe: 'response'})
      .catch((res: Response) => this.handleError(res);
  }
  private handleError(err) {
    return Observable.throw(err);
  }

}

customers-list.component

export class CustomersListComponent implements OnInit {

  public customers: Array <any>;

  constructor (private customersService: CustomersService ) { }

  ngOnInit() {
    this.get();
  }

  private get(): void {
    this.customerService
      .get()
      .subscribe((data) => {this.customers = data.data; console.log(data) },
        error => console.log(error),
        () => console.log('Get all Items complete'));
  }
}

before angular 4.3 I had observables , i could have catch for the error, and throw an observable in global service, in child service, in the component. Now it's not working and i'm not sure how to manage catch, and handle error with observables

In the new angular guide: https://angular.io/guide/http#error-handling

just manage error in a simple way,

http
  .get<ItemsResponse>('/api/items')
  .subscribe(
    data => {...},
    (err: HttpErrorResponse) => {
      if (err.error instanceof Error) {
        // A client-side or network error occurred. Handle it accordingly.
        console.log('An error occurred:', err.error.message);
      } else {
        // The backend returned an unsuccessful response code.
        // The response body may contain clues as to what went wrong,
        console.log(`Backend returned code ${err.status}, body was: ${err.error}`);
      }
    }
  });

what is the right way to manage now this ?

like image 907
stackdave Avatar asked Jan 03 '23 15:01

stackdave


2 Answers

You do still have observables and you can basically leave your existing component code as is. All you need to change is the service code to use the new HttpClient.

Here is my new service:

import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';

import { IProduct } from './product';

@Injectable()
export class ProductService {
    private _productUrl = './api/products/products.json';

    constructor(private _http: HttpClient) { }

    getProducts(): Observable<IProduct[]> {
        return this._http.get<IProduct[]>(this._productUrl)
            .do(data => console.log('All: ' + JSON.stringify(data)))
            .catch(this.handleError);
    }

    private handleError(err: HttpErrorResponse) {
        // in a real world app, we may send the server to some remote logging infrastructure
        // instead of just logging it to the console
        let errorMessage = '';
        if (err.error instanceof Error) {
            // A client-side or network error occurred. Handle it accordingly.
            errorMessage = `An error occurred: ${err.error.message}`;
        } else {
            // The backend returned an unsuccessful response code.
            // The response body may contain clues as to what went wrong,
            errorMessage = `Server returned code: ${err.status}, error message is: ${err.message}`;
        }
        console.error(errorMessage);
        return Observable.throw(errorMessage);
    }
}

And here is the method in my component, which was basically unchanged:

ngOnInit(): void {
    this._productService.getProducts()
            .subscribe(products => this.products = products,
                error => this.errorMessage = <any>error);
}
like image 102
DeborahK Avatar answered Jan 15 '23 15:01

DeborahK


I've found finally a solution, errors are objects, so before angular 4.3 the error object was a Response, now its HttpErrorResponse, anyway we get objects, so we can ask for properties. Most of functions don't consider the error status 0, o nothing when the server is not working, or when your interceptor in angular 4.3 or anything you did in your error manage don't produce correct status.

the final solution i've found is just simply ask for object properties from error object, and I can have define messages errors in the case i don't want show backend errors for known errors.

look for environment.ts file ( angular-cli create this file ):

export const environment = {
  production: false,
  apiUrl: 'http://localhost:3000/',
  httpErrors: {
    0:   { 'msg': 'Server is not available'},
    404: { 'msg': 'Page not Found'},
    401: { 'msg': 'Not Authorized'}
  }
};

then handle error for the global service can be:

  private handleError(err: any) {
    console.log( 'Error global service');
    console.log(err);
    let errorMessage = '';

    if (err.hasOwnProperty('status') ) { // if error has status
      if (environment.httpErrors.hasOwnProperty(err.status)) {
         errorMessage = environment.httpErrors[err.status].msg; // predefined errors
      } else {
        errorMessage = `Error status: ${err.status}`;
        if (err.hasOwnProperty('message')) {
          errorMessage +=  err.message;
        }
      }
    }
    if (errorMessage === '') {
      if (err.hasOwnProperty('error') && err.error.hasOwnProperty('message') ) { // if error has status
        errorMessage = `Error: ${err.error.message}`;
      }
    }
    if (errorMessage === '')  errorMessage = environment.httpErrors[0].msg; +// no errors, then is connection error
   this.snackBar.open(errorMessage, 'Close', {
      duration: 5000
    });
    console.error(errorMessage);
    return Observable.throw(errorMessage);
  }

in your interceptor

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

@Injectable()
export class InterceptorService implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (localStorage.getItem('SignIn-Token')) {
      req = req.clone({
        setHeaders: {
          authorization: localStorage.getItem('SignIn-Token')
        }
      });
    }
    return next.handle(req).catch(err => {
      if (err instanceof HttpErrorResponse) {
        console.log('interceptor error');
        console.log(err);
        if (err.status === 401) {
          // JWT expired, can be setted to go to login
          return Observable.throw(err);
        } else {
          return Observable.throw(err);
        }
      }
    });
  }
}

if you make an error in your interceptor by example with the code below, the handleError will work anyway:

return next.handle(req).catch(err => {
  if (err instanceof HttpErrorResponse) {
    console.log('interceptor error');
    console.log(err);
    if (err.status === 401) {
      // JWT expired, can be setted to go to login
      return Observable.throw(err);
    }
    // here you are not return observable so, your global service get nothing of status ....

  }
});
like image 31
stackdave Avatar answered Jan 15 '23 14:01

stackdave