RxJs Observables:在一些更多的异步请求之后运行重试 [英] RxJs Observables: run retryWhen after some more async requests
问题描述
我的用例是:
- 用户从我们的API请求资产因JWT到期而失败(作为httpOnly cookie传递) - API返回401状态代码。
- 我们再次使用refresh_token来验证它们(无需用户做任何事情),以便从我们的客户端请求auth0来检索新的JWT。
- 我们将新的JWT发送到我们的API,将其设置为httpOnly cookie以替换过期的。
- 然后我们要重试原始请求用户在步骤1中对API进行了操作。
- User requests asset from our API which fails because of JWT expiring (passed as an httpOnly cookie) - API returns a 401 status code.
- We go and authenticate them (without the user doing anything) again using a refresh_token to retrieve a new JWT with a request from our client to auth0.
- We send that new JWT to our API to be set as an httpOnly cookie to replace the expired one.
- We then want to retry the original request the user made to the API in step 1.
我正在尝试使用 redux-observable 。如果你能想到让上述用户流程工作的另一种方式,我会很高兴听到如何。
I'm trying to use Observables within my Redux app with redux-observable. If you can think of another way of making the above user flow work I would be happy to hear how.
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步的方法。我可能完全不合适但任何帮助都会很棒!
So far I able to complete steps 1, 2 and 3 but not too sure of a way to add step 4. I may be completely off the mark but any help would be great!
推荐答案
您可能不想做的一件事是允许错误进入顶级流。即使您执行 catch
,您也有效地杀死了顶级流。因此,除非您的重定向通过像react-router这样的东西进行硬重定向而不是软重定向,否则您将无法再使用此史诗。
Well one thing you probably won't want to do is allow the error to make it to the top level stream. Even if you do a catch
you have effectively killed the top level stream. So unless your redirect is doing a hard redirect instead of a a soft one via something like react-router, you won't be able to use this epic any more.
因此我想说你希望大部分逻辑被封装在 switchMap
中:
Thus I would say that you want most of the logic to be encapsulated within the 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
能够正常工作。
The use of let
above is completely optional, I threw it in to clean up the function. Essentially though you want to contain the error to the inner stream so that it can't halt the outer one. I am not sure which ajax
library you are using but you should also confirm that it will in fact return a cold Observable
otherwise you will need to wrap it in a defer
block to in order for the retryWhen
to work.
这篇关于RxJs Observables:在一些更多的异步请求之后运行重试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!