Reputation: 341
I'm trying to handle HTTP redirection status codes (e.g. 302 redirect when the session timed out) and I don't know if there is a generic way to handle specific response codes using redux-observable? The problem I have now is that the browser follows location specified in 302 response and I can only tap into subsequent 200 response for the login page. I've got a bit of a hack now where I detect the word 'login' in response URL and redirect to login page using window.location object. I have to do this in every epic.
Here's what I've got:
export const getData = (action$) => {
return action$.pipe(
ofType(GET_DATA_REQUEST),
mergeMap(action => {
return ajax(options).pipe(
map((response) => response.originalEvent.currentTarget.responseURL.endsWith('login') ? window.location.href = 'login' : getDataSuccess(response.response)),
catchError(error => of(getDataFailure(error)))
);
}),
catchError(error => of(getDataFailure(error)))
);
};
Does anyone know any better ways of handling this problem where I wouldn't have to repeat it for all new epics?
Upvotes: 1
Views: 762
Reputation: 1459
The ajax
operations wrap XMLHttpRequest
, and XMLHttpRequest
automatically follows redirects. While the redirect cannot be prevented, it can be detected. Here's another example of detecting the redirect:
export const getData = action$ =>
action$.pipe(
ofType(GET_DATA_REQUEST),
mergeMap(action =>
ajax(options).pipe(
mergeMap(response => {
// Navigate to login if the request was successful but redirected
if (response.status >= 200 && response.status < 300 && response.responseURL !== options.url) {
window.location.href = 'login'
return empty()
}
return of(getDataSuccess(response.response))
})
)
)
)
If you want to reuse this logic in multiple epics, simply export it as a reusable function:
export const ajaxWithLoginRedirect = options =>
ajax(options).pipe(
mergeMap(response => {
// Navigate to login if the request was successful but redirected
if (response.status >= 200 && response.status < 300 && response.responseURL !== options.url) {
window.location.href = 'login'
return empty()
}
// Return the raw response
return of(response)
})
)
export const getData = action$ =>
action$.pipe(
ofType(GET_DATA_REQUEST),
mergeMap(action =>
ajaxWithLoginRedirect(options).pipe(
// This is only called if we did not redirect
map(response => getDataSuccess(response.response))
)
)
)
Take note that the fetch
API does support handling redirects manually (the response object you get back will have the 3xx status code). There are a number of trade-offs between XMLHttpRequest
and fetch
, so I would research that if not automatically following redirects were preferable in your application.
Upvotes: 1