【问题标题】:RxJs Observables: run retryWhen after some more async requestsRxJs Observables:在更多异步请求后运行 retryWhen
【发布时间】:2016-12-02 02:47:33
【问题描述】:

我的用例是:

  1. 用户从我们的 API 请求资产,但由于 JWT 过期(作为 httpOnly cookie 传递)而失败 - API 返回 401 状态代码。
  2. 我们再次使用 refresh_token 对它们进行身份验证(无需用户执行任何操作),以检索来自客户端对 auth0 的请求的新 JWT。
  3. 我们将新的 JWT 发送到我们的 API 以设置为 httpOnly cookie 以替换过期的。
  4. 然后我们想重试用户在步骤 1 中对 API 发出的原始请求。

我正在尝试通过redux-observable 在我的 Redux 应用程序中使用 Observables。如果您能想出另一种方法来使上述用户流程发挥作用,我很乐意听到。

注意。我正在使用rxjs V5

export const fetchAssetListEpic = (action$, store) => {
  return action$.ofType('FETCH_ASSET_LIST')
  .switchMap( action => {
    const options = {
      crossDomain: true,
      withCredentials: true,
      url: uriGenerator('assetList', action.payload)
    };
    return ajax(options);
  })
  .map(fetchAssetListSuccess)
  .retryWhen(handleError)
  .catch(redirectToSignIn);
};

function handleError(err) {
  return (err.status === 401) ? 
  /* Authenticate here [Step 2] */
  /* Send new JWT to API [Step 3] */
  /* If successful make original request again [Step 4] */
  :
  Observable.throw(err);
}
  
function redirectToSignIn() {
  /*I will redirect here*/
}

到目前为止,我能够完成步骤 1、2 和 3,但不太确定添加步骤 4 的方法。我可能完全不合时宜,但任何帮助都会很棒!

【问题讨论】:

    标签: javascript redux rxjs angular2-observables


    【解决方案1】:

    您可能不想做的一件事是让错误出现在顶级流中。即使您执行catch,您也有效地杀死了顶级流。因此,除非您的重定向是通过诸如 react-router 之类的东西进行硬重定向而不是软重定向,否则您将无法再使用此史诗。

    因此我会说您希望将大部分逻辑封装在switchMap 中:

    function withAuthorizedFlow(source) {
      return source
        .map(fetchAssetListSuccess)
        // retryWhen takes a callback which accepts an Observable of errors
        // emitting a next causes a retry, while an error or complete will
        // stop retrying
        .retryWhen(e => e.flatMap(err => 
          Observable.if(
            // Returns the first stream if true, second if false
            () => err.status === 401,
            reauthenticate, // A stream that will emit once authenticated
            Observable.throw(err) // Rethrow the error
          ))
        )
        .catch(redirectToSignIn);
    }
    
    /** Within the epic **/
    .switchMap(({payload}) => {
      const options = {
        crossDomain: true,
        withCredentials: true,
        url: uriGenerator('assetList', payload)
      };
    
      // Invoke the ajax request
      return ajax(options)
        // Attach a custom pipeline here
        // Not strictly necessary but it keeps this method clean looking.
        .let(withAuthorizedFlow);
    })
    

    上面let的使用完全是可选的,我把它扔进去是为了清理功能。本质上,尽管您希望将错误包含在内部流中,以使其无法停止外部流。我不确定您使用的是哪个ajax 库,但您还应该确认它实际上会返回一个冷的Observable,否则您需要将它包装在defer 块中以便retryWhen 到工作。

    【讨论】:

    • 非常好的答案 - 有趣的是:.if.let 似乎都没有记录(reactivex.io/rxjs/class/es6/Observable.js~Observable.html
    • 有趣,我认为自动生成这些文档的缺点
    • 感谢@paulpdaniels,这看起来很棒!我还没有机会实现它,但当我有机会时会更新。很高兴在此之前将其标记为已接受的答案:)
    猜你喜欢
    • 1970-01-01
    • 2018-08-12
    • 2018-01-14
    • 2016-06-11
    • 1970-01-01
    • 1970-01-01
    • 2012-05-11
    • 1970-01-01
    • 2022-01-18
    相关资源
    最近更新 更多