How to handle unauthorized requests(status with 401 or 403) with new httpClient in angular 4.3

前端 未结 3 882
长发绾君心
长发绾君心 2020-12-25 14:18

I have an auth-interceptor.service.ts to handle the requests

import {Injectable} from \'@angular/core\';
import {HttpErrorResponse, HttpEvent, Ht         


        
相关标签:
3条回答
  • 2020-12-25 15:02

    From the @bryan60 suggestion I made few changes to his solution

    In app.module.ts:

    providers: [
         {
            provide: HTTP_INTERCEPTORS,
            useFactory: function(injector: Injector) {
                return new AuthInterceptor(injector);
            },
            multi: true,
            deps: [Injector]
        },
          .... other providers ...
    ]
    

    and in auth-interceptor.service.ts:

    import {Injectable, Injector} from '@angular/core';
    import {HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '@angular/common/http';
    import {Observable} from 'rxjs/Observable';
    import {Cookie} from './cookie.service';
    import {Router} from '@angular/router';
    import {UserService} from './user.service';
    import {ToasterService} from '../toaster/toaster.service';
    import 'rxjs/add/operator/catch';
    import 'rxjs/add/observable/throw';
    
    @Injectable()
    export class AuthInterceptor implements HttpInterceptor {
        constructor(private injector: Injector) {}
    
        private handleError(err: HttpErrorResponse): Observable<any> {
            let errorMsg;
            if (err.error instanceof Error) {
                // A client-side or network error occurred. Handle it accordingly.
                errorMsg = `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,
                errorMsg = `Backend returned code ${err.status}, body was: ${err.error}`;
            }
            if (err.status === 404 || err.status === 403) {
                this.injector.get(UserService).purgeAuth();
                this.injector.get(ToasterService).showError(`Unauthorized`, errorMsg);
                this.injector.get(Router).navigateByUrl(`/login`);
            }
            console.error(errorMsg);
            return Observable.throw(errorMsg);
        }
    
        intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
            // Clone the request to add the new header.
            const authReq = req.clone({headers: req.headers.set(Cookie.tokenKey, Cookie.getToken())});
            // Pass on the cloned request instead of the original request.
            return next.handle(authReq).catch(err => this.handleError(err));
        }
    }
    

    If you are using AOT in building try:

    export function authInterceptorFactory(injector: Injector) {
        return new AuthInterceptor(injector);
    }
    
    providers: [
             {
                provide: HTTP_INTERCEPTORS,
                useFactory: authInterceptorFactory,
                multi: true,
                deps: [Injector]
            },
              .... other providers ...
    ]
    
    0 讨论(0)
  • 2020-12-25 15:06

    the above @bryan60 answer is works fine , if any one facing issue like me with catch the error in below line

    return next.handle(authReq).catch(x=> this.handleAuthError(x));
    

    using do() handle the error(if you face issue with catch())

    import in file:

    import 'rxjs/add/operator/do';
    

    handle error:

    return next.handle(authReq)
     .do(
        success => {/*todo*/},
        err => {this.handleAuthError(authReq)}
        );
    }
    
    handleAuthError(err: any) {
        if(err.status === 401 || err.status === 403) {
        this.storageService.clear();
        window.location.href = '/home';
        }
    }
    

    I hope this is help someone.

    0 讨论(0)
  • 2020-12-25 15:07

    You should use your interceptor and just handle it like this:

    @Injectable()
    export class AuthInterceptor implements HttpInterceptor {
        constructor(private router: Router) { }
    
        private handleAuthError(err: HttpErrorResponse): Observable<any> {
            //handle your auth error or rethrow
            if (err.status === 401 || err.status === 403) {
                //navigate /delete cookies or whatever
                this.router.navigateByUrl(`/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); // or EMPTY may be appropriate here
            }
            return throwError(err);
        }
    
        intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        // Clone the request to add the new header.
            const authReq = req.clone({headers: req.headers.set(Cookie.tokenKey, Cookie.getToken())});
            // catch the error, make specific functions for catching specific errors and you can chain through them with more catch operators
            return next.handle(authReq).pipe(catchError(x=> this.handleAuthError(x))); //here use an arrow function, otherwise you may get "Cannot read property 'navigate' of undefined" on angular 4.4.2/net core 2/webpack 2.70
        }
    }
    

    no need for the http service wrapper.

    to use the router you'll need a factory provider like:

     providers: [
         {
             provide: HTTP_INTERCEPTORS,
             useFactory: function(router: Router) {
               return new AuthInterceptor(router);
             },
             multi: true,
             deps: [Router]
          },
          .... other providers ...
      ]
    

    where ever you're providing the interceptor (probably app.module). don't use an arrow function. they aren't supported in factory functions when you try to build for prod.

    Working plunk: https://plnkr.co/edit/UxOEqhEHX1tCDVPDy488?p=preview

    0 讨论(0)
提交回复
热议问题