RxJs Observables: starting retryWhen after several asynchronous requests

My use case:

  • The user requests an asset from our API, which fails due to the expiration of the JWT (transmitted as an httpOnly cookie). The API returns a 401 status code.
  • We finish them again and again (without user use), using refresh_token to retrieve a new JWT with a request from our client for auth0.
  • We are sending this new JWT to our API, which must be set as an httpOnly cookie to replace the expired.
  • Then we want to repeat the initial request made by the user in the API in step 1.

I am trying to use Observables in my Redux application with redux-observable . If you can think of another way to execute the above stream of users, I would be glad to hear how to do it.

NB. Im using 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*/ } 

So far, I could follow steps 1, 2, and 3, but not too sure how to add step 4. I may be completely unfamiliar, but any help would be great!

+6
source share
1 answer

Well, one thing you probably don't want to do is let the error do it before the top-level stream. Even if you execute catch , you have effectively killed the top-level thread. Therefore, if your redirect does not perform hard forwarding, but not soft using something like a reactive router, you can no longer use this epic.

So, I would say that you want most of the logic to be encapsulated in 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); }) 

Using let above is completely optional, I threw it to clear the function. Essentially, although you want to contain an error in the internal thread so that it cannot stop the external. I'm not sure which ajax library you are using, but you must also confirm that it will really return the Observable chill, otherwise you will need to wrap it in a defer block to retryWhen work.

+6
source

Source: https://habr.com/ru/post/1012793/


All Articles