Handling 401s globally with Angular

后端 未结 8 932
误落风尘
误落风尘 2020-11-28 18:50

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         


        
相关标签:
8条回答
  • 2020-11-28 19:27

    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'));
            });
        }
    }
    
    0 讨论(0)
  • 2020-11-28 19:28

    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;
            })
          );
      }
    }
    
    
    0 讨论(0)
  • 2020-11-28 19:29

    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

    0 讨论(0)
  • 2020-11-28 19:29

    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.

    0 讨论(0)
  • 2020-11-28 19:31

    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);
    }
    
    0 讨论(0)
  • 2020-11-28 19:32

    Angular 4.3+

    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.

    Updated for RxJS 6+

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

    RxJS <6

    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
                }
            });
        }
    }
    
    0 讨论(0)
提交回复
热议问题