I, am trying to handle the http error using the below class in angular 6. I got a 401 unAuthorized status from server. But however I, don't see the console error message.
HttpErrorsHandler.ts file
import { ErrorHandler, Injectable} from '@angular/core'; @Injectable() export class HttpErrorsHandler implements ErrorHandler { handleError(error: Error) { // Do whatever you like with the error (send it to the server?) // And log it to the console console.error('It happens: ', error); } }
app.module.ts file
providers: [{provide: ErrorHandler, useClass: HttpErrorsHandler}],
HttpCallFile
import { Injectable , Component} from '@angular/core'; import { HttpClient, HttpHeaders } from "@angular/common/http"; import { Observable } from 'rxjs'; import {AuthServiceJwt} from '../Common/sevice.auth.component'; @Injectable() export class GenericHttpClientService { private readonly baseUrl : string = "**********"; constructor(private httpClientModule: HttpClient , private authServiceJwt : AuthServiceJwt) { } public GenericHttpPost<T>(_postViewModel: T , destinationUrl : string): Observable<T> { const headers = new HttpHeaders().set('Content-Type', 'application/json; charset=utf-8') .set('Authorization',`Bearer ${this.authServiceJwt.getToken}`); return this.httpClientModule.post<T>(this.baseUrl + destinationUrl, _postViewModel, { headers }); } // This method is to post Data and Get Response Data in two different type public GenericHttpPostAndResponse<T,TE>(postViewModel: TE, destinationUrl: string): Observable<T> { const headers = new HttpHeaders().set('Content-Type', 'application/json; charset=utf-8') .set('Authorization',`Bearer ${this.authServiceJwt.getToken}`); return this.httpClientModule.post<T>(this.baseUrl + destinationUrl, postViewModel, { headers }); } // This method is to post Data and Get Response Data in two different type without JWT Token public GenericHttpPostWithOutToken<T,TE>(postViewModel: TE, destinationUrl: string): Observable<T> { const headers = new HttpHeaders().set('Content-Type', 'application/json; charset=utf-8'); return this.httpClientModule.post<T>(this.baseUrl + destinationUrl, postViewModel, { headers }); } public GenericHttpGet<T>(destinationUrl: string): Observable<T> { const headers = new HttpHeaders().set('Content-Type', 'application/json') .set('Authorization',`Bearer ${this.authServiceJwt.getToken}`); return this.httpClientModule.get<T>(this.baseUrl + destinationUrl, { headers }); } public GenericHttpDelete<T>(destinationUrl: string): Observable<T> { const headers = new HttpHeaders().set('Content-Type', 'application/json') .set('Authorization',`Bearer ${this.authServiceJwt.getToken}`); return this.httpClientModule.delete<T>(this.baseUrl + destinationUrl, { headers }); } }
admin.user.component.ts file
private getUsersHttpCall(): void { this.spinnerProgress = true; this.genericHttpService.GenericHttpGet<GenericResponseObject<UserViewModel[]>>(this.getAdminUserUrl).subscribe(data => { if (data.isSuccess) { this.genericResponseObject.data = data.data; this.dataSource = this.genericResponseObject.data this.spinnerProgress = false; } }, error => { console.log(error); this.spinnerProgress = false; }); }
When the error occurs in the HTTP Request it is intercepted and invokes the catchError . Inside the catchError you can handle the error and then use throwError to throw it to the service. We then register the Interceptor in the Providers array of the root module using the injection token HTTP_INTERCEPTORS .
The basic way to handle errors in Angular is to use Angular's HttpClient service along with RxJS operators throwError and catchError. The HTTP request is made, and it returns the data with a response if anything wrong happens then it returns an error object with an error status code.
Answer is "By creating flattened versions of Angular modules"
For XHR request you should use an Interceptor
This is the one I use to add JWT to headers and to handle some response errors:
import {Injectable} from '@angular/core'; import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse } from '@angular/common/http'; import {AuthService} from '../service/auth.service'; import {Observable, of} from 'rxjs'; import {Router} from "@angular/router"; import {catchError} from "rxjs/internal/operators"; @Injectable() export class TokenInterceptor implements HttpInterceptor { constructor(public auth: AuthService, private router: Router) { } /** * intercept all XHR request * @param request * @param next * @returns {Observable<A>} */ intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { if (localStorage.getItem('jwtToken')) { request = request.clone({ setHeaders: { Authorization: 'Bearer ' + localStorage.getItem('jwtToken') } }); } /** * continues request execution */ return next.handle(request).pipe(catchError((error, caught) => { //intercept the respons error and displace it to the console console.log(error); this.handleAuthError(error); return of(error); }) as any); } /** * manage errors * @param err * @returns {any} */ private handleAuthError(err: HttpErrorResponse): Observable<any> { //handle your auth error or rethrow if (err.status === 401) { //navigate /delete cookies or whatever console.log('handled error ' + err.status); this.router.navigate([`/login`]); // if you've caught / handled the error, you don't want to rethrow it unless you also want downstream consumers to have to handle it as well. return of(err.message); } throw err; } }
Don't forget to register you interceptor into app.module.ts
like so:
import { TokenInterceptor } from './auth/token.interceptor'; @NgModule({ declarations: [], imports: [], exports: [], providers: [ { provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true, } ], bootstrap: [AppComponent] }) export class AppModule { }
From @firegloves's answer, in order to have the .pipe
handlers in individual services actually be able to catchError
their own HTTP failure codes, you will need to structure the code such that:
of(error)
which can potentially be handled normally in any subsequent .pipe
's.throw
it again so subsequent error handlers, like those in your services, can pick up and handle non-401 errors for their own calls.My Interceptor has a twofold job. It:
Authorization
header into all outgoing requests, if it has a token stored./login
.import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, } from '@angular/common/http'; import { Observable } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { of } from 'rxjs/observable/of'; import { AuthService } from './auth.service'; @Injectable({ providedIn: 'root', }) export class RequestInterceptor implements HttpInterceptor { constructor( private readonly auth: AuthService, private readonly router: Router, ) { } /** * @param HttpRequest<any> request - The intercepted request * @param HttpHandler next - The next interceptor in the pipeline * @return Observable<HttpEvent<any>> */ intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { request = this.addToken(request); return next.handle(request) // add error handling .pipe( catchError( (error: any, caught: Observable<HttpEvent<any>>) => { if (error.status === 401) { this.handleAuthError(); // if you've caught / handled the error, you don't // want to rethrow it unless you also want // downstream consumers to have to handle it as // well. return of(error); } throw error; } ), ); } /** * Handle API authentication errors. */ private handleAuthError() { // clear stored credentials; they're invalid this.auth.credentials = null; // navigate back to the login page this.router.navigate(['/login']); } /** * Add stored auth token to request headers. * @param HttpRequest<any> request - the intercepted request * @return HttpRequest<any> - the modified request */ private addToken(request: HttpRequest<any>): HttpRequest<any> { const token: string = this.auth.token; if (token) { return request.clone({ setHeaders: { Authorization: `Bearer ${token}`, }, }); } return request; } }
All AuthService
does is have a public get/set that sticks a credentials object into localStorage
- it makes sure the token isn't expired, but you can design it however you want.
Like @firegloves stated above, you must add the Interceptor to the pipeline in app.module.ts
:
import { RequestInterceptor } from './auth/request.interceptor'; @NgModule({ declarations: [], imports: [], exports: [], providers: [ { provide: HTTP_INTERCEPTORS, useClass: RequestInterceptor, multi: true, }, ], bootstrap: [AppComponent], }) export class AppModule { }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With