In my Angular 2 project I make API calls from services that return an Observable. The calling code then subscribes to this observable. For example:
getCampai
Angular 4.3+
To complete The Gilbert Arenas Dagger answer:
If what you need is intercept any error, apply a treatment to it and forward it down the chain (and not just add a side effect with .do
), you can use HttpClient and its interceptors to do something of the kind:
import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// install an error handler
return next.handle(req).catch((err: HttpErrorResponse) => {
console.log(err);
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}`);
}
return Observable.throw(new Error('Your custom error'));
});
}
}
As frontend APIs expire faster than milk, with Angular 6+ and RxJS 5.5+, you need to use pipe
:
import { HttpInterceptor, HttpEvent, HttpRequest, HttpHandler, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { Injectable } from '@angular/core';
import { catchError } from 'rxjs/operators';
import { Router } from '@angular/router';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private router: Router) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
catchError((err: HttpErrorResponse) => {
if (err.status === 401) {
this.router.navigate(['login'], { queryParams: { returnUrl: req.url } });
}
return throwError(err);
})
);
}
}
Update for Angular 7+ and rxjs 6+
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { Injectable } from '@angular/core';
import { catchError } from 'rxjs/internal/operators';
import { Router } from '@angular/router';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private router: Router) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(request)
.pipe(
catchError((err, caught: Observable<HttpEvent<any>>) => {
if (err instanceof HttpErrorResponse && err.status == 401) {
this.router.navigate(['login'], { queryParams: { returnUrl: request.url } });
return of(err as any);
}
throw err;
})
);
}
}
The Observable
you get from each request method is of type Observable<Response>
. The Response
object, has an status
property which will hold the 401
IF the server returned that code. So you might want to retrieve that before mapping it or converting it.
If you want to avoid doing this functionality on each call you might have to extend Angular 2's Http
class and inject your own implementation of it that calls the parent (super
) for the regular Http
functionality and then handle the 401
error before returning the object.
See:
https://angular.io/docs/ts/latest/api/http/index/Response-class.html
From Angular >= 2.3.0 you can override the HTTP
module and inject your services.
Before version 2.3.0, you couldn't use your injected services due to a core bug.
I've created a gist to show how it's done.
Angular >4.3: ErrorHandler for the base service
protected handleError(err: HttpErrorResponse | any) {
console.log('Error global service');
console.log(err);
let errorMessage: string = '';
if (err.hasOwnProperty('status')) { // if error has status
if (environment.httpErrors.hasOwnProperty(err.status)) {
// predefined errors
errorMessage = environment.httpErrors[err.status].msg;
} 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}`;
}
}
// no errors, then is connection error
if (errorMessage === '') errorMessage = environment.httpErrors[0].msg;
// this.snackBar.open(errorMessage, 'Close', { duration: 5000 }});
console.error(errorMessage);
return Observable.throw(errorMessage);
}
With the introduction of HttpClient came the ability to easily intercept all requests / responses. The general usage of HttpInterceptors is well documented, see the basic usage and how to provide the interceptor. Below is an example of an HttpInterceptor that can handle 401 errors.
import { Observable, throwError } from 'rxjs';
import { HttpErrorResponse, HttpEvent, HttpHandler,HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { catchError } from 'rxjs/operators';
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).pipe(
catchError((err: HttpErrorResponse) => {
if (err.status == 401) {
// Handle 401 error
} else {
return throwError(err);
}
})
);
}
}
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http'
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';
@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next.handle(req).do(event => {}, err => {
if (err instanceof HttpErrorResponse && err.status == 401) {
// handle 401 errors
}
});
}
}