RxJs Observables:在一些更多的异步请求之后运行重试

时间:2016-12-01 09:23:18

标签: javascript redux rxjs angular2-observables

我的用例是:

  1. 用户从我们的API请求资产因JWT过期而失败(作为httpOnly cookie传递) - API返回401状态代码。
  2. 我们再次使用refresh_token验证它们(无需用户执行任何操作),以便从我们的客户端请求auth0检索新的JWT。
  3. 我们将新的JWT发送到我们的API,将其设置为httpOnly cookie以替换过期的。
  4. 然后,我们想要重试用户在步骤1中对API所做的原始请求。
  5. 我正在尝试使用redux-observable在我的Redux应用程序中使用Observables。如果您能想到使上述用户流程工作的另一种方式,我会很高兴听到如何。

    NB。我正在使用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步的方法。我可能完全不合适,但任何帮助都会很棒!

1 个答案:

答案 0 :(得分:8)

您可能不想做的一件事是允许错误进入顶级流。即使你做了catch,你也有效地杀死了顶级流。因此,除非您的重定向通过反应路由器之类的东西进行硬重定向而不是软重定向,否则您将无法再使用此史诗。

因此我想说你希望大部分逻辑被封装在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工作。