• 用VSCode开发一个asp.net core 2.0+angular 5项目(4): Angular5全局错误处理


    第一部分: http://www.cnblogs.com/cgzl/p/8478993.html

    第二部分: http://www.cnblogs.com/cgzl/p/8481825.html

    第三部分: https://www.cnblogs.com/cgzl/p/8525541.html

    这篇文章将介绍angular 5的全局错误处理.

    需要使用到代码: https://pan.baidu.com/s/1F0KjbwVE8_Tzfwy69Alp-A

    angular 5 全局错误处理

    参考文档: https://angular.io/api/core/ErrorHandler

    首先按照文档在客户端项目建立app.error-handler.ts 文件:

    import { ErrorHandler } from '@angular/core';
    
    export class AppErrorHandler implements ErrorHandler {
        handleError(error: any): void {
            console.log('ERROR Occurred.');
        }
    }

    这里, 我们只写log.

    然后在app.module里面注册:

    providers: [
        TvNetworkService,
        { provide: ErrorHandler, useClass: AppErrorHandler }
      ],

    然后把tv-network-list.component.ts里面到一个错误处理删除掉:

    然后在后端到Controller里面抛一个异常:

    然后我们试一下:

    可以看到, 这个全局错误处理器正常到工作了.

    先别急, 让我们在errorhandler里面使用toastr试试.

    app.error-handler.ts:

    import { ErrorHandler } from '@angular/core';
    import { ToastrService } from 'ngx-toastr';
    
    export class AppErrorHandler implements ErrorHandler {
    
        constructor(private toastr: ToastrService) { }
    
        handleError(error: any): void {
            // console.log('ERROR Occurred.');
            this.toastr.error('发生了错误');
        }
    }

    而这时回到浏览器之后, 发生了错误:

    之所以发生这个错误, 是因为AppErrorHandler在angular引入Toastr模块之前就初始化了.

    我们可以这样处理:

    import { ErrorHandler, Injectable, Injector, Inject } from '@angular/core';
    import { ToastrService, Toast } from 'ngx-toastr';
    
    @Injectable()
    export class AppErrorHandler implements ErrorHandler {
    
        constructor(private injector: Injector) { }
    
        private get toastr(): ToastrService {
            return this.injector.get(ToastrService);
        }
    
        handleError(error: any): void {
            this.toastr.error('发生了错误');
        }
    }

    使用Injector来手动注入ToastrService.

    回到浏览器:

    并没有弹出错误信息!!!!, 但是来回切换菜单后, 开始显示错误信息了, 貌似有点迟钝.

    这是什么原因呢? 首先, 我们得了解以下这个东西:

    Zone

    首先到首先, 需要了解以下execution context, 程序执行到上下文, 但是这些东西到定义看了之后可能会让人迷糊. 所以还是先看这段代码吧:

    const Zone = {
      run: (callback) => {
        if (this.beforeTask) {
          this.beforeTask();
        }
        callback();
        if (this.afterTask) {
          this.afterTask();
        }
      }
    };
    
    Zone.beforeTask = () => {
      console.log('Before Task.');
    };
    Zone.afterTask = () => {
      console.log('After Task.');
    };
    Zone.run(() => {
      console.log('Running...');
    });

    就是定义一个Zone, 它到run方法可以执行某个回调函数, 回调函数到前后还可以有一些预定义的函数, 如果它们存在就会被执行. 通过定义这些函数的内容, 我们就可以在执行run的回调前后添加自定义逻辑了.

    回到Angular, angular的变化检测(Change Detection)功能就用到了这些东西.

    比如angular的一个component有一个click事件, click()方法里更新了某些属性的值, 这个时候angular就需要进行变化检测, 如果真的发生了变化, 那么angular 就会更新dom, 这样我们就能看见页面的变化了. Angular用了这个猴子补丁, 使之运行在Zone里面, 当点击按钮的时候, 这段代码总是在Zone里面执行, 在执行完click处理方法之后, angular会执行变化检测动作.

    angular应该是这样来进行猴子补丁的:

    const Zone = {
      run: (callback) => {
        if (this.beforeTask) {
          this.beforeTask();
        }
        callback();
        if (this.afterTask) {
          this.afterTask();
        }
      }
    };
    
    Zone.beforeTask = () => {
      console.log('Before Task.');
    };
    Zone.afterTask = () => {
      console.log('After Task.');
    };
    Zone.run(() => {
      console.log('Running...');
    });
    
    var _setTimeout = setTimeout;
    setTimeout = (callback, timeout) => {
      Zone.run(() => {
        _setTimeout(callback, timeout);
      });
    };
    click(() => {
      console.log('设置Timeout');
    });

    由于这个是异步的, 所以打印到控制台到顺序可能是: Before Task, After Task, 设置Timeout.

    js运行时里, 有一个信息队列. 任何时候出现一个异步操作, 队列里就会推进去一条信息, js运行时会训话这个队列, 一个个把消息推出队列, 然后调用这个消息到回调函数. 对于这个例子来说就是setTimeout().

    所以就出现了Zone.js这个库.

    Zone.js就是一个执行的上下文, 它可以在不同的异步操作之间进行持久性传递.

    Angular就使用了这个库, 在它之上建立了ngZone这个模块. 就这样angular在发生异步操作后进行到了变化检测.

    浏览器里面主要有这几种异步操作: dom事件, ajax请求, 定时回调之类的.

    回到项目里的app.error-handler.ts:

    这句话呢就跑出了angular zone的范围...

    所以当错误发生的时候, toastr的error方法被调用了(状态改变了), 但是angular并不知道这个变化, 所以toastr通知没有显示.

    那如何解决呢?

    使用ngZone:

    import { ErrorHandler, Injectable, Injector, Inject, NgZone } from '@angular/core';
    import { ToastrService, Toast } from 'ngx-toastr';
    
    @Injectable()
    export class AppErrorHandler implements ErrorHandler {
    
        constructor(
            private injector: Injector,
            private ngZone: NgZone
        ) { }
    
        private get toastr(): ToastrService {
            return this.injector.get(ToastrService);
        }
    
        handleError(error: any): void {
            this.ngZone.run(() => {
                this.toastr.error('发生了错误');
            });
        }
    }

    下面试试页面:

    这次没有任何问题了.

    Logging Errors 记录错误 

    您可以自己写一个后台api来记录日志, 但是这里我介绍一个专门做logging的云服务, sentry.iohttps://sentry.io/

    首先请您自己注册账户. 

    然后创建一个项目, 选择angular:

    然后点击下面按钮Create Project.

    然后它给出了安装和配置的说明:

    首先执行命令安装.

    然后, 配置:

    import * as Raven from 'raven-js';
    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule, ErrorHandler } from '@angular/core';
    import { AppComponent } from './app.component';
    
    Raven
      .config('https://fa66d9390ab04c7f8e8c82ad0613fb4e@sentry.io/301095')
      .install();
    
    @NgModule({
      imports: [ BrowserModule ],
      declarations: [ AppComponent ],
      bootstrap: [ AppComponent ],
      providers: [ { provide: ErrorHandler, useClass: AppErrorHandler } ]
    })
    export class AppModule { }

    按照说明进行配置, 我们做一些调整, 这里红色部分是每个用户都不一样都.

    最后修改app.error-handler.ts:

    import { ErrorHandler, Injectable, Injector, Inject, NgZone } from '@angular/core';
    import { ToastrService, Toast } from 'ngx-toastr';
    import * as Raven from 'raven-js';
    
    @Injectable()
    export class AppErrorHandler implements ErrorHandler {
    
        constructor(
            private injector: Injector,
            private ngZone: NgZone
        ) { }
    
        private get toastr(): ToastrService {
            return this.injector.get(ToastrService);
        }
    
        handleError(error: any): void {
            Raven.captureException(error);
            this.ngZone.run(() => {
                this.toastr.error('发生了错误');
            });
        }
    }

    回到浏览器的错误页面, 触发错误后, 大约几分钟后, 来到sentry.io网站查看:

    今天先写到这, 明天后天写以下 angular5上传文件到asp.net core web api.

  • 相关阅读:
    单元测试之NUnit一
    第一个基于ArcGIS的Android应用
    NuGet的安装和使用
    PIE调用Python获得彩色直方图
    PIE调用Python返回得到直方图矩阵数组
    PIE属性表多字段的文本绘制
    微信公众号配置和使用客服消息
    python多线程爬取斗图啦数据
    微信小微商户获取申请状态
    微信小微商户申请入驻
  • 原文地址:https://www.cnblogs.com/cgzl/p/8536350.html
Copyright © 2020-2023  润新知