Javascript 如何捕获请求中的错误,然后打开一个模式,然后在使用RxJS关闭模式时重试

Javascript 如何捕获请求中的错误,然后打开一个模式,然后在使用RxJS关闭模式时重试,javascript,typescript,angular,rxjs,rxjs5,Javascript,Typescript,Angular,Rxjs,Rxjs5,我想调用一个服务器,该服务器可以使用Angular2的HTTP类返回授权失败(401) 请求流应该如下所示: 用户使用myService.getSomething().subscribe()向服务器发出请求 如果服务器返回401:则打开一个模式窗口,询问用户的凭据 用户已成功登录回应用程序 模态关闭并执行回调 回调应该重试初始请求(myService.getSomething().subscribe()) 以下是我目前的情况: export class MyService { //

我想调用一个服务器,该服务器可以使用Angular2的HTTP类返回授权失败(401)

请求流应该如下所示:

  • 用户使用myService.getSomething().subscribe()向服务器发出请求
  • 如果服务器返回401:则打开一个模式窗口,询问用户的凭据
  • 用户已成功登录回应用程序
  • 模态关闭并执行回调
  • 回调应该重试初始请求(myService.getSomething().subscribe())
以下是我目前的情况:

export class MyService {
    // ...
    public getSomething(): Observable<Response> {
        return this.http.get(url, options).catch((res: any, ob: any) => this.errorHandler(res, ob));
    }

    public errorHandler(res: Response, ob: Observable<any>): Observable<Response> {
        if (res.status === 401) {
            this.modalService.open(new ModalConfig({
                content: LoginModalComponent,
                close: () => { ob.retry(1); console.log("weow") } // <=close is the callback that should initiate the retry action on the initial request.
            }));
        }
        else {
            return Observable.throw(res.json());
        }
    }
}
遗憾的是,它仍然不起作用。retryWhen立即执行,不等待调用closedSubject.next()。因此,它启动一个无限循环,对原始的可观察对象(getSomething()函数)进行垃圾处理

更新2

我创建了一个plunker来演示无限循环:

警告:运行plunker将向控制台发送带有字符串“test”的垃圾邮件

更新3

按照Thierry的正确答案,我试图找到一种不使用源字段的方法,因为它是受保护的。在要求rxjs的问题跟踪者公开该领域后,一位投稿人给出了更好的解决方案

private errorHandler(res: Response, ob: Observable<any>): Observable<Response> {
    if (res.status === 401) {
        let closedSubject = new Subject();
        this.modalService.open(new ModalConfig({
            content: LoginModalComponent,
            close: () => { closedSubject.next(res);} // I also tried .complete(), .next(null), .next(true), .next(false)
        }));
        return ob.retryWhen(() => closedSubject);
    }
    else {
        return Observable.throw(res.json());
    }
}
public get(url: string, options?: RequestOptionsArgs): Observable<Response> {
    return super.get(url, options).retryWhen((errors: any) => this.errorHandler(errors));
}
private errorHandler(errors): any {
    return errors.switchMap((err) => {
        if (err.status === 401) {
            let closedSubject = new Subject();
            this.modalService.open(new ModalConfig({
                content: LoginModalComponent,
                close: () => { closedSubject.next(err); }
            }));
            return <any>closedSubject;
        }
        else {
            return Observable.throw(err.json());
        }
    });
}
公共获取(url:string,options?:RequestOptionsArgs):可观察{ 返回super.get(url,options).retryWhen((errors:any)=>this.errorHandler(errors)); } 私有errorHandler(错误):任何{ 返回错误。开关映射((错误)=>{ 如果(错误状态===401){ 让closedSubject=新主题(); this.modalService.open(新ModalConfig({ 内容:LoginModalComponent, close:()=>{closedSubject.next(err);} })); 返回关闭的主题; } 否则{ 返回Observable.throw(err.json()); } }); }
我避免使用.catch,因此我不必使用源字段。

我想
retryWhen
应该会有所帮助。

我认为即使在401错误的情况下,您也需要返回一些可观察的内容:

public errorHandler(res: Response, ob: Observable<any>): Observable<Response> {
    if (res.status === 401) {
        let closedSubject = new Subject();
        this.modalService.open(new ModalConfig({
            content: LoginModalComponent,
            close: () => {
              closedSubject.next();
        }));
        return ob.retryWhen(() => closedSubject);
    }
    else {
        return Observable.throw(res.json());
    }
}

请参阅工作说明:。

我相信解决方案已经有了一些变化,因为在Angular的新闻版本中,我们必须使用
pipe()
方法。因此,我决定使用自定义运算符解决方案。一件好事是
handleError()
方法可以作为全局函数导出,然后在多个服务中使用

有关详细信息,请参阅此解决方案:

导出类MyService{
// ...
public getSomething():可观察{
返回this.http.get(url,options).pipe(this.handleError('可能是您的自定义消息在这里');
}
私有句柄错误(错误消息:字符串){
返回(源:可观察)=>source.pipe(
retryWhen(errors=>errors.pipe(
合并映射((errorResponse:HttpErrorResponse)=>{
控制台错误(错误消息);
if(errorResponse.status==401){
const closedSubject=新主题();
this.modalService.open(新ModalConfig({
内容:LoginModalComponent,
关闭:()=>{
closedSubject.next();
}
}));
返回关闭的主题;
}
返回投掷器(错误响应);
})
))
);
}

}

我觉得这接近答案,但仍然不起作用。当服务器抛出401时,errorHandler进入无限循环。它会在不等待模式关闭的情况下重试请求。我不知道您在登录模式中做了什么,但我想您需要在再次执行请求之前在请求中设置一些内容。出于这个原因,我认为您不应该使用retry,而应该根据请求的url和更新的选项(例如,
授权
头)再次执行请求。问题是retryWhen立即得到解决。它不是在等待调用closeSubject.next()。奇怪。不应该。也就是说我的代码片段中有一个输入错误。我相应地更新了我的答案…请参阅我的“编辑”部分和plunkr:。那么现在一切都正常了吗?
return ob.source.retryWhen((errors) => closedSubject);
export class MyService {
// ...
public getSomething(): Observable<Response> {
    return this.http.get(url, options).pipe(this.handleError('Maybe your a custom message here'));
}

private handleError(errorMessage: string) {
    return (source: Observable<any>) => source.pipe(
        retryWhen(errors => errors.pipe(
            mergeMap((errorResponse: HttpErrorResponse) => {
                console.error(errorMessage);
                if (errorResponse.status === 401) {
                    const closedSubject = new Subject();
                    this.modalService.open(new ModalConfig({
                        content: LoginModalComponent,
                        close: () => {
                            closedSubject.next();
                        }
                    }));
                    return closedSubject;
                }
                return throwError(errorResponse);
            })
        ))
    );
}