RxJs Observables:运行 在一些更多的异步请求之后重试
RxJs Observables: run retryWhen after some more async requests
我的用例是:
- 用户从我们的 API 请求资产,由于 JWT 过期(作为 httpOnly cookie 传递)而失败 - API returns 401 状态代码。
- 我们再次使用 refresh_token 对它们进行身份验证(无需用户执行任何操作),以检索新的 JWT 以及我们的客户端对 auth0 的请求。
- 我们将新的 JWT 发送到我们的 API 以设置为 httpOnly cookie 以替换过期的。
- 然后我们要重试用户在步骤 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 步的方法。我可能完全偏离了目标,但任何帮助都会很棒!
好吧,您可能不想做的一件事是允许错误进入顶级流。即使你做了 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
库,但你还应该确认它实际上 return 很冷 Observable
否则你需要将它包装在 defer
阻止 retryWhen
工作。
我的用例是:
- 用户从我们的 API 请求资产,由于 JWT 过期(作为 httpOnly cookie 传递)而失败 - API returns 401 状态代码。
- 我们再次使用 refresh_token 对它们进行身份验证(无需用户执行任何操作),以检索新的 JWT 以及我们的客户端对 auth0 的请求。
- 我们将新的 JWT 发送到我们的 API 以设置为 httpOnly cookie 以替换过期的。
- 然后我们要重试用户在步骤 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 步的方法。我可能完全偏离了目标,但任何帮助都会很棒!
好吧,您可能不想做的一件事是允许错误进入顶级流。即使你做了 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
库,但你还应该确认它实际上 return 很冷 Observable
否则你需要将它包装在 defer
阻止 retryWhen
工作。