【问题标题】:How to catch an error on a Request, then open a modal, then retry when modal closes with RxJS如何在请求中捕获错误,然后打开一个模态,然后在 RxJS 关闭模态时重试
【发布时间】:2016-08-07 21:15:00
【问题描述】:

我想使用 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());
        }
    }
}

doSomething() 是这样使用的:doSomething().map((r) =&gt; r.json()).subscribe((r) =&gt; ....)

更新 1

我修改了我的代码,使其看起来像 @Thierry Templier 的解决方案。

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

遗憾的是它仍然不起作用。 retryWhen 会立即执行,并且不会等待 closedSubject.next() 被调用。因此它启动了一个无限循环,向原始 Observable(getSomething() 函数)发送垃圾邮件。

更新 2

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

https://plnkr.co/edit/8SzmZlRHvi00OIdA7Bga

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

更新 3

按照蒂埃里的正确答案,我试图找到一种不使用源字段的方法,因为它受到保护。在询问 rxjs 的问题跟踪器以公开该字段后,一位贡献者回复了一个更好的解决方案。

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

我避免使用 .catch,因此我不必使用源字段。

【问题讨论】:

  • 现在一切正常吗?

标签: javascript typescript angular rxjs rxjs5


【解决方案1】:

我猜retryWhen operator 应该会有所帮助。

【讨论】:

    【解决方案2】:

    我认为即使出现 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());
        }
    }
    

    查看这篇文章了解更多详情:https://jaxenter.com/reactive-programming-http-and-angular-2-124560.html

    编辑

    问题在于catch 回调的第二个参数不是可观察的源。此源 observable 对应于其 source 属性的值:

    return ob.source.retryWhen((errors) => closedSubject);
    

    查看工作 plunkr:https://plnkr.co/edit/eb2UdF9PSMhf4Dau2hqe?p=preview

    【讨论】:

    • 我觉得这很接近答案,但它仍然不起作用。当服务器抛出 401 时,errorHandler 进入无限循环。它重试请求而不等待模式关闭。
    • 我不知道您在登录模式中做了什么,但我想您需要在请求中设置一些内容才能再次执行它。出于这个原因,我认为您不应该使用重试,而是根据其 url 和更新的选项(例如,Authorization 标头)再次执行请求...
    • 问题是retryWhen马上就解决了。它不等待 closeSubject.next() 被调用。
    • 奇怪。它不应该。也就是说,我的 sn-p 中有一个错字。我相应地更新了我的答案......
    • 查看我的“编辑”部分和 plunkr:plnkr.co/edit/eb2UdF9PSMhf4Dau2hqe?p=preview
    【解决方案3】:

    我相信解决方案有所改变,因为在 Angular 的新闻版本中,我们必须使用 pipe() 方法。所以我决定使用自定义运营商解决方案。一件好事是handleError() 方法可以导出为全局函数,然后用于多个服务。

    查看此解决方案了解更多详情:https://blog.angularindepth.com/retry-failed-http-requests-in-angular-f5959d486294

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

    }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-02-20
      • 1970-01-01
      • 1970-01-01
      • 2012-05-14
      • 2019-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多