DTeam 团队日志

Doer、Delivery、Dream

Angular或Ionic 异常处理:ErrorHandler

纪瑞瑶 Posted at — Feb 6, 2020 阅读

在前端异常处理是非常重要的,包括客户端和服务端的异常。之前异常处理是对于每个异步函数添加err处理,这样不仅加大了工作量,还容易遗漏某些异常。幸好Angular6提供了ErrorHandler来处理异常(Ionic4为IonicErrorHandler),默认的ErrorHandler处理异常是将其输出在console上,这显然不能满足需求,所以需要自己实现一个GlobalErrorHandler。

为什么要用ErrorHandler统一处理异常

         (err: any) => {
                reject(err);
              }
            ), (err: any) => {
              reject(err);
            };
          }, (err: any) => {
            reject(err);
          });
        },
        (err: any) => {
          reject(err);
        }
      );

用ErrorHandler捕获异常的具体实现

第一步:创建ErrorService和LoggingService用于获取异常信息和记录异常日志

ErrorService: 获取客户端的异常信息和堆栈、服务端的异常信息和状态码。

import { Injectable } from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';

@Injectable()
export class ErrorService {

    getClientMessage(error: Error): string {
        if (!navigator.onLine) {
            return '暂无网络,请检查网络';
        }
        return error.message ? error.message : error.toString();
    }

    getClientStack(error: Error): string {
        return error.stack;
    }

    getServerMessage(error: HttpErrorResponse): string {
        return error.message;
    }

    getServerStatus(error: HttpErrorResponse): number {
        return error.status;
    }
}

LoggingService: 通过Beacon API向后端发送异常信息,记录异常日志,包括当前登录用户,异常发生时间,异常信息等。

import { Injectable } from '@angular/core';
import { DatePipe } from '@angular/common';
import { GlobalProvider } from '../globalprovider';
import { environment } from '@env/environment';
import { URL } from '../url';

@Injectable()
export class LoggingService {

    constructor(private global: GlobalProvider,
        private datePipe: DatePipe) { }

    logError(message: string, stack: string) {
        let currentUser = 'notLogin';
        if  (this.global.isLogin) {
            currentUser = this.global.currentUser.username;
        }
        const data = `{user: ${currentUser}, logtime: ${this.datePipe.transform(new Date(), 'yyyy-MM-dd HH:mm:ss')}, content: ${message}, stack: ${stack}}`;
        if (navigator.sendBeacon) {
            navigator.sendBeacon(environment.SERVER_URL + URL.log, data);
        } else {
            // 不支持Beacon
            const xhr = new XMLHttpRequest();
            xhr.open('post', environment.SERVER_URL + URL.log, false);
            xhr.send(data);
        }
    }
}

第二步:创建GlobalErrorHandler

  1. 创建global-error-handler.ts

import { ErrorHandler, Injectable, Injector } from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';

@Injectable()
export class GlobalErrorHandler implements ErrorHandler {

    constructor(private injector: Injector) { }

    handleError(error: Error | HttpErrorResponse) {
    }
}
  1. 在app.module.ts中添加
providers:  [
...
 { provide: ErrorHandler, useClass: GlobalErrorHandler},
...
]
  1. 添加异常处理逻辑
import { ErrorHandler, Injectable, Injector } from '@angular/core';
import { HttpErrorResponse } from '@angular/common/http';
import { LoggingService } from './services/logging.service';
import { ErrorService } from './services/error.service';
import { NzMessageService } from 'ng-zorro-antd';

@Injectable()
export class GlobalErrorHandler implements ErrorHandler {

    constructor(private injector: Injector) { }

    handleError(error: Error | HttpErrorResponse) {

        const errorService = this.injector.get(ErrorService);
        const logger = this.injector.get(LoggingService);
        const msg = this.injector.get(NzMessageService);

        let message;
        let status;
        let stackTrace;

        if (error instanceof HttpErrorResponse) {
            message = errorService.getServerMessage(error);
            status = errorService.getServerStatus(error);
           // 提示异常信息
            msg.error(message);
        } else {
            message = errorService.getClientMessage(error);
            stackTrace = errorService.getClientStack(error);
           // 提示异常信息
            msg.error(message);
        }
        // 记录日常日志
        logger.logError(message, stackTrace);
    }
}

注:很多人之前在Interceptor中处理异常,Interceptor中只能处理HttpErrorResponse类型的error,如果这里处理了,那么ErrorHandler将捕获不到。所以在Interceptor中,遇到HttpErrorResponse的error需要抛出。

catchError((error: HttpErrorResponse) => {
        if (error.status === 401) {
          // 跳到登录页或者刷新token
        } else {
          return throwError(error);
        }
 })

对于一个项目,有一个良好的异常处理机制是非常重要的,ErrorHandler解决了很多之前异常处理方式的痛点,对于使用Angular/Ionic的小伙伴来说是个不错的选择。


相关文章