typescript - Hantering av HTTP-fel i Angular 6


Jag försöker hantera HTTP-felet med nedanstående klass i Angular 6. Jag fick en 401 oautentiserad status från servern. Men jag ser dock inte felmeddelandet i konsolen.

HttpErrorsHandler.ts-fil

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-fil

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-fil

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

Svar #1

För XHR-förfrågan bör du använda en Interceptor

Det här är den jag använder för att lägga till JWT i headers och för att hantera vissa svarsfel:

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

Glöm inte att registrera din interceptor i app.module.ts på följande sätt:

import { TokenInterceptor } from './auth/token.interceptor';

@NgModule({
  declarations: [],
  imports: [],
  exports: [],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: TokenInterceptor,
      multi: true,
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Svar #2

Från @firegloves:s svar, för att .pipe-hanterare i individuella tjänster faktiskt ska kunna catchError sina egna HTTP-felkoder, måste du strukturera koden så att:

  1. Om Interceptorn upptäcker en HTTP 401-felkod, hantera den och returnera of(error) som potentiellt kan hanteras normalt i efterföljande .pipe's.
  2. Om HTTP-felkoden är en som Interceptorn inte är utformad för att hantera, kasta den igen så att efterföljande felhanterare, som de i dina tjänster, kan ta upp och hantera icke-401 fel för sina egna anrop.

Min Interceptor har ett dubbelt uppdrag. Den:

  1. Injicerar en Authorization-header i alla utgående förfrågningar om den har en sparad token.
  2. Fångar HTTP 401 Unauthenticated-fel, vid vilket tillfälle den ogiltigförklarar tokenlagret och sedan omdirigerar tillbaka till /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;
    }

}

Allt AuthService gör är att ha en offentlig get/set som placerar ett autentiseringsobjekt i localStorage - den ser till att token inte är utgången, men du kan designa den precis som du vill.

Som @firegloves nämnde ovan, måste du lägga till Interceptor i pipelinen i 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 { }

Relaterade artiklar