自定义错误处理程序抛出错误:无法读取未定义的属性“get”(Injector)

时间:2018-03-07 09:50:01

标签: angular typescript error-handling

我正在使用角度4构建自定义错误处理程序,以使用错误拦截器处理不同类型的应用程序错误

创建一个基类( app-error.ts )和其他类(例如,处理403错误创建类 access-denied.ts ),这扩展了这个基类。

基类中的

注入了一个服务toastrService,我想显示来自子类的自定义消息但是它给出了错误

无法读取未定义的属性'get'

这个问题与OOPS概念有关。我不明白如何获取覆盖父方法或使用我的自定义参数调用。

TS v 2.3.3 棱角v 4.3.4

app.module.ts

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

注意:AppErrorHandler类与AppError完全不同,AppError扩展了处理系统错误的角度ErorHandler接口。

错误拦截

import { Router } from '@angular/router';
import { Injectable, Injector } from '@angular/core';
import { HttpInterceptor, HttpResponse, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse
} from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';

import {
    AuthFail,
    BadInput,
    NotFoundError,
    ServerError,
    AppError,
    AccessDenied,
} from '../shared/errors';
import { AuthenticationService } from './../authentication/authentication.service';


@Injectable()
export class ErrorInterceptor implements HttpInterceptor {

    private auth: AuthenticationService;
    constructor(private router: Router, private injector: Injector) { }
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const auth = this.injector.get(AuthenticationService);
        return next.handle(req).catch((err: HttpErrorResponse) => {
            if (err instanceof HttpErrorResponse) {
                if (err.status === 401) {
                    return Observable.throw(new AuthFail(err.error));
                }
                if (err.status === 400) {
                    return Observable.throw(new BadInput(err.error));
                }
                if (err.status === 404) {
                    return Observable.throw(new NotFoundError());
                }
                if (err.status === 403) {
                    return Observable.throw(new AccessDenied());
                }
                return Observable.throw(new AppError(err));
            }
        });
    }
}

访问denied.ts

import { AppError } from './app-error';
export class AccessDenied extends AppError {
    constructor(public originalError?: any) {
        super();
        console.log('inside acces denied constructor');
        // super.handleError("superrrrr"); // this also doesn't work
    }

    public handleError(): void {
        console.log('handleError: ', );
        super.handleError("Access denined error occured");
    }
}

APP-error.ts

import { Inject, Injector } from "@angular/core";

import { ToastrService } from "ngx-toastr";

export class AppError {
    toastrService: ToastrService;
    constructor(public originalError?: any, private injector?: Injector) {
        this.toastrService = this.injector.get(ToastrService);
    }

    // NOTE: using getter seems impossible to access in child so add the same in constructor
    // get toastrService(): ToastrService {
    //  return this.injector.get(ToastrService);
    // }

    public handleError(msg: string): void {
        this.toastrService.error(`Error Message: ${msg}`,
            "Error", {
                closeButton: true,
                timeOut: 5000,
                onActivateTick: true
            }
        );
    }

}

给出错误

core.es5.js:1020 ERROR TypeError: Cannot read property 'get' of undefined 
    at AccessDenied.AppError (app-error.ts:8) 
    at new AccessDenied (access-denied.ts:6)  
    at CatchSubscriber.eval [as selector] (error.interceptor.ts:61) 
    at CatchSubscriber.error (catchError.js:105)
    at XMLHttpRequest.onLoad (http.es5.js:1739)
    at ZoneDelegate.invokeTask (zone.js:421)
    at Object.onInvokeTask (core.es5.js:3881)
    at ZoneDelegate.invokeTask (zone.js:420)
    at Zone.runTask (zone.js:188)
    at ZoneTask.invokeTask [as invoke] (zone.js:496)

2 个答案:

答案 0 :(得分:1)

AccessDenied错误地扩展了AppErrorsuper()会导致injector未定义,injectorAppError构造函数中不应该是可选的,因为它是必需的。

可以通过强制执行参数来修复:

constructor(private injector: Injector, public originalError?: any) {
    this.toastrService = this.injector.get(ToastrService);
}

AccessDenied中可以省略构造函数。并且应该像new AccessDenied(injector)一样进行实例化。

这里真正的问题是AppError完成了它不应该做的工作。考虑到它只包含稍后可以使用err instanceof AppError确定的错误,它不应该执行当前在handleError中完成的副作用。

handleError中的逻辑可以移动到ToastrService中的方法或接受AppError实例的单独错误服务。如果需要为Access denied error occurred等错误类型提供默认消息,AppError可以包含包含该消息的公共属性。

答案 1 :(得分:0)

您正在尝试将ToastrService注入错误类。这是不可能的,因为Angular不会为您创建对象,因此无法注入依赖项。如果你坚持这样做,你必须自己将ToastrService传递给每个AppError。

我建议创建一个自定义的ErrorHandler,查看错误是否是AppError的一个实例,然后使用ToastrService显示toast。

粗略地这样(未经测试):

应用-error.ts

import { Inject, Injector } from "@angular/core";

import { ToastrService } from "ngx-toastr";

export class AppError {
    constructor(public message?: string, public title?: string) { }

}

使用-denied.ts

import { AppError } from './app-error';
export class AccessDenied extends AppError {
    constructor(message?: string, title?: string) {
        super(message, title);
    }
}

应用-error.handler.ts

import { ErrorHandler, Inject, Injector, Injectable } from "@angular/core";
import { ToastrService } from "ngx-toastr";
import { AppError } from './app-error';

@Injectable()
export class AppErrorHandler extends ErrorHandler {

    constructor(@Inject(Injector) private injector: Injector) {
        super(true);
    }

    // Need to get ToastrService from injector rather than constructor injection to avoid cyclic dependency error
    private get toastrService(): ToastrService {
        return this.injector.get(ToastrService);
    }

    public handleError(error: any): void {
        if (error instanceof AppError) {
            this.toastrService.error(
                error.message,
                error.title,
                {
                    closeButton: true,
                    timeOut: 5000,
                    onActivateTick: true
                }
            );
        }

        super.handleError(error);
    }
}

<强> app.module.ts

@NgModule({
  // Your imports, declarations, bootstrap, etc.
  // ...

  providers: [
    // Other providers...

    {
      provide: ErrorHandler,
      useClass: AppErrorHandler
    }
  ]
})
export class AppModule {}