Generic http error handling in Angular

2019-02-20 17:41发布

问题:

angular2-jwt provides an AuthHttp wrapper for the native Angular http class that automatically includes the http authorization header with each request. In my services I am usually calling a backend api like this

  getThings(): Observable<Thing[]> {
    return this.http.get(environment.apiEndpoint + '/things')
                    .map(res => res.json().data)
                    .catch(res => this.handleError(res));
  }

with an error handler like

private handleError(error: Response | any) {
  let errMsg: string;
  if (error instanceof Response) {
    const body = error.json() || '';
    const err = body.error || JSON.stringify(body);
    errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
  } else {
    errMsg = error.message ? error.message : error.toString();
  }
  // If JWT is expired open a new window to log in again
  if (errMsg.indexOf('No JWT present or has expired') !== -1) {
    window.open(environment.apiEndpoint + '/loginToApp?expired', '_blank', 'height=570,width=520');
  }
  console.error(error);
  return Observable.throw(errMsg);
}

I keep repeating the handleError method in all my services and I would like to know if there is a way to make this DRY. Is there a way to define a generic error handler for all http requests?

I'd like to avoid having to call catch each time if possible. Is there a way to extend the http class to automatically catch errors on each request and then in special situations I can add custom catch logic on a case by case basis?

I could export the method from another file and import it in each of my services but I think there has got to be a better way. Thanks for any suggestions.

UPDATE: I ended up extending the AuthHttp class from angular2-jwt.

import { Http, RequestOptions, Response, Request, RequestOptionsArgs } from '@angular/http';

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';

import { AuthHttp, AuthConfig } from 'angular2-jwt';

export class ApiHttp extends AuthHttp { 

  public request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
    return super.request(url, options).catch(res => this.handleError(res));
  }

  private handleError(error: Response | any) {
    // error handling then rethrow the error
    return Observable.throw(error);
  }
}

回答1:

You can create custom service CustomAuthHttp like AuthHTTP Wrapper. Like wrapper adds header, in CustomAuthHttp you can catch error. Then in your code inject CustomAuthHttp instead of AuthHTTP and use.

@Injectable()
export class CustomAuthHttp {

  constructor(private http: AuthHttpWrapper) { }

  get(url: string) {
    return this.http.get(url)
      .catch(res => this.handleError(res));
  }

  private handleError(error: Response | any) {
    // your code
    return Observable.throw('');
  }
}